QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsmeshtracerenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmeshtracerenderer.cpp
3  -------------------------
4  begin : November 2019
5  copyright : (C) 2019 by Vincent Cloarec
6  email : vcloarec at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsmeshtracerenderer.h"
19 #include "qgsmeshlayerrenderer.h"
21 
22 QgsVector QgsMeshVectorValueInterpolator::vectorValue( const QgsPointXY &point ) const
23 {
24  if ( mCacheFaceIndex != -1 && mCacheFaceIndex < mTriangularMesh.triangles().count() )
25  {
26  QgsVector res = interpolatedValuePrivate( mCacheFaceIndex, point );
27  if ( isVectorValid( res ) )
28  {
29  activeFaceFilter( res, mCacheFaceIndex );
30  return res;
31  }
32  }
33 
34  //point is not on the face associated with mCacheIndex --> search for the face containing the point
35  QList<int> potentialFaceIndexes = mTriangularMesh.faceIndexesForRectangle( QgsRectangle( point, point ) );
36  mCacheFaceIndex = -1;
37  for ( const int faceIndex : potentialFaceIndexes )
38  {
39  QgsVector res = interpolatedValuePrivate( faceIndex, point );
40  if ( isVectorValid( res ) )
41  {
42  mCacheFaceIndex = faceIndex;
43  activeFaceFilter( res, mCacheFaceIndex );
44  return res;
45  }
46  }
47 
48  //--> no face found return non valid vector
49  return ( QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) );
50 
51 }
52 
53 QgsMeshVectorValueInterpolator &QgsMeshVectorValueInterpolator::operator=( const QgsMeshVectorValueInterpolator &other )
54 {
55  mTriangularMesh = other.mTriangularMesh;
56  mDatasetValues = other.mDatasetValues;
57  mActiveFaceFlagValues = other.mActiveFaceFlagValues;
58  mFaceCache = other.mFaceCache;
59  mCacheFaceIndex = other.mCacheFaceIndex;
60  mUseScalarActiveFaceFlagValues = other.mUseScalarActiveFaceFlagValues;
61 
62  return *this;
63 }
64 
65 QgsMeshVectorValueInterpolatorFromVertex::
66 QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &datasetVectorValues ):
67  QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
68 {
69 
70 }
71 
72 QgsMeshVectorValueInterpolatorFromVertex::
73 QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh,
74  const QgsMeshDataBlock &datasetVectorValues,
75  const QgsMeshDataBlock &scalarActiveFaceFlagValues ):
76  QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
77 {
78 
79 }
80 
81 QgsMeshVectorValueInterpolatorFromVertex::QgsMeshVectorValueInterpolatorFromVertex( const QgsMeshVectorValueInterpolatorFromVertex &other ):
82  QgsMeshVectorValueInterpolator( other )
83 {}
84 
85 QgsMeshVectorValueInterpolatorFromVertex *QgsMeshVectorValueInterpolatorFromVertex::clone()
86 {
87  return new QgsMeshVectorValueInterpolatorFromVertex( *this );
88 }
89 
90 QgsMeshVectorValueInterpolatorFromVertex &QgsMeshVectorValueInterpolatorFromVertex::
91 operator=( const QgsMeshVectorValueInterpolatorFromVertex &other )
92 {
93  QgsMeshVectorValueInterpolator::operator=( other );
94  return ( *this );
95 }
96 
97 QgsVector QgsMeshVectorValueInterpolatorFromVertex::interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const
98 {
99  QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
100 
101  QgsPoint p1 = mTriangularMesh.vertices().at( face.at( 0 ) );
102  QgsPoint p2 = mTriangularMesh.vertices().at( face.at( 1 ) );
103  QgsPoint p3 = mTriangularMesh.vertices().at( face.at( 2 ) );
104 
105  QgsVector v1 = QgsVector( mDatasetValues.value( face.at( 0 ) ).x(),
106  mDatasetValues.value( face.at( 0 ) ).y() );
107 
108  QgsVector v2 = QgsVector( mDatasetValues.value( face.at( 1 ) ).x(),
109  mDatasetValues.value( face.at( 1 ) ).y() );
110 
111  QgsVector v3 = QgsVector( mDatasetValues.value( face.at( 2 ) ).x(),
112  mDatasetValues.value( face.at( 2 ) ).y() );
113 
114  return QgsMeshLayerUtils::interpolateVectorFromVerticesData(
115  p1,
116  p2,
117  p3,
118  v1,
119  v2,
120  v3,
121  point );
122 }
123 
124 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator( const QgsTriangularMesh &triangularMesh,
125  const QgsMeshDataBlock &datasetVectorValues ):
126  mTriangularMesh( triangularMesh ),
127  mDatasetValues( datasetVectorValues ),
128  mUseScalarActiveFaceFlagValues( false )
129 {}
130 
131 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator( const QgsTriangularMesh &triangularMesh,
132  const QgsMeshDataBlock &datasetVectorValues,
133  const QgsMeshDataBlock &scalarActiveFaceFlagValues ):
134  mTriangularMesh( triangularMesh ),
135  mDatasetValues( datasetVectorValues ),
136  mActiveFaceFlagValues( scalarActiveFaceFlagValues ),
137  mUseScalarActiveFaceFlagValues( true )
138 {}
139 
140 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator( const QgsMeshVectorValueInterpolator &other ):
141  mTriangularMesh( other.mTriangularMesh ),
142  mDatasetValues( other.mDatasetValues ),
143  mActiveFaceFlagValues( other.mActiveFaceFlagValues ),
144  mFaceCache( other.mFaceCache ),
145  mCacheFaceIndex( other.mCacheFaceIndex ),
146  mUseScalarActiveFaceFlagValues( other.mUseScalarActiveFaceFlagValues )
147 {}
148 
149 void QgsMeshVectorValueInterpolator::updateCacheFaceIndex( const QgsPointXY &point ) const
150 {
151  if ( ! QgsMeshUtils::isInTriangleFace( point, mFaceCache, mTriangularMesh.vertices() ) )
152  {
153  mCacheFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
154  }
155 }
156 
157 bool QgsMeshVectorValueInterpolator::isVectorValid( const QgsVector &v ) const
158 {
159  return !( std::isnan( v.x() ) || std::isnan( v.y() ) );
160 
161 }
162 
163 void QgsMeshVectorValueInterpolator::activeFaceFilter( QgsVector &vector, int faceIndex ) const
164 {
165  if ( mUseScalarActiveFaceFlagValues && ! mActiveFaceFlagValues.active( mTriangularMesh.trianglesToNativeFaces()[faceIndex] ) )
166  vector = QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) ;
167 }
168 
169 QSize QgsMeshStreamField::size() const
170 {
171  return mFieldSize;
172 }
173 
174 QPoint QgsMeshStreamField::topLeft() const
175 {
176  return mFieldTopLeftInDeviceCoordinates;
177 }
178 
179 int QgsMeshStreamField::resolution() const
180 {
181  return mFieldResolution;
182 }
183 
184 QgsPointXY QgsMeshStreamField::positionToMapCoordinates( const QPoint &pixelPosition, const QgsPointXY &positionInPixel )
185 {
186  QgsPointXY mapPoint = mMapToFieldPixel.toMapCoordinates( pixelPosition );
187  mapPoint = mapPoint + QgsVector( positionInPixel.x() * mMapToFieldPixel.mapUnitsPerPixel(),
188  positionInPixel.y() * mMapToFieldPixel.mapUnitsPerPixel() );
189  return mapPoint;
190 }
191 
192 QgsMeshStreamField::QgsMeshStreamField(
193  const QgsTriangularMesh &triangularMesh,
194  const QgsMeshDataBlock &dataSetVectorValues,
195  const QgsMeshDataBlock &scalarActiveFaceFlagValues,
196  const QgsRectangle &layerExtent,
197  double magnitudeMaximum, bool dataIsOnVertices,
198  const QgsRenderContext &rendererContext,
199  const QgsInterpolatedLineColor &vectorColoring,
200  int resolution ):
201  mFieldResolution( resolution ),
202  mVectorColoring( vectorColoring ),
203  mLayerExtent( layerExtent ),
204  mMaximumMagnitude( magnitudeMaximum ),
205  mRenderContext( rendererContext )
206 {
207  if ( dataIsOnVertices )
208  {
209  if ( scalarActiveFaceFlagValues.isValid() )
210  mVectorValueInterpolator.reset( new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
211  dataSetVectorValues,
212  scalarActiveFaceFlagValues ) );
213  else
214  mVectorValueInterpolator.reset( new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
215  dataSetVectorValues ) );
216  }
217  else
218  {
219  if ( scalarActiveFaceFlagValues.isValid() )
220  mVectorValueInterpolator.reset( new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
221  dataSetVectorValues,
222  scalarActiveFaceFlagValues ) );
223  else
224  mVectorValueInterpolator.reset( new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
225  dataSetVectorValues ) );
226  }
227 }
228 
229 QgsMeshStreamField::QgsMeshStreamField( const QgsMeshStreamField &other ):
230  mFieldSize( other.mFieldSize ),
231  mFieldResolution( other.mFieldResolution ),
232  mPen( other.mPen ),
233  mTraceImage( other.mTraceImage ),
234  mMapToFieldPixel( other.mMapToFieldPixel ),
235  mVectorColoring( other.mVectorColoring ),
236  mPixelFillingCount( other.mPixelFillingCount ),
237  mMaxPixelFillingCount( other.mMaxPixelFillingCount ),
238  mLayerExtent( other.mLayerExtent ),
239  mMapExtent( other.mMapExtent ),
240  mFieldTopLeftInDeviceCoordinates( other.mFieldTopLeftInDeviceCoordinates ),
241  mValid( other.mValid ),
242  mMaximumMagnitude( other.mMaximumMagnitude ),
243  mPixelFillingDensity( other.mPixelFillingDensity ),
244  mMinMagFilter( other.mMinMagFilter ),
245  mMaxMagFilter( other.mMaxMagFilter ),
246  mRenderContext( other.mRenderContext ),
247  mMinimizeFieldSize( other.mMinimizeFieldSize )
248 {
249  mPainter.reset( new QPainter( &mTraceImage ) );
250  mVectorValueInterpolator =
251  std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
252 }
253 
254 QgsMeshStreamField::~QgsMeshStreamField()
255 {
256  if ( mPainter )
257  mPainter->end();
258 }
259 
260 void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
261 {
262  mMapExtent = renderContext.mapExtent();
263  const QgsMapToPixel &deviceMapToPixel = renderContext.mapToPixel();
264  QgsRectangle layerExtent;
265  try
266  {
267  layerExtent = renderContext.coordinateTransform().transform( mLayerExtent );
268  }
269  catch ( QgsCsException &cse )
270  {
271  Q_UNUSED( cse );
272  //if the transform fails, consider the whole map
273  layerExtent = mMapExtent;
274  }
275 
276  QgsRectangle interestZoneExtent;
277  if ( mMinimizeFieldSize )
278  interestZoneExtent = layerExtent.intersect( mMapExtent );
279  else
280  interestZoneExtent = mMapExtent;
281 
282  if ( interestZoneExtent == QgsRectangle() )
283  {
284  mValid = false;
285  mFieldSize = QSize();
286  mFieldTopLeftInDeviceCoordinates = QPoint();
287  initField();
288  return;
289  }
290 
291  QgsPointXY interestZoneTopLeft;
292  QgsPointXY interestZoneBottomRight;
293 
294  interestZoneTopLeft = deviceMapToPixel.transform( QgsPointXY( interestZoneExtent.xMinimum(), interestZoneExtent.yMaximum() ) );
295  interestZoneBottomRight = deviceMapToPixel.transform( QgsPointXY( interestZoneExtent.xMaximum(), interestZoneExtent.yMinimum() ) );
296 
297 
298  mFieldTopLeftInDeviceCoordinates = interestZoneTopLeft.toQPointF().toPoint();
299  QPoint mFieldBottomRightInDeviceCoordinates = interestZoneBottomRight.toQPointF().toPoint();
300  int fieldWidthInDeviceCoordinate = mFieldBottomRightInDeviceCoordinates.x() - mFieldTopLeftInDeviceCoordinates.x();
301  int fieldHeightInDeviceCoordinate = mFieldBottomRightInDeviceCoordinates.y() - mFieldTopLeftInDeviceCoordinates.y();
302 
303  int fieldWidth = int( fieldWidthInDeviceCoordinate / mFieldResolution );
304  int fieldHeight = int( fieldHeightInDeviceCoordinate / mFieldResolution );
305 
306  //increase the field size if this size is not adjusted to extent of zone of interest in device coordinates
307  if ( fieldWidthInDeviceCoordinate % mFieldResolution > 0 )
308  fieldWidth++;
309  if ( fieldHeightInDeviceCoordinate % mFieldResolution > 0 )
310  fieldHeight++;
311 
312  if ( fieldWidth == 0 || fieldHeight == 0 )
313  {
314  mFieldSize = QSize();
315  }
316  else
317  {
318  mFieldSize.setWidth( fieldWidth );
319  mFieldSize.setHeight( fieldHeight );
320  }
321 
322 
323  double mapUnitPerFieldPixel;
324  if ( interestZoneExtent.width() > 0 )
325  mapUnitPerFieldPixel = interestZoneExtent.width() / fieldWidthInDeviceCoordinate * mFieldResolution;
326  else
327  mapUnitPerFieldPixel = 1e-8;
328 
329  int fieldRightDevice = mFieldTopLeftInDeviceCoordinates.x() + mFieldSize.width() * mFieldResolution;
330  int fieldBottomDevice = mFieldTopLeftInDeviceCoordinates.y() + mFieldSize.height() * mFieldResolution;
331  QgsPointXY fieldRightBottomMap = deviceMapToPixel.toMapCoordinates( fieldRightDevice, fieldBottomDevice );
332 
333  int fieldTopDevice = mFieldTopLeftInDeviceCoordinates.x();
334  int fieldLeftDevice = mFieldTopLeftInDeviceCoordinates.y();
335  QgsPointXY fieldTopLeftMap = deviceMapToPixel.toMapCoordinates( fieldTopDevice, fieldLeftDevice );
336 
337  double xc = ( fieldRightBottomMap.x() + fieldTopLeftMap.x() ) / 2;
338  double yc = ( fieldTopLeftMap.y() + fieldRightBottomMap.y() ) / 2;
339 
340  mMapToFieldPixel = QgsMapToPixel( mapUnitPerFieldPixel,
341  xc,
342  yc,
343  fieldWidth,
344  fieldHeight, 0 );
345 
346  initField();
347  mValid = true;
348 }
349 
350 void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext, int resolution )
351 {
352  if ( renderContext.mapExtent() == mMapExtent && resolution == mFieldResolution )
353  return;
354  mFieldResolution = resolution;
355 
356  updateSize( renderContext );
357 }
358 
359 bool QgsMeshStreamField::isValid() const
360 {
361  return mValid;
362 }
363 
364 void QgsMeshStreamField::addTrace( QgsPointXY startPoint )
365 {
366  QPoint sp;
367  sp = mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint();
368  addTrace( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint() );
369 }
370 
371 
372 void QgsMeshStreamField::addRandomTraces()
373 {
374  if ( mMaximumMagnitude > 0 )
375  while ( mPixelFillingCount < mMaxPixelFillingCount && !mRenderContext.renderingStopped() )
376  addRandomTrace();
377 }
378 
379 void QgsMeshStreamField::addRandomTrace()
380 {
381  if ( !mValid )
382  return;
383 
384  int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
385  int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
386  addTrace( QPoint( xRandom, yRandom ) );
387 }
388 
389 void QgsMeshStreamField::addGriddedTraces( int dx, int dy )
390 {
391  int i = 0 ;
392  while ( i < mFieldSize.width() && !mRenderContext.renderingStopped() )
393  {
394  int j = 0 ;
395  while ( j < mFieldSize.height() && !mRenderContext.renderingStopped() )
396  {
397  addTrace( QPoint( i, j ) );
398  j += dy;
399  }
400  i += dx;
401  }
402 }
403 
404 void QgsMeshStreamField::addTracesOnMesh( const QgsTriangularMesh &mesh, const QgsRectangle &extent )
405 {
406  QList<int> facesInExtent = mesh.faceIndexesForRectangle( extent );
407  QSet<int> vertices;
408  for ( auto f : qgis::as_const( facesInExtent ) )
409  {
410  auto face = mesh.triangles().at( f );
411  for ( auto i : qgis::as_const( face ) )
412  vertices.insert( i );
413  }
414 
415  for ( auto i : qgis::as_const( vertices ) )
416  {
417  addTrace( mesh.vertices().at( i ) );
418  }
419 }
420 
421 void QgsMeshStreamField::addTrace( QPoint startPixel )
422 {
423  //This is where each traces are constructed
424  if ( !mPainter )
425  return;
426 
427  if ( isTraceExists( startPixel ) || isTraceOutside( startPixel ) )
428  return;
429 
430  if ( !mVectorValueInterpolator )
431  return;
432 
433  if ( !( mMaximumMagnitude > 0 ) )
434  return;
435 
436  mPainter->setPen( mPen );
437 
438  //position in the pixelField
439  double x1 = 0;
440  double y1 = 0;
441 
442  std::list<QPair<QPoint, FieldData>> chunkTrace;
443 
444  QPoint currentPixel = startPixel;
445  QgsVector vector;
446  FieldData data;
447  data.time = 1;
448 
449  while ( !mRenderContext.renderingStopped() )
450  {
451  QgsPointXY mapPosition = positionToMapCoordinates( currentPixel, QgsPointXY( x1, y1 ) );
452  vector = mVectorValueInterpolator->vectorValue( mapPosition ) ;
453 
454  if ( std::isnan( vector.x() ) || std::isnan( vector.y() ) )
455  {
456  mPixelFillingCount++;
457  setChunkTrace( chunkTrace );
458  drawChunkTrace( chunkTrace );
459  break;
460  }
461 
462  /* nondimensional value : Vu=2 when the particle need dt=1 to go through a pixel with the mMagMax magnitude
463  * The nondimensional size of the side of a pixel is 2
464  */
465  QgsVector vu = vector / mMaximumMagnitude * 2;
466  data.magnitude = vector.length();
467  double Vx = vu.x();
468  double Vy = vu.y();
469  double Vu = data.magnitude / mMaximumMagnitude * 2; //nondimensional vector magnitude
470 
471  if ( qgsDoubleNear( Vu, 0 ) )
472  {
473  // no trace anymore
474  addPixelToChunkTrace( currentPixel, data, chunkTrace );
475  simplifyChunkTrace( chunkTrace );
476  setChunkTrace( chunkTrace );
477  drawChunkTrace( chunkTrace );
478  break;
479  }
480 
481  //calculates where the particle will be after dt=1,
482  QgsPointXY nextPosition = QgsPointXY( x1, y1 ) + vu;
483  int incX = 0;
484  int incY = 0;
485  if ( nextPosition.x() > 1 )
486  incX = +1;
487  if ( nextPosition.x() < -1 )
488  incX = -1;
489  if ( nextPosition.y() > 1 )
490  incY = +1;
491  if ( nextPosition.y() < -1 )
492  incY = -1;
493 
494  double x2, y2;
495 
496  if ( incX != 0 || incY != 0 )
497  {
498  data.directionX = incX;
499  data.directionY = -incY;
500  //the particule leave the current pixel --> store pixels, calculates where the particle is and change the current pixel
501  if ( chunkTrace.empty() )
502  {
503  storeInField( QPair<QPoint, FieldData>( currentPixel, data ) );
504  }
505  if ( addPixelToChunkTrace( currentPixel, data, chunkTrace ) )
506  {
507  setChunkTrace( chunkTrace );
508  drawChunkTrace( chunkTrace );
509  clearChunkTrace( chunkTrace );
510  }
511 
512  data.time = 1;
513  currentPixel += QPoint( incX, -incY );
514  x1 = nextPosition.x() - 2 * incX;
515  y1 = nextPosition.y() - 2 * incY;
516  }
517  else
518  {
519  /*the particule still in the pixel --> "push" the position with the vector value to join a border
520  * and calculate the time spent to go to this border
521  */
522  if ( qgsDoubleNear( Vy, 0 ) )
523  {
524  y2 = y1;
525  if ( Vx > 0 )
526  incX = +1;
527  else
528  incX = -1;
529 
530  x2 = incX ;
531  }
532  else if ( qgsDoubleNear( Vx, 0 ) )
533  {
534  x2 = x1;
535  if ( Vy > 0 )
536  incY = +1;
537  else
538  incY = -1;
539 
540  y2 = incY ;
541  }
542  else
543  {
544  if ( Vy > 0 )
545  x2 = x1 + ( 1 - y1 ) * Vx / fabs( Vy ) ;
546  else
547  x2 = x1 + ( 1 + y1 ) * Vx / fabs( Vy ) ;
548  if ( Vx > 0 )
549  y2 = y1 + ( 1 - x1 ) * Vy / fabs( Vx ) ;
550  else
551  y2 = y1 + ( 1 + x1 ) * Vy / fabs( Vx ) ;
552 
553  if ( x2 >= 1 )
554  {
555  x2 = 1;
556  incX = +1;
557  }
558  if ( x2 <= -1 )
559  {
560  x2 = -1;
561  incX = -1;
562  }
563  if ( y2 >= 1 )
564  {
565  y2 = 1;
566  incY = +1;
567  }
568  if ( y2 <= -1 )
569  {
570  y2 = -1;
571  incY = -1;
572  }
573  }
574 
575  //calculate distance
576  double dx = x2 - x1;
577  double dy = y2 - y1;
578  double dl = sqrt( dx * dx + dy * dy );
579 
580  data.time += dl / Vu ; //adimensional time step : this the time needed to go to the border of the pixel
581  if ( data.time > 10000 ) //Guard to prevent that the particle never leave the pixel
582  {
583  addPixelToChunkTrace( currentPixel, data, chunkTrace );
584  setChunkTrace( chunkTrace );
585  drawChunkTrace( chunkTrace );
586  break;
587  }
588  x1 = x2;
589  y1 = y2;
590  }
591 
592  //test if the new current pixel is already defined, if yes no need to continue
593  if ( isTraceExists( currentPixel ) )
594  {
595  //Set the pixel in the chunk before adding the current pixel because this pixel is already defined
596  setChunkTrace( chunkTrace );
597  addPixelToChunkTrace( currentPixel, data, chunkTrace );
598  drawChunkTrace( chunkTrace );
599  break;
600  }
601 
602  if ( isTraceOutside( currentPixel ) )
603  {
604  setChunkTrace( chunkTrace );
605  drawChunkTrace( chunkTrace );
606  break;
607  }
608  }
609 }
610 
611 void QgsMeshStreamField::setResolution( int width )
612 {
613  mFieldResolution = width;
614 }
615 
616 QSize QgsMeshStreamField::imageSize() const
617 {
618  return mFieldSize * mFieldResolution;
619 }
620 
621 QPointF QgsMeshStreamField::fieldToDevice( const QPoint &pixel ) const
622 {
623  QPointF p( pixel );
624  p = mFieldResolution * p + QPointF( mFieldResolution - 1, mFieldResolution - 1 ) / 2;
625  return p;
626 }
627 
628 bool QgsMeshStreamField::addPixelToChunkTrace( QPoint &pixel,
629  QgsMeshStreamField::FieldData &data,
630  std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
631 {
632  chunkTrace.emplace_back( pixel, data );
633  if ( chunkTrace.size() == 3 )
634  {
635  simplifyChunkTrace( chunkTrace );
636  return true;
637  }
638  return false;
639 }
640 
641 void QgsMeshStreamlinesField::initField()
642 {
643  mField = QVector<bool>( mFieldSize.width() * mFieldSize.height(), false );
644  initImage();
645 }
646 
647 QgsMeshStreamlinesField::QgsMeshStreamlinesField( const QgsTriangularMesh &triangularMesh,
648  const QgsMeshDataBlock &datasetVectorValues,
649  const QgsMeshDataBlock &scalarActiveFaceFlagValues,
650  const QgsRectangle &layerExtent,
651  double magMax,
652  bool dataIsOnVertices,
653  QgsRenderContext &rendererContext,
654  const QgsInterpolatedLineColor vectorColoring ):
655  QgsMeshStreamField( triangularMesh,
656  datasetVectorValues,
657  scalarActiveFaceFlagValues,
658  layerExtent,
659  magMax,
660  dataIsOnVertices,
661  rendererContext,
662  vectorColoring )
663 {}
664 
665 QgsMeshStreamlinesField::QgsMeshStreamlinesField( const QgsMeshStreamlinesField &other ):
666  QgsMeshStreamField( other ),
667  mField( other.mField )
668 {}
669 
670 QgsMeshStreamlinesField &QgsMeshStreamlinesField::operator=( const QgsMeshStreamlinesField &other )
671 {
672  QgsMeshStreamField::operator=( other );
673  mField = other.mField;
674  return *this;
675 }
676 
677 void QgsMeshStreamlinesField::storeInField( const QPair<QPoint, FieldData> pixelData )
678 {
679  int i = pixelData.first.x();
680  int j = pixelData.first.y();
681  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
682  {
683  mField[j * mFieldSize.width() + i] = true;
684  }
685 }
686 
687 void QgsMeshStreamField::setChunkTrace( std::list<QPair<QPoint, FieldData> > &chunkTrace )
688 {
689  auto p = chunkTrace.begin();
690  while ( p != chunkTrace.end() )
691  {
692  storeInField( ( *p ) );
693  mPixelFillingCount++;
694  ++p;
695  }
696 }
697 
698 void QgsMeshStreamlinesField::drawChunkTrace( const std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
699 {
700  auto p1 = chunkTrace.begin();
701  auto p2 = p1;
702  p2++;
703  while ( p2 != chunkTrace.end() )
704  {
705  double mag1 = ( *p1 ).second.magnitude;
706  double mag2 = ( *p2 ).second.magnitude;
707  if ( filterMag( mag1 ) && filterMag( mag2 ) )
708  {
709  QPen pen = mPainter->pen();
710  pen.setColor( mVectorColoring.color( ( mag1 + mag2 ) / 2 ) );
711  mPainter->setPen( pen );
712  mPainter->drawLine( fieldToDevice( ( *p1 ).first ), fieldToDevice( ( *p2 ).first ) );
713  }
714 
715  p1++;
716  p2++;
717  }
718 }
719 
720 void QgsMeshStreamField::clearChunkTrace( std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
721 {
722  auto one_before_end = std::prev( chunkTrace.end() );
723  chunkTrace.erase( chunkTrace.begin(), one_before_end );
724 }
725 
726 void QgsMeshStreamField::simplifyChunkTrace( std::list<QPair<QPoint, FieldData> > &shunkTrace )
727 {
728  if ( shunkTrace.size() != 3 )
729  return;
730 
731  auto ip3 = shunkTrace.begin();
732  auto ip1 = ip3++;
733  auto ip2 = ip3++;
734 
735  while ( ip3 != shunkTrace.end() && ip2 != shunkTrace.end() )
736  {
737  QPoint v1 = ( *ip1 ).first - ( *ip2 ).first;
738  QPoint v2 = ( *ip2 ).first - ( *ip3 ).first;
739  if ( v1.x()*v2.x() + v1.y()*v2.y() == 0 )
740  {
741  ( *ip1 ).second.time += ( ( *ip2 ).second.time ) / 2;
742  ( *ip3 ).second.time += ( ( *ip2 ).second.time ) / 2;
743  ( *ip1 ).second.directionX += ( *ip2 ).second.directionX;
744  ( *ip1 ).second.directionY += ( *ip2 ).second.directionY;
745  shunkTrace.erase( ip2 );
746  }
747  ip1 = ip3++;
748  ip2 = ip3++;
749  }
750 }
751 
752 bool QgsMeshStreamlinesField::isTraceExists( const QPoint &pixel ) const
753 {
754  int i = pixel.x();
755  int j = pixel.y();
756  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
757  {
758  return mField[j * mFieldSize.width() + i];
759  }
760 
761  return false;
762 }
763 
764 bool QgsMeshStreamField::isTraceOutside( const QPoint &pixel ) const
765 {
766  int i = pixel.x();
767  int j = pixel.y();
768  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
769  {
770  return false;
771  }
772  return true;
773 }
774 
775 void QgsMeshStreamField::setMinimizeFieldSize( bool minimizeFieldSize )
776 {
777  mMinimizeFieldSize = minimizeFieldSize;
778 }
779 
780 QgsMeshStreamField &QgsMeshStreamField::operator=( const QgsMeshStreamField &other )
781 {
782  mFieldSize = other.mFieldSize ;
783  mFieldResolution = other.mFieldResolution;
784  mPen = other.mPen;
785  mTraceImage = other.mTraceImage ;
786  mMapToFieldPixel = other.mMapToFieldPixel ;
787  mVectorColoring = other.mVectorColoring;
788  mPixelFillingCount = other.mPixelFillingCount ;
789  mMaxPixelFillingCount = other.mMaxPixelFillingCount ;
790  mLayerExtent = other.mLayerExtent ;
791  mMapExtent = other.mMapExtent;
792  mFieldTopLeftInDeviceCoordinates = other.mFieldTopLeftInDeviceCoordinates ;
793  mValid = other.mValid ;
794  mMaximumMagnitude = other.mMaximumMagnitude ;
795  mPixelFillingDensity = other.mPixelFillingDensity ;
796  mMinMagFilter = other.mMinMagFilter ;
797  mMaxMagFilter = other.mMaxMagFilter ;
798  mMinimizeFieldSize = other.mMinimizeFieldSize ;
799  mVectorValueInterpolator =
800  std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
801 
802  mPainter.reset( new QPainter( &mTraceImage ) );
803 
804  return ( *this );
805 }
806 
807 void QgsMeshStreamField::initImage()
808 {
809 
810  mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
811  mTraceImage.fill( 0X00000000 );
812 
813  mPainter.reset( new QPainter( &mTraceImage ) );
814  mPainter->setRenderHint( QPainter::Antialiasing, true );
815  mPainter->setPen( mPen );
816 }
817 
818 bool QgsMeshStreamField::filterMag( double value ) const
819 {
820  return ( mMinMagFilter < 0 || value > mMinMagFilter ) && ( mMaxMagFilter < 0 || value < mMaxMagFilter );
821 }
822 
823 QImage QgsMeshStreamField::image()
824 {
825  return mTraceImage.scaled( mFieldSize * mFieldResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
826 }
827 
828 void QgsMeshStreamField::setPixelFillingDensity( double maxFilling )
829 {
830  mPixelFillingDensity = maxFilling;
831  mMaxPixelFillingCount = int( mPixelFillingDensity * mFieldSize.width() * mFieldSize.height() );
832 }
833 
834 void QgsMeshStreamField::setColor( QColor color )
835 {
836  mPen.setColor( color );
837 }
838 
839 void QgsMeshStreamField::setLineWidth( double width )
840 {
841  mPen.setWidthF( width );
842 }
843 
844 void QgsMeshStreamField::setFilter( double min, double max )
845 {
846  mMinMagFilter = min;
847  mMaxMagFilter = max;
848 }
849 
850 QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &datasetVectorValues ):
851  QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
852 {}
853 
854 QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &datasetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues ):
855  QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
856 {}
857 
858 QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace( const QgsMeshVectorValueInterpolatorFromFace &other ):
859  QgsMeshVectorValueInterpolator( other )
860 {}
861 
862 QgsMeshVectorValueInterpolatorFromFace *QgsMeshVectorValueInterpolatorFromFace::clone()
863 {
864  return new QgsMeshVectorValueInterpolatorFromFace( *this );
865 }
866 
867 QgsMeshVectorValueInterpolatorFromFace &QgsMeshVectorValueInterpolatorFromFace::operator=( const QgsMeshVectorValueInterpolatorFromFace &other )
868 {
869  QgsMeshVectorValueInterpolator::operator=( other );
870  return ( *this );
871 }
872 
873 QgsVector QgsMeshVectorValueInterpolatorFromFace::interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const
874 {
875  QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
876 
877  QgsPoint p1 = mTriangularMesh.vertices().at( face.at( 0 ) );
878  QgsPoint p2 = mTriangularMesh.vertices().at( face.at( 1 ) );
879  QgsPoint p3 = mTriangularMesh.vertices().at( face.at( 2 ) );
880 
881  QgsVector vect = QgsVector( mDatasetValues.value( mTriangularMesh.trianglesToNativeFaces().at( faceIndex ) ).x(),
882  mDatasetValues.value( mTriangularMesh.trianglesToNativeFaces().at( faceIndex ) ).y() );
883 
884  return QgsMeshLayerUtils::interpolateVectorFromFacesData(
885  p1,
886  p2,
887  p3,
888  vect,
889  point );
890 }
891 
892 QgsMeshVectorStreamlineRenderer::QgsMeshVectorStreamlineRenderer(
893  const QgsTriangularMesh &triangularMesh,
894  const QgsMeshDataBlock &dataSetVectorValues,
895  const QgsMeshDataBlock &scalarActiveFaceFlagValues,
896  bool dataIsOnVertices,
897  const QgsMeshRendererVectorSettings &settings,
898  QgsRenderContext &rendererContext,
899  const QgsRectangle &layerExtent, double magMax ):
900  mRendererContext( rendererContext )
901 {
902  mStreamlineField.reset( new QgsMeshStreamlinesField( triangularMesh,
903  dataSetVectorValues,
904  scalarActiveFaceFlagValues,
905  layerExtent,
906  magMax,
907  dataIsOnVertices,
908  rendererContext,
909  settings.vectorStrokeColoring() ) );
910 
911  mStreamlineField->updateSize( rendererContext );
912  mStreamlineField->setPixelFillingDensity( settings.streamLinesSettings().seedingDensity() );
913  mStreamlineField->setLineWidth( rendererContext.convertToPainterUnits( settings.lineWidth(),
914  QgsUnitTypes::RenderUnit::RenderMillimeters ) ) ;
915  mStreamlineField->setColor( settings.color() );
916  mStreamlineField->setFilter( settings.filterMin(), settings.filterMax() );
917 
918  switch ( settings.streamLinesSettings().seedingMethod() )
919  {
921  if ( settings.isOnUserDefinedGrid() )
922  mStreamlineField->addGriddedTraces( settings.userGridCellWidth(), settings.userGridCellHeight() );
923  else
924  mStreamlineField->addTracesOnMesh( triangularMesh, rendererContext.mapExtent() );
925  break;
927  mStreamlineField->addRandomTraces();
928  break;
929  }
930 }
931 
932 void QgsMeshVectorStreamlineRenderer::draw()
933 {
934  if ( mRendererContext.renderingStopped() )
935  return;
936  mRendererContext.painter()->drawImage( mStreamlineField->topLeft(), mStreamlineField->image() );
937 }
938 
939 QgsMeshParticleTracesField::QgsMeshParticleTracesField( const QgsTriangularMesh &triangularMesh,
940  const QgsMeshDataBlock &datasetVectorValues,
941  const QgsMeshDataBlock &scalarActiveFaceFlagValues,
942  const QgsRectangle &layerExtent,
943  double magMax,
944  bool dataIsOnVertices,
945  const QgsRenderContext &rendererContext,
946  const QgsInterpolatedLineColor vectorColoring ):
947  QgsMeshStreamField( triangularMesh,
948  datasetVectorValues,
949  scalarActiveFaceFlagValues,
950  layerExtent,
951  magMax,
952  dataIsOnVertices,
953  rendererContext,
954  vectorColoring )
955 {
956  std::srand( uint( ::time( nullptr ) ) );
957  mPen.setCapStyle( Qt::RoundCap );
958 }
959 
960 QgsMeshParticleTracesField::QgsMeshParticleTracesField( const QgsMeshParticleTracesField &other ):
961  QgsMeshStreamField( other ),
962  mTimeField( other.mTimeField ),
963  mMagnitudeField( other.mMagnitudeField ),
964  mDirectionField( other.mDirectionField ),
965  mParticles( other.mParticles ),
966  mStumpImage( other.mStumpImage ),
967  mTimeStep( other.mTimeStep ),
968  mParticlesLifeTime( other.mParticlesLifeTime ),
969  mParticlesCount( other.mParticlesCount ),
970  mTailFactor( other.mTailFactor ),
971  mParticleColor( other.mParticleColor ),
972  mParticleSize( other.mParticleSize ),
973  mStumpFactor( other.mStumpFactor )
974 {}
975 
976 void QgsMeshParticleTracesField::addParticle( const QPoint &startPoint, double lifeTime )
977 {
978  addTrace( startPoint );
979  if ( time( startPoint ) > 0 )
980  {
981  QgsMeshTraceParticle p;
982  p.lifeTime = lifeTime;
983  p.position = startPoint;
984  mParticles.append( p );
985  }
986 
987 }
988 
989 void QgsMeshParticleTracesField::addParticleXY( const QgsPointXY &startPoint, double lifeTime )
990 {
991  addParticle( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint(), lifeTime );
992 }
993 
994 void QgsMeshParticleTracesField::moveParticles()
995 {
996  stump();
997  for ( auto &p : mParticles )
998  {
999  double spentTime = p.remainingTime; //adjust with the past remaining time
1000  size_t countAdded = 0;
1001  while ( spentTime < mTimeStep && p.lifeTime > 0 )
1002  {
1003  double timeToSpend = double( time( p.position ) );
1004  if ( timeToSpend > 0 )
1005  {
1006  p.lifeTime -= timeToSpend;
1007  spentTime += timeToSpend;
1008  QPoint dir = direction( p.position );
1009  if ( p.lifeTime > 0 )
1010  {
1011  p.position += dir;
1012  p.tail.emplace_back( p.position );
1013  countAdded++;
1014  }
1015  else
1016  {
1017  break;
1018  }
1019  }
1020  else
1021  {
1022  p.lifeTime = -1;
1023  break;
1024  }
1025  }
1026 
1027  if ( p.lifeTime <= 0 )
1028  {
1029  // the particle is not alive anymore
1030  p.lifeTime = 0;
1031  p.tail.clear();
1032  }
1033  else
1034  {
1035  p.remainingTime = spentTime - mTimeStep;
1036  while ( int( p.tail.size() ) > mMinTailLength && p.tail.size() > countAdded * mTailFactor )
1037  p.tail.erase( p.tail.begin() );
1038  drawParticleTrace( p );
1039  }
1040  }
1041 
1042  //remove empty (dead particles)
1043  int i = 0;
1044  while ( i < mParticles.count() )
1045  {
1046  if ( mParticles.at( i ).tail.size() == 0 )
1047  mParticles.removeAt( i );
1048  else
1049  ++i;
1050  }
1051 
1052  //add new particles if needed
1053  if ( mParticles.count() < mParticlesCount )
1054  addRandomParticles();
1055 }
1056 
1057 void QgsMeshParticleTracesField::addRandomParticles()
1058 {
1059  if ( !isValid() )
1060  return;
1061 
1062  if ( mParticlesCount < 0 ) //for tests, add one particle on the center of the map
1063  {
1064  addParticleXY( QgsPointXY( mMapToFieldPixel.xCenter(), mMapToFieldPixel.yCenter() ), mParticlesLifeTime );
1065  return;
1066  }
1067 
1068  int count = mParticlesCount - mParticles.count();
1069 
1070  for ( int i = 0; i < count; ++i )
1071  {
1072  int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
1073  int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
1074  double lifeTime = ( std::rand() / ( ( RAND_MAX + 1u ) / mParticlesLifeTime ) );
1075  addParticle( QPoint( xRandom, yRandom ), lifeTime );
1076  }
1077 }
1078 
1079 void QgsMeshParticleTracesField::storeInField( const QPair<QPoint, QgsMeshStreamField::FieldData> pixelData )
1080 {
1081  int i = pixelData.first.x();
1082  int j = pixelData.first.y();
1083  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1084  {
1085  mTimeField[j * mFieldSize.width() + i] = pixelData.second.time;
1086  int d = pixelData.second.directionX + 2 + ( pixelData.second.directionY + 1 ) * 3;
1087  mDirectionField[j * mFieldSize.width() + i] = static_cast<char>( d );
1088  mMagnitudeField[j * mFieldSize.width() + i] = pixelData.second.magnitude;
1089  }
1090 }
1091 
1092 void QgsMeshParticleTracesField::initField()
1093 {
1094  mTimeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), -1 );
1095  mDirectionField = QVector<char>( mFieldSize.width() * mFieldSize.height(), static_cast<char>( int( 0 ) ) );
1096  mMagnitudeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), 0 );
1097  initImage();
1098  mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1099  mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) ); //alpha=0 -> no persitence, alpha=255 -> total persistence
1100 }
1101 
1102 bool QgsMeshParticleTracesField::isTraceExists( const QPoint &pixel ) const
1103 {
1104  int i = pixel.x();
1105  int j = pixel.y();
1106  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1107  {
1108  return mTimeField[j * mFieldSize.width() + i] >= 0;
1109  }
1110 
1111  return false;
1112 }
1113 
1114 void QgsMeshParticleTracesField::setStumpParticleWithLifeTime( bool stumpParticleWithLifeTime )
1115 {
1116  mStumpParticleWithLifeTime = stumpParticleWithLifeTime;
1117 }
1118 
1119 void QgsMeshParticleTracesField::setParticlesColor( const QColor &c )
1120 {
1121  mVectorColoring.setColor( c );
1122 }
1123 
1124 void QgsMeshParticleTracesField::setMinTailLength( int minTailLength )
1125 {
1126  mMinTailLength = minTailLength;
1127 }
1128 
1129 QgsMeshParticleTracesField &QgsMeshParticleTracesField::operator=( const QgsMeshParticleTracesField &other )
1130 {
1131  QgsMeshStreamField::operator=( other );
1132  mTimeField = other.mTimeField;
1133  mDirectionField = other.mDirectionField;
1134  mParticles = other.mParticles;
1135  mStumpImage = other.mStumpImage;
1136  mTimeStep = other.mTimeStep;
1137  mParticlesLifeTime = other.mParticlesLifeTime;
1138  mParticlesCount = other.mParticlesCount;
1139  mTailFactor = other.mTailFactor;
1140  mParticleColor = other.mParticleColor;
1141  mParticleSize = other.mParticleSize;
1142  mStumpFactor = other.mStumpFactor;
1143 
1144  return ( *this );
1145 }
1146 
1147 void QgsMeshParticleTracesField::setTailFactor( double tailFactor )
1148 {
1149  mTailFactor = tailFactor;
1150 }
1151 
1152 void QgsMeshParticleTracesField::setParticleSize( double particleSize )
1153 {
1154  mParticleSize = particleSize;
1155 }
1156 
1157 void QgsMeshParticleTracesField::setTimeStep( double timeStep )
1158 {
1159  mTimeStep = timeStep;
1160 }
1161 
1162 void QgsMeshParticleTracesField::setParticlesLifeTime( double particlesLifeTime )
1163 {
1164  mParticlesLifeTime = particlesLifeTime;
1165 }
1166 
1167 QImage QgsMeshParticleTracesField::imageRendered() const
1168 {
1169  return mTraceImage;
1170 }
1171 
1172 void QgsMeshParticleTracesField::stump()
1173 {
1174  mPainter->save();
1175  mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1176  mPainter->drawImage( QPoint( 0, 0 ), mStumpImage );
1177  mPainter->restore();
1178 }
1179 
1180 void QgsMeshParticleTracesField::setStumpFactor( int sf )
1181 {
1182  mStumpFactor = sf;
1183  mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1184  mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1185 }
1186 
1187 QPoint QgsMeshParticleTracesField::direction( QPoint position ) const
1188 {
1189  int i = position.x();
1190  int j = position.y();
1191  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1192  {
1193  int dir = static_cast<int>( mDirectionField[j * mFieldSize.width() + i] );
1194  if ( dir != 0 && dir < 10 )
1195  return QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
1196  }
1197  return QPoint( 0, 0 );
1198 }
1199 
1200 float QgsMeshParticleTracesField::time( QPoint position ) const
1201 {
1202  int i = position.x();
1203  int j = position.y();
1204  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1205  {
1206  return mTimeField[j * mFieldSize.width() + i];
1207  }
1208  return -1;
1209 }
1210 
1211 float QgsMeshParticleTracesField::magnitude( QPoint position ) const
1212 {
1213  int i = position.x();
1214  int j = position.y();
1215  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1216  {
1217  return mMagnitudeField[j * mFieldSize.width() + i];
1218  }
1219  return -1;
1220 }
1221 
1222 void QgsMeshParticleTracesField::drawParticleTrace( const QgsMeshTraceParticle &particle )
1223 {
1224  const std::list<QPoint> &tail = particle.tail;
1225  if ( tail.size() == 0 )
1226  return;
1227  double iniWidth = mParticleSize;
1228  double finWidth = 0;
1229 
1230  size_t pixelCount = tail.size();
1231 
1232  double transparency = 1;
1233  if ( mStumpParticleWithLifeTime )
1234  transparency = sin( M_PI * particle.lifeTime / mParticlesLifeTime );
1235 
1236  double dw;
1237  if ( pixelCount > 1 )
1238  dw = ( iniWidth - finWidth ) / ( pixelCount );
1239  else
1240  dw = 0;
1241 
1242  auto ip1 = std::prev( tail.end() );
1243  auto ip2 = std::prev( ip1 );
1244  int i = 0;
1245  while ( ip1 != tail.begin() )
1246  {
1247  QPointF p1 = fieldToDevice( ( *ip1 ) );
1248  QPointF p2 = fieldToDevice( ( *ip2 ) );
1249  QColor traceColor = mVectorColoring.color( magnitude( *ip1 ) );
1250  traceColor.setAlphaF( traceColor.alphaF()*transparency );
1251  mPen.setColor( traceColor );
1252  mPen.setWidthF( iniWidth - i * dw );
1253  mPainter->setPen( mPen );
1254  mPainter->drawLine( p1, p2 );
1255  ip1--;
1256  ip2--;
1257  ++i;
1258  }
1259 }
1260 
1261 void QgsMeshParticleTracesField::setParticlesCount( int particlesCount )
1262 {
1263  mParticlesCount = particlesCount;
1264 }
1265 
1267  const QgsMeshDataBlock &dataSetVectorValues,
1268  const QgsMeshDataBlock &scalarActiveFaceFlagValues,
1269  bool dataIsOnVertices,
1270  const QgsRenderContext &rendererContext,
1271  const QgsRectangle &layerExtent,
1272  double magMax,
1273  const QgsMeshRendererVectorSettings &vectorSettings ):
1274  mRendererContext( rendererContext )
1275 {
1276  mParticleField = std::unique_ptr<QgsMeshParticleTracesField>( new QgsMeshParticleTracesField( triangularMesh,
1277  dataSetVectorValues,
1278  scalarActiveFaceFlagValues,
1279  layerExtent,
1280  magMax,
1281  dataIsOnVertices,
1282  rendererContext,
1283  vectorSettings.vectorStrokeColoring() ) ) ;
1284  mParticleField->updateSize( rendererContext ) ;
1285 }
1286 
1288  mRendererContext( rendererContext )
1289 {
1290  if ( !layer->triangularMesh() )
1291  layer->reload();
1292 
1293  QgsMeshDataBlock vectorDatasetValues;
1294  QgsMeshDataBlock scalarActiveFaceFlagValues;
1295  bool vectorDataOnVertices;
1296  double magMax;
1297 
1298  QgsMeshDatasetIndex datasetIndex = layer->activeVectorDatasetAtTime( rendererContext.temporalRange() );
1299 
1300  // Find out if we can use cache up to date. If yes, use it and return
1301  int datasetGroupCount = layer->dataProvider()->datasetGroupCount();
1302  const QgsMeshRendererVectorSettings vectorSettings = layer->rendererSettings().vectorSettings( datasetIndex.group() );
1303  QgsMeshLayerRendererCache *cache = layer->rendererCache();
1304 
1305  if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
1306  ( cache->mActiveVectorDatasetIndex == datasetIndex ) )
1307  {
1308  vectorDatasetValues = cache->mVectorDatasetValues;
1309  scalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
1310  magMax = cache->mVectorDatasetMagMaximum;
1311  vectorDataOnVertices = cache->mVectorDataType == QgsMeshDatasetGroupMetadata::DataOnVertices;
1312  }
1313  else
1314  {
1315  const QgsMeshDatasetGroupMetadata metadata =
1316  layer->dataProvider()->datasetGroupMetadata( datasetIndex.group() );
1317  magMax = metadata.maximum();
1318  vectorDataOnVertices = metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
1319 
1320  int count;
1321  if ( vectorDataOnVertices )
1322  count = layer->nativeMesh()->vertices.count();
1323  else
1324  count = layer->nativeMesh()->faces.count();
1325 
1326  vectorDatasetValues = QgsMeshLayerUtils::datasetValues( layer, datasetIndex, 0, count );
1327 
1328  scalarActiveFaceFlagValues = layer->dataProvider()->areFacesActive(
1329  datasetIndex,
1330  0,
1331  layer->nativeMesh()->faces.count() );
1332  }
1333 
1334  mParticleField = std::unique_ptr<QgsMeshParticleTracesField>( new QgsMeshParticleTracesField( ( *layer->triangularMesh() ),
1335  vectorDatasetValues,
1336  scalarActiveFaceFlagValues,
1337  layer->extent(),
1338  magMax,
1339  vectorDataOnVertices,
1340  rendererContext,
1341  vectorSettings.vectorStrokeColoring() ) ) ;
1342 
1343  mParticleField->setMinimizeFieldSize( false );
1344  mParticleField->updateSize( mRendererContext );
1345 }
1346 
1348  mRendererContext( other.mRendererContext ),
1349  mFPS( other.mFPS ),
1350  mVpixMax( other.mVpixMax ),
1351  mParticleLifeTime( other.mParticleLifeTime )
1352 {
1353  mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
1354  new QgsMeshParticleTracesField( *other.mParticleField ) );
1355 }
1356 
1357 
1359 {
1360  mParticleField->setParticlesCount( count );
1361  mParticleField->addRandomParticles();
1362 }
1363 
1365 {
1366  mParticleField->moveParticles();
1367  return mParticleField->image();
1368 }
1369 
1371 {
1372  if ( FPS > 0 )
1373  mFPS = FPS;
1374  else
1375  mFPS = 1;
1376 
1377  updateFieldParameter();
1378 }
1379 
1381 {
1382  mVpixMax = max;
1383  updateFieldParameter();
1384 }
1385 
1386 void QgsMeshVectorTraceAnimationGenerator::setParticlesLifeTime( double particleLifeTime )
1387 {
1388  mParticleLifeTime = particleLifeTime;
1389  updateFieldParameter();
1390 }
1391 
1393 {
1394  mParticleField->setParticlesColor( c );
1395 }
1396 
1398 {
1399  mParticleField->setParticleSize( width );
1400 }
1401 
1403 {
1404  mParticleField->setTailFactor( fct );
1405 }
1406 
1408 {
1409  mParticleField->setMinTailLength( l );
1410 }
1411 
1413 {
1414  if ( p < 0 )
1415  p = 0;
1416  if ( p > 1 )
1417  p = 1;
1418  mParticleField->setStumpFactor( int( 255 * p ) );
1419 }
1420 
1422 {
1423  mParticleField.reset( new QgsMeshParticleTracesField( *mParticleField ) );
1424  const_cast<QgsRenderContext &>( mRendererContext ) = other.mRendererContext;
1425  mFPS = other.mFPS;
1426  mVpixMax = other.mVpixMax;
1427  mParticleLifeTime = other.mParticleLifeTime;
1428 
1429  return ( *this );
1430 }
1431 
1432 void QgsMeshVectorTraceAnimationGenerator::updateFieldParameter()
1433 {
1434  double fieldTimeStep = mVpixMax / mFPS;
1435  double fieldLifeTime = mParticleLifeTime * mFPS * fieldTimeStep;
1436  mParticleField->setTimeStep( fieldTimeStep );
1437  mParticleField->setParticlesLifeTime( fieldLifeTime );
1438 }
1439 
1440 QgsMeshVectorTraceRenderer::QgsMeshVectorTraceRenderer(
1441  const QgsTriangularMesh &triangularMesh,
1442  const QgsMeshDataBlock &dataSetVectorValues,
1443  const QgsMeshDataBlock &scalarActiveFaceFlagValues,
1444  bool dataIsOnVertices,
1445  const QgsMeshRendererVectorSettings &settings,
1446  QgsRenderContext &rendererContext,
1447  const QgsRectangle &layerExtent,
1448  double magMax ):
1449  mRendererContext( rendererContext )
1450 {
1451  mParticleField = std::unique_ptr<QgsMeshParticleTracesField>( new QgsMeshParticleTracesField( triangularMesh,
1452  dataSetVectorValues,
1453  scalarActiveFaceFlagValues,
1454  layerExtent,
1455  magMax,
1456  dataIsOnVertices,
1457  rendererContext,
1458  settings.vectorStrokeColoring() ) ) ;
1459  mParticleField->updateSize( rendererContext ) ;
1460 
1461  mParticleField->setParticleSize( rendererContext.convertToPainterUnits(
1462  settings.lineWidth(), QgsUnitTypes::RenderUnit::RenderMillimeters ) );
1463  mParticleField->setParticlesCount( settings.tracesSettings().particlesCount() );
1464  mParticleField->setTailFactor( 1 );
1465  mParticleField->setStumpParticleWithLifeTime( false );
1466  mParticleField->setTimeStep( rendererContext.convertToPainterUnits( settings.tracesSettings().maximumTailLength(),
1467  settings.tracesSettings().maximumTailLengthUnit() ) ); //as the particles go through 1 pix for dt=1 and Vmax
1468  mParticleField->addRandomParticles();
1469  mParticleField->moveParticles();
1470 }
1471 
1472 void QgsMeshVectorTraceRenderer::draw()
1473 {
1474  if ( mRendererContext.renderingStopped() )
1475  return;
1476  mRendererContext.painter()->drawImage( mParticleField->topLeft(), mParticleField->image() );
1477 }
1478 
QgsMeshDatasetSourceInterface::datasetGroupCount
virtual int datasetGroupCount() const =0
Returns number of datasets groups loaded.
QgsMeshRendererVectorSettings
Definition: qgsmeshrenderersettings.h:410
QgsPointXY::y
double y
Definition: qgspointxy.h:48
QgsRenderContext::convertToPainterUnits
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
Definition: qgsrendercontext.cpp:287
QgsRenderContext::mapToPixel
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
Definition: qgsrendercontext.h:309
QgsTriangularMesh::vertices
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
Definition: qgstriangularmesh.cpp:287
QgsMeshVectorTraceAnimationGenerator::setParticlesSize
void setParticlesSize(double width)
Sets particle size in px.
QgsMeshVectorTraceAnimationGenerator::setFPS
void setFPS(int FPS)
Sets the number of frames per seconds that will be rendered.
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QgsMeshVectorTraceAnimationGenerator::setMaxSpeedPixel
void setMaxSpeedPixel(int max)
Sets the max number of pixels that can be go through by the particles in 1 second.
QgsMeshLayer::reload
void reload() override
Synchronises with changes in the datasource.
Definition: qgsmeshlayer.cpp:1141
QgsMeshVectorTraceAnimationGenerator::setParticlesColor
void setParticlesColor(const QColor &c)
Sets colors of particle.
QgsRectangle::xMaximum
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsMeshLayer::rendererCache
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering)
Definition: qgsmeshlayer.cpp:258
QgsTemporalRangeObject::temporalRange
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
Definition: qgstemporalrangeobject.cpp:43
QgsRenderContext
Definition: qgsrendercontext.h:57
QgsMeshVectorTraceAnimationGenerator::seedRandomParticles
void seedRandomParticles(int count)
seeds particles in the vector fields
QgsPointXY::toQPointF
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:154
QgsMeshVectorTraceAnimationGenerator
Definition: qgsmeshtracerenderer.h:563
QgsTriangularMesh::triangles
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
Definition: qgstriangularmesh.cpp:292
QgsMeshRendererVectorSettings::tracesSettings
QgsMeshRendererVectorTracesSettings tracesSettings() const
Returns settings for vector rendered with traces.
Definition: qgsmeshrenderersettings.cpp:687
QgsMapToPixel::toMapCoordinates
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
Definition: qgsmaptopixel.cpp:108
QgsCoordinateTransform::transform
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Definition: qgscoordinatetransform.cpp:239
QgsRectangle::intersect
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:312
QgsMeshRendererVectorStreamlineSettings::MeshGridded
@ MeshGridded
Seeds start points on the vertices mesh or user regular grid.
Definition: qgsmeshrenderersettings.h:334
QgsMeshLayer::triangularMesh
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
Definition: qgsmeshlayer.cpp:224
QgsRectangle
Definition: qgsrectangle.h:41
QgsMeshVectorTraceAnimationGenerator::setTailFactor
void setTailFactor(double fct)
Sets the tail factor, used to adjust the length of the tail. 0 : minimum length, >1 increase the tail...
QgsMeshRendererVectorStreamlineSettings::Random
@ Random
Seeds start points randomly on the mesh.
Definition: qgsmeshrenderersettings.h:339
QgsMeshUtils::isInTriangleFace
bool isInTriangleFace(const QgsPointXY point, const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Tests if point p is on the face defined with vertices.
Definition: qgstriangularmesh.cpp:563
QgsMeshRendererVectorSettings::vectorStrokeColoring
QgsInterpolatedLineColor vectorStrokeColoring() const
Returns the stroke coloring used to render vector datasets.
Definition: qgsmeshrenderersettings.cpp:671
QgsMeshLayer::rendererSettings
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
Definition: qgsmeshlayer.cpp:263
QgsRenderContext::coordinateTransform
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Definition: qgsrendercontext.h:229
QgsMeshRendererVectorSettings::filterMin
double filterMin() const
Returns filter value for vector magnitudes.
Definition: qgsmeshrenderersettings.cpp:213
QgsMeshDatasetGroupMetadata::DataOnVertices
@ DataOnVertices
Data is defined on vertices.
Definition: qgsmeshdataset.h:356
QgsCsException
Definition: qgsexception.h:65
QgsMeshRendererVectorTracesSettings::particlesCount
int particlesCount() const
Returns particles count.
Definition: qgsmeshrenderersettings.cpp:735
QgsMeshDatasetIndex::group
int group() const
Returns a group index.
Definition: qgsmeshdataset.cpp:26
QgsVector::x
double x() const
Returns the vector's x-component.
Definition: qgsvector.cpp:76
QgsMeshVectorTraceAnimationGenerator::QgsMeshVectorTraceAnimationGenerator
QgsMeshVectorTraceAnimationGenerator(const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &dataSetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues, bool dataIsOnVertices, const QgsRenderContext &rendererContext, const QgsRectangle &layerExtent, double magMax, const QgsMeshRendererVectorSettings &vectorSettings)
Constructor to use from QgsMeshVectorRenderer.
QgsMesh::faces
QVector< QgsMeshFace > faces
Definition: qgsmeshdataprovider.h:107
QgsMeshDatasetIndex
Definition: qgsmeshdataset.h:45
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsMeshLayer
Definition: qgsmeshlayer.h:94
QgsMeshRendererVectorTracesSettings::maximumTailLength
double maximumTailLength() const
Returns the maximum tail length.
Definition: qgsmeshrenderersettings.cpp:725
QgsInterpolatedLineColor
Definition: qgsinterpolatedlinerenderer.h:34
QgsMeshVectorTraceAnimationGenerator::imageRendered
QImage imageRendered()
Moves all the particles using frame per second (fps) to calculate the displacement and return the ren...
QgsMeshLayer::nativeMesh
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
Definition: qgsmeshlayer.cpp:214
QgsMeshRendererVectorStreamlineSettings::seedingDensity
double seedingDensity() const
Returns the density used for seeding start points.
Definition: qgsmeshrenderersettings.cpp:539
QgsMeshVectorTraceAnimationGenerator::setTailPersitence
void setTailPersitence(double p)
Sets the visual persistence of the tail.
QgsMeshLayer::activeVectorDatasetAtTime
QgsMeshDatasetIndex activeVectorDatasetAtTime(const QgsDateTimeRange &timeRange) const
Returns dataset index from active vector group depending on the time range If the temporal properties...
Definition: qgsmeshlayer.cpp:554
QgsMeshVectorTraceAnimationGenerator::setMinimumTailLength
void setMinimumTailLength(int l)
Sets the minimum tail length.
QgsMeshVectorTraceAnimationGenerator::operator=
QgsMeshVectorTraceAnimationGenerator & operator=(const QgsMeshVectorTraceAnimationGenerator &other)
Assignment operator.
QgsMeshFace
QVector< int > QgsMeshFace
List of vertex indexes.
Definition: qgsmeshdataprovider.h:41
QgsMeshRendererVectorSettings::color
QColor color() const
Returns color used for drawing arrows.
Definition: qgsmeshrenderersettings.cpp:203
QgsMapToPixel::transform
QgsPointXY transform(const QgsPointXY &p) const
Transform the point from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.cpp:217
QgsRectangle::yMaximum
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsPointXY
Definition: qgspointxy.h:43
QgsMeshDatasetGroupMetadata::dataType
DataType dataType() const
Returns whether dataset group data is defined on vertices or faces or volumes.
Definition: qgsmeshdataset.cpp:169
QgsMeshRendererVectorSettings::filterMax
double filterMax() const
Returns filter value for vector magnitudes.
Definition: qgsmeshrenderersettings.cpp:223
QgsMeshRendererVectorSettings::userGridCellHeight
int userGridCellHeight() const
Returns height in pixels of user grid cell.
Definition: qgsmeshrenderersettings.cpp:253
QgsMeshRendererVectorTracesSettings::maximumTailLengthUnit
QgsUnitTypes::RenderUnit maximumTailLengthUnit() const
Returns the maximum tail length unit.
Definition: qgsmeshrenderersettings.cpp:715
QgsMeshRendererSettings::vectorSettings
QgsMeshRendererVectorSettings vectorSettings(int groupIndex) const
Returns renderer settings.
Definition: qgsmeshrenderersettings.h:629
QgsTriangularMesh::faceIndexesForRectangle
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
Definition: qgstriangularmesh.cpp:353
QgsMeshDatasetGroupMetadata
Definition: qgsmeshdataset.h:348
qgsmeshtracerenderer.h
qgsmeshlayerrenderer.h
QgsRenderContext::mapExtent
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
Definition: qgsrendercontext.h:302
c
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Definition: porting_processing.dox:1
QgsVector::y
double y() const
Returns the vector's y-component.
Definition: qgsvector.cpp:81
QgsMeshDatasetGroupMetadata::maximum
double maximum() const
Returns maximum scalar value/vector magnitude present for whole dataset group.
Definition: qgsmeshdataset.cpp:179
QgsMapToPixel
Definition: qgsmaptopixel.h:37
QgsVector
Definition: qgsvector.h:29
QgsAbstractGeometry::vertices
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
Definition: qgsabstractgeometry.cpp:282
QgsPointXY::x
double x
Definition: qgspointxy.h:47
QgsMesh::vertices
QVector< QgsMeshVertex > vertices
Definition: qgsmeshdataprovider.h:105
QgsRectangle::yMinimum
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsMeshDataBlock
Definition: qgsmeshdataset.h:134
QgsTriangularMesh
Definition: qgstriangularmesh.h:49
QgsMeshRendererVectorSettings::userGridCellWidth
int userGridCellWidth() const
Returns width in pixels of user grid cell.
Definition: qgsmeshrenderersettings.cpp:243
QgsMeshRendererVectorSettings::isOnUserDefinedGrid
bool isOnUserDefinedGrid() const
Returns whether vectors are drawn on user-defined grid.
Definition: qgsmeshrenderersettings.cpp:233
QgsMeshRendererVectorStreamlineSettings::seedingMethod
SeedingStartPointsMethod seedingMethod() const
Returns the method used for seeding start points of strealines.
Definition: qgsmeshrenderersettings.cpp:529
QgsMeshLayer::extent
QgsRectangle extent() const override
Returns the extent of the layer.
Definition: qgsmeshlayer.cpp:169
QgsMeshRendererVectorSettings::streamLinesSettings
QgsMeshRendererVectorStreamlineSettings streamLinesSettings() const
Returns settings for vector rendered with streamlines.
Definition: qgsmeshrenderersettings.cpp:587
QgsVector::length
double length() const
Returns the length of the vector.
Definition: qgsvector.cpp:71
QgsMeshRendererVectorSettings::lineWidth
double lineWidth() const
Returns line width of the arrow (in millimeters)
Definition: qgsmeshrenderersettings.cpp:193
QgsMeshDatasetSourceInterface::areFacesActive
virtual QgsMeshDataBlock areFacesActive(QgsMeshDatasetIndex index, int faceIndex, int count) const =0
Returns whether the faces are active for particular dataset.
QgsMeshDatasetSourceInterface::datasetGroupMetadata
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.
QgsMeshVectorTraceAnimationGenerator::setParticlesLifeTime
void setParticlesLifeTime(double particleLifeTime)
Sets maximum life time of particles in seconds.
QgsMeshLayer::dataProvider
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
Definition: qgsmeshlayer.cpp:147
QgsRectangle::width
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QgsRectangle::xMinimum
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsMeshDataBlock::isValid
bool isValid() const
Whether the block is valid.
Definition: qgsmeshdataset.cpp:254