QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 #ifndef M_DEG2RAD
23 #define M_DEG2RAD 0.0174532925
24 #endif
25 
26 
27 QgsVector QgsMeshVectorValueInterpolator::vectorValue( const QgsPointXY &point ) const
28 {
29  if ( mCacheFaceIndex != -1 && mCacheFaceIndex < mTriangularMesh.triangles().count() )
30  {
31  QgsVector res = interpolatedValuePrivate( mCacheFaceIndex, point );
32  if ( isVectorValid( res ) )
33  {
34  activeFaceFilter( res, mCacheFaceIndex );
35  return res;
36  }
37  }
38 
39  //point is not on the face associated with mCacheIndex --> search for the face containing the point
40  QList<int> potentialFaceIndexes = mTriangularMesh.faceIndexesForRectangle( QgsRectangle( point, point ) );
41  mCacheFaceIndex = -1;
42  for ( const int faceIndex : potentialFaceIndexes )
43  {
44  QgsVector res = interpolatedValuePrivate( faceIndex, point );
45  if ( isVectorValid( res ) )
46  {
47  mCacheFaceIndex = faceIndex;
48  activeFaceFilter( res, mCacheFaceIndex );
49  return res;
50  }
51  }
52 
53  //--> no face found return non valid vector
54  return ( QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) );
55 
56 }
57 
58 QgsMeshVectorValueInterpolator &QgsMeshVectorValueInterpolator::operator=( const QgsMeshVectorValueInterpolator &other )
59 {
60  mTriangularMesh = other.mTriangularMesh;
61  mDatasetValues = other.mDatasetValues;
62  mActiveFaceFlagValues = other.mActiveFaceFlagValues;
63  mFaceCache = other.mFaceCache;
64  mCacheFaceIndex = other.mCacheFaceIndex;
65  mUseScalarActiveFaceFlagValues = other.mUseScalarActiveFaceFlagValues;
66 
67  return *this;
68 }
69 
70 QgsMeshVectorValueInterpolatorFromVertex::
71 QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &datasetVectorValues ):
72  QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
73 {
74 
75 }
76 
77 QgsMeshVectorValueInterpolatorFromVertex::
78 QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh,
79  const QgsMeshDataBlock &datasetVectorValues,
80  const QgsMeshDataBlock &scalarActiveFaceFlagValues ):
81  QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
82 {
83 
84 }
85 
86 QgsMeshVectorValueInterpolatorFromVertex::QgsMeshVectorValueInterpolatorFromVertex( const QgsMeshVectorValueInterpolatorFromVertex &other ):
87  QgsMeshVectorValueInterpolator( other )
88 {}
89 
90 QgsMeshVectorValueInterpolatorFromVertex *QgsMeshVectorValueInterpolatorFromVertex::clone()
91 {
92  return new QgsMeshVectorValueInterpolatorFromVertex( *this );
93 }
94 
95 QgsMeshVectorValueInterpolatorFromVertex &QgsMeshVectorValueInterpolatorFromVertex::
96 operator=( const QgsMeshVectorValueInterpolatorFromVertex &other )
97 {
98  QgsMeshVectorValueInterpolator::operator=( other );
99  return ( *this );
100 }
101 
102 QgsVector QgsMeshVectorValueInterpolatorFromVertex::interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const
103 {
104  QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
105 
106  QgsPoint p1 = mTriangularMesh.vertices().at( face.at( 0 ) );
107  QgsPoint p2 = mTriangularMesh.vertices().at( face.at( 1 ) );
108  QgsPoint p3 = mTriangularMesh.vertices().at( face.at( 2 ) );
109 
110  QgsVector v1 = QgsVector( mDatasetValues.value( face.at( 0 ) ).x(),
111  mDatasetValues.value( face.at( 0 ) ).y() );
112 
113  QgsVector v2 = QgsVector( mDatasetValues.value( face.at( 1 ) ).x(),
114  mDatasetValues.value( face.at( 1 ) ).y() );
115 
116  QgsVector v3 = QgsVector( mDatasetValues.value( face.at( 2 ) ).x(),
117  mDatasetValues.value( face.at( 2 ) ).y() );
118 
119  return QgsMeshLayerUtils::interpolateVectorFromVerticesData(
120  p1,
121  p2,
122  p3,
123  v1,
124  v2,
125  v3,
126  point );
127 }
128 
129 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator( const QgsTriangularMesh &triangularMesh,
130  const QgsMeshDataBlock &datasetVectorValues ):
131  mTriangularMesh( triangularMesh ),
132  mDatasetValues( datasetVectorValues ),
133  mUseScalarActiveFaceFlagValues( false )
134 {}
135 
136 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator( const QgsTriangularMesh &triangularMesh,
137  const QgsMeshDataBlock &datasetVectorValues,
138  const QgsMeshDataBlock &scalarActiveFaceFlagValues ):
139  mTriangularMesh( triangularMesh ),
140  mDatasetValues( datasetVectorValues ),
141  mActiveFaceFlagValues( scalarActiveFaceFlagValues ),
142  mUseScalarActiveFaceFlagValues( true )
143 {}
144 
145 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator( const QgsMeshVectorValueInterpolator &other ):
146  mTriangularMesh( other.mTriangularMesh ),
147  mDatasetValues( other.mDatasetValues ),
148  mActiveFaceFlagValues( other.mActiveFaceFlagValues ),
149  mFaceCache( other.mFaceCache ),
150  mCacheFaceIndex( other.mCacheFaceIndex ),
151  mUseScalarActiveFaceFlagValues( other.mUseScalarActiveFaceFlagValues )
152 {}
153 
154 void QgsMeshVectorValueInterpolator::updateCacheFaceIndex( const QgsPointXY &point ) const
155 {
156  if ( ! QgsMeshUtils::isInTriangleFace( point, mFaceCache, mTriangularMesh.vertices() ) )
157  {
158  mCacheFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
159  }
160 }
161 
162 bool QgsMeshVectorValueInterpolator::isVectorValid( const QgsVector &v ) const
163 {
164  return !( std::isnan( v.x() ) || std::isnan( v.y() ) );
165 
166 }
167 
168 void QgsMeshVectorValueInterpolator::activeFaceFilter( QgsVector &vector, int faceIndex ) const
169 {
170  if ( mUseScalarActiveFaceFlagValues && ! mActiveFaceFlagValues.active( mTriangularMesh.trianglesToNativeFaces()[faceIndex] ) )
171  vector = QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) ;
172 }
173 
174 QSize QgsMeshStreamField::size() const
175 {
176  return mFieldSize;
177 }
178 
179 QPoint QgsMeshStreamField::topLeft() const
180 {
181  return mFieldTopLeftInDeviceCoordinates;
182 }
183 
184 int QgsMeshStreamField::resolution() const
185 {
186  return mFieldResolution;
187 }
188 
189 QgsPointXY QgsMeshStreamField::positionToMapCoordinates( const QPoint &pixelPosition, const QgsPointXY &positionInPixel )
190 {
191  QgsPointXY mapPoint = mMapToFieldPixel.toMapCoordinates( pixelPosition );
192  mapPoint = mapPoint + QgsVector( positionInPixel.x() * mMapToFieldPixel.mapUnitsPerPixel(),
193  positionInPixel.y() * mMapToFieldPixel.mapUnitsPerPixel() );
194  return mapPoint;
195 }
196 
197 QgsMeshStreamField::QgsMeshStreamField(
198  const QgsTriangularMesh &triangularMesh,
199  const QgsMeshDataBlock &dataSetVectorValues,
200  const QgsMeshDataBlock &scalarActiveFaceFlagValues,
201  const QgsRectangle &layerExtent,
202  double magnitudeMaximum, bool dataIsOnVertices,
203  const QgsRenderContext &rendererContext,
204  const QgsInterpolatedLineColor &vectorColoring,
205  int resolution ):
206  mFieldResolution( resolution ),
207  mVectorColoring( vectorColoring ),
208  mLayerExtent( layerExtent ),
209  mMaximumMagnitude( magnitudeMaximum ),
210  mRenderContext( rendererContext )
211 {
212  if ( dataIsOnVertices )
213  {
214  if ( scalarActiveFaceFlagValues.isValid() )
215  mVectorValueInterpolator.reset( new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
216  dataSetVectorValues,
217  scalarActiveFaceFlagValues ) );
218  else
219  mVectorValueInterpolator.reset( new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
220  dataSetVectorValues ) );
221  }
222  else
223  {
224  if ( scalarActiveFaceFlagValues.isValid() )
225  mVectorValueInterpolator.reset( new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
226  dataSetVectorValues,
227  scalarActiveFaceFlagValues ) );
228  else
229  mVectorValueInterpolator.reset( new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
230  dataSetVectorValues ) );
231  }
232 }
233 
234 QgsMeshStreamField::QgsMeshStreamField( const QgsMeshStreamField &other ):
235  mFieldSize( other.mFieldSize ),
236  mFieldResolution( other.mFieldResolution ),
237  mPen( other.mPen ),
238  mTraceImage( other.mTraceImage ),
239  mMapToFieldPixel( other.mMapToFieldPixel ),
240  mVectorColoring( other.mVectorColoring ),
241  mPixelFillingCount( other.mPixelFillingCount ),
242  mMaxPixelFillingCount( other.mMaxPixelFillingCount ),
243  mLayerExtent( other.mLayerExtent ),
244  mMapExtent( other.mMapExtent ),
245  mFieldTopLeftInDeviceCoordinates( other.mFieldTopLeftInDeviceCoordinates ),
246  mValid( other.mValid ),
247  mMaximumMagnitude( other.mMaximumMagnitude ),
248  mPixelFillingDensity( other.mPixelFillingDensity ),
249  mMinMagFilter( other.mMinMagFilter ),
250  mMaxMagFilter( other.mMaxMagFilter ),
251  mRenderContext( other.mRenderContext ),
252  mMinimizeFieldSize( other.mMinimizeFieldSize )
253 {
254  mPainter.reset( new QPainter( &mTraceImage ) );
255  mVectorValueInterpolator =
256  std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
257 }
258 
259 QgsMeshStreamField::~QgsMeshStreamField()
260 {
261  if ( mPainter )
262  mPainter->end();
263 }
264 
265 void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
266 {
267  mMapExtent = renderContext.mapExtent();
268  const QgsMapToPixel &deviceMapToPixel = renderContext.mapToPixel();
269  QgsRectangle layerExtent;
270  try
271  {
272  layerExtent = renderContext.coordinateTransform().transform( mLayerExtent );
273  }
274  catch ( QgsCsException &cse )
275  {
276  Q_UNUSED( cse )
277  //if the transform fails, consider the whole map
278  layerExtent = mMapExtent;
279  }
280 
281  QgsRectangle interestZoneExtent;
282  if ( mMinimizeFieldSize )
283  interestZoneExtent = layerExtent.intersect( mMapExtent );
284  else
285  interestZoneExtent = mMapExtent;
286 
287  if ( interestZoneExtent == QgsRectangle() )
288  {
289  mValid = false;
290  mFieldSize = QSize();
291  mFieldTopLeftInDeviceCoordinates = QPoint();
292  initField();
293  return;
294  }
295 
296 
297  QgsRectangle fieldInterestZoneInDeviceCoordinates = QgsMeshLayerUtils::boundingBoxToScreenRectangle( deviceMapToPixel, interestZoneExtent );
298  mFieldTopLeftInDeviceCoordinates = QPoint( int( fieldInterestZoneInDeviceCoordinates.xMinimum() ), int( fieldInterestZoneInDeviceCoordinates.yMinimum() ) );
299  int fieldWidthInDeviceCoordinate = int( fieldInterestZoneInDeviceCoordinates.width() );
300  int fieldHeightInDeviceCoordinate = int ( fieldInterestZoneInDeviceCoordinates.height() );
301 
302  int fieldWidth = int( fieldWidthInDeviceCoordinate / mFieldResolution );
303  int fieldHeight = int( fieldHeightInDeviceCoordinate / mFieldResolution );
304 
305  //increase the field size if this size is not adjusted to extent of zone of interest in device coordinates
306  if ( fieldWidthInDeviceCoordinate % mFieldResolution > 0 )
307  fieldWidth++;
308  if ( fieldHeightInDeviceCoordinate % mFieldResolution > 0 )
309  fieldHeight++;
310 
311  if ( fieldWidth == 0 || fieldHeight == 0 )
312  {
313  mFieldSize = QSize();
314  }
315  else
316  {
317  mFieldSize.setWidth( fieldWidth );
318  mFieldSize.setHeight( fieldHeight );
319  }
320 
321  double mapUnitPerFieldPixel;
322  if ( interestZoneExtent.width() > 0 )
323  mapUnitPerFieldPixel = deviceMapToPixel.mapUnitsPerPixel() * mFieldResolution * mFieldSize.width() / ( fieldWidthInDeviceCoordinate / mFieldResolution ) ;
324  else
325  mapUnitPerFieldPixel = 1e-8;
326 
327  int fieldRightDevice = mFieldTopLeftInDeviceCoordinates.x() + mFieldSize.width() * mFieldResolution;
328  int fieldBottomDevice = mFieldTopLeftInDeviceCoordinates.y() + mFieldSize.height() * mFieldResolution;
329  QgsPointXY fieldRightBottomMap = deviceMapToPixel.toMapCoordinates( fieldRightDevice, fieldBottomDevice );
330 
331  int fieldTopDevice = mFieldTopLeftInDeviceCoordinates.x();
332  int fieldLeftDevice = mFieldTopLeftInDeviceCoordinates.y();
333  QgsPointXY fieldTopLeftMap = deviceMapToPixel.toMapCoordinates( fieldTopDevice, fieldLeftDevice );
334 
335  double xc = ( fieldRightBottomMap.x() + fieldTopLeftMap.x() ) / 2;
336  double yc = ( fieldTopLeftMap.y() + fieldRightBottomMap.y() ) / 2;
337 
338  mMapToFieldPixel = QgsMapToPixel( mapUnitPerFieldPixel,
339  xc,
340  yc,
341  fieldWidth,
342  fieldHeight,
343  deviceMapToPixel.mapRotation()
344  );
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  addTrace( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint() );
367 }
368 
369 
370 void QgsMeshStreamField::addRandomTraces()
371 {
372  if ( mMaximumMagnitude > 0 )
373  while ( mPixelFillingCount < mMaxPixelFillingCount && !mRenderContext.renderingStopped() )
374  addRandomTrace();
375 }
376 
377 void QgsMeshStreamField::addRandomTrace()
378 {
379  if ( !mValid )
380  return;
381 
382  int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
383  int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
384  addTrace( QPoint( xRandom, yRandom ) );
385 }
386 
387 void QgsMeshStreamField::addGriddedTraces( int dx, int dy )
388 {
389  int i = 0 ;
390  while ( i < mFieldSize.width() && !mRenderContext.renderingStopped() )
391  {
392  int j = 0 ;
393  while ( j < mFieldSize.height() && !mRenderContext.renderingStopped() )
394  {
395  addTrace( QPoint( i, j ) );
396  j += dy;
397  }
398  i += dx;
399  }
400 }
401 
402 void QgsMeshStreamField::addTracesOnMesh( const QgsTriangularMesh &mesh, const QgsRectangle &extent )
403 {
404  QList<int> facesInExtent = mesh.faceIndexesForRectangle( extent );
405  QSet<int> vertices;
406  for ( auto f : qgis::as_const( facesInExtent ) )
407  {
408  auto face = mesh.triangles().at( f );
409  for ( auto i : qgis::as_const( face ) )
410  vertices.insert( i );
411  }
412 
413  for ( auto i : qgis::as_const( vertices ) )
414  {
415  addTrace( mesh.vertices().at( i ) );
416  }
417 }
418 
419 void QgsMeshStreamField::addTrace( QPoint startPixel )
420 {
421  //This is where each traces are constructed
422  if ( !mPainter )
423  return;
424 
425  if ( isTraceExists( startPixel ) || isTraceOutside( startPixel ) )
426  return;
427 
428  if ( !mVectorValueInterpolator )
429  return;
430 
431  if ( !( mMaximumMagnitude > 0 ) )
432  return;
433 
434  mPainter->setPen( mPen );
435 
436  //position in the pixelField
437  double x1 = 0;
438  double y1 = 0;
439 
440  std::list<QPair<QPoint, FieldData>> chunkTrace;
441 
442  QPoint currentPixel = startPixel;
443  QgsVector vector;
444  FieldData data;
445  data.time = 1;
446 
447  while ( !mRenderContext.renderingStopped() )
448  {
449  QgsPointXY mapPosition = positionToMapCoordinates( currentPixel, QgsPointXY( x1, y1 ) );
450  vector = mVectorValueInterpolator->vectorValue( mapPosition ) ;
451 
452  if ( std::isnan( vector.x() ) || std::isnan( vector.y() ) )
453  {
454  mPixelFillingCount++;
455  setChunkTrace( chunkTrace );
456  drawChunkTrace( chunkTrace );
457  break;
458  }
459 
460  /* nondimensional value : Vu=2 when the particle need dt=1 to go through a pixel with the mMagMax magnitude
461  * The nondimensional size of the side of a pixel is 2
462  */
463  vector = vector.rotateBy( -mMapToFieldPixel.mapRotation() * M_DEG2RAD );
464  QgsVector vu = vector / mMaximumMagnitude * 2;
465  data.magnitude = vector.length();
466 
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  QgsScopedQPainterState painterState( mPainter.get() );
1175  mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1176  mPainter->drawImage( QPoint( 0, 0 ), mStumpImage );
1177 }
1178 
1179 void QgsMeshParticleTracesField::setStumpFactor( int sf )
1180 {
1181  mStumpFactor = sf;
1182  mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1183  mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1184 }
1185 
1186 QPoint QgsMeshParticleTracesField::direction( QPoint position ) const
1187 {
1188  int i = position.x();
1189  int j = position.y();
1190  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1191  {
1192  int dir = static_cast<int>( mDirectionField[j * mFieldSize.width() + i] );
1193  if ( dir != 0 && dir < 10 )
1194  return QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
1195  }
1196  return QPoint( 0, 0 );
1197 }
1198 
1199 float QgsMeshParticleTracesField::time( QPoint position ) const
1200 {
1201  int i = position.x();
1202  int j = position.y();
1203  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1204  {
1205  return mTimeField[j * mFieldSize.width() + i];
1206  }
1207  return -1;
1208 }
1209 
1210 float QgsMeshParticleTracesField::magnitude( QPoint position ) const
1211 {
1212  int i = position.x();
1213  int j = position.y();
1214  if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1215  {
1216  return mMagnitudeField[j * mFieldSize.width() + i];
1217  }
1218  return -1;
1219 }
1220 
1221 void QgsMeshParticleTracesField::drawParticleTrace( const QgsMeshTraceParticle &particle )
1222 {
1223  const std::list<QPoint> &tail = particle.tail;
1224  if ( tail.size() == 0 )
1225  return;
1226  double iniWidth = mParticleSize;
1227  double finWidth = 0;
1228 
1229  size_t pixelCount = tail.size();
1230 
1231  double transparency = 1;
1232  if ( mStumpParticleWithLifeTime )
1233  transparency = sin( M_PI * particle.lifeTime / mParticlesLifeTime );
1234 
1235  double dw;
1236  if ( pixelCount > 1 )
1237  dw = ( iniWidth - finWidth ) / ( pixelCount );
1238  else
1239  dw = 0;
1240 
1241  auto ip1 = std::prev( tail.end() );
1242  auto ip2 = std::prev( ip1 );
1243  int i = 0;
1244  while ( ip1 != tail.begin() )
1245  {
1246  QPointF p1 = fieldToDevice( ( *ip1 ) );
1247  QPointF p2 = fieldToDevice( ( *ip2 ) );
1248  QColor traceColor = mVectorColoring.color( magnitude( *ip1 ) );
1249  traceColor.setAlphaF( traceColor.alphaF()*transparency );
1250  mPen.setColor( traceColor );
1251  mPen.setWidthF( iniWidth - i * dw );
1252  mPainter->setPen( mPen );
1253  mPainter->drawLine( p1, p2 );
1254  ip1--;
1255  ip2--;
1256  ++i;
1257  }
1258 }
1259 
1260 void QgsMeshParticleTracesField::setParticlesCount( int particlesCount )
1261 {
1262  mParticlesCount = particlesCount;
1263 }
1264 
1266  const QgsMeshDataBlock &dataSetVectorValues,
1267  const QgsMeshDataBlock &scalarActiveFaceFlagValues,
1268  bool dataIsOnVertices,
1269  const QgsRenderContext &rendererContext,
1270  const QgsRectangle &layerExtent,
1271  double magMax,
1272  const QgsMeshRendererVectorSettings &vectorSettings ):
1273  mRendererContext( rendererContext )
1274 {
1275  mParticleField = std::unique_ptr<QgsMeshParticleTracesField>( new QgsMeshParticleTracesField( triangularMesh,
1276  dataSetVectorValues,
1277  scalarActiveFaceFlagValues,
1278  layerExtent,
1279  magMax,
1280  dataIsOnVertices,
1281  rendererContext,
1282  vectorSettings.vectorStrokeColoring() ) ) ;
1283  mParticleField->updateSize( rendererContext ) ;
1284 }
1285 
1287  mRendererContext( rendererContext )
1288 {
1289  if ( !layer->triangularMesh() )
1290  layer->reload();
1291 
1292  QgsMeshDataBlock vectorDatasetValues;
1293  QgsMeshDataBlock scalarActiveFaceFlagValues;
1294  bool vectorDataOnVertices;
1295  double magMax;
1296 
1297  QgsMeshDatasetIndex datasetIndex = layer->activeVectorDatasetAtTime( rendererContext.temporalRange() );
1298 
1299  // Find out if we can use cache up to date. If yes, use it and return
1300  int datasetGroupCount = layer->dataProvider()->datasetGroupCount();
1301  const QgsMeshRendererVectorSettings vectorSettings = layer->rendererSettings().vectorSettings( datasetIndex.group() );
1302  QgsMeshLayerRendererCache *cache = layer->rendererCache();
1303 
1304  if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
1305  ( cache->mActiveVectorDatasetIndex == datasetIndex ) )
1306  {
1307  vectorDatasetValues = cache->mVectorDatasetValues;
1308  scalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
1309  magMax = cache->mVectorDatasetMagMaximum;
1310  vectorDataOnVertices = cache->mVectorDataType == QgsMeshDatasetGroupMetadata::DataOnVertices;
1311  }
1312  else
1313  {
1314  const QgsMeshDatasetGroupMetadata metadata =
1315  layer->dataProvider()->datasetGroupMetadata( datasetIndex.group() );
1316  magMax = metadata.maximum();
1317  vectorDataOnVertices = metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
1318 
1319  int count;
1320  if ( vectorDataOnVertices )
1321  count = layer->nativeMesh()->vertices.count();
1322  else
1323  count = layer->nativeMesh()->faces.count();
1324 
1325  vectorDatasetValues = QgsMeshLayerUtils::datasetValues( layer, datasetIndex, 0, count );
1326 
1327  scalarActiveFaceFlagValues = layer->dataProvider()->areFacesActive(
1328  datasetIndex,
1329  0,
1330  layer->nativeMesh()->faces.count() );
1331  }
1332 
1333  mParticleField = std::unique_ptr<QgsMeshParticleTracesField>( new QgsMeshParticleTracesField( ( *layer->triangularMesh() ),
1334  vectorDatasetValues,
1335  scalarActiveFaceFlagValues,
1336  layer->extent(),
1337  magMax,
1338  vectorDataOnVertices,
1339  rendererContext,
1340  vectorSettings.vectorStrokeColoring() ) ) ;
1341 
1342  mParticleField->setMinimizeFieldSize( false );
1343  mParticleField->updateSize( mRendererContext );
1344 }
1345 
1347  mRendererContext( other.mRendererContext ),
1348  mFPS( other.mFPS ),
1349  mVpixMax( other.mVpixMax ),
1350  mParticleLifeTime( other.mParticleLifeTime )
1351 {
1352  mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
1353  new QgsMeshParticleTracesField( *other.mParticleField ) );
1354 }
1355 
1356 
1358 {
1359  mParticleField->setParticlesCount( count );
1360  mParticleField->addRandomParticles();
1361 }
1362 
1364 {
1365  mParticleField->moveParticles();
1366  return mParticleField->image();
1367 }
1368 
1370 {
1371  if ( FPS > 0 )
1372  mFPS = FPS;
1373  else
1374  mFPS = 1;
1375 
1376  updateFieldParameter();
1377 }
1378 
1380 {
1381  mVpixMax = max;
1382  updateFieldParameter();
1383 }
1384 
1385 void QgsMeshVectorTraceAnimationGenerator::setParticlesLifeTime( double particleLifeTime )
1386 {
1387  mParticleLifeTime = particleLifeTime;
1388  updateFieldParameter();
1389 }
1390 
1392 {
1393  mParticleField->setParticlesColor( c );
1394 }
1395 
1397 {
1398  mParticleField->setParticleSize( width );
1399 }
1400 
1402 {
1403  mParticleField->setTailFactor( fct );
1404 }
1405 
1407 {
1408  mParticleField->setMinTailLength( l );
1409 }
1410 
1412 {
1413  if ( p < 0 )
1414  p = 0;
1415  if ( p > 1 )
1416  p = 1;
1417  mParticleField->setStumpFactor( int( 255 * p ) );
1418 }
1419 
1421 {
1422  mParticleField.reset( new QgsMeshParticleTracesField( *mParticleField ) );
1423  const_cast<QgsRenderContext &>( mRendererContext ) = other.mRendererContext;
1424  mFPS = other.mFPS;
1425  mVpixMax = other.mVpixMax;
1426  mParticleLifeTime = other.mParticleLifeTime;
1427 
1428  return ( *this );
1429 }
1430 
1431 void QgsMeshVectorTraceAnimationGenerator::updateFieldParameter()
1432 {
1433  double fieldTimeStep = mVpixMax / mFPS;
1434  double fieldLifeTime = mParticleLifeTime * mFPS * fieldTimeStep;
1435  mParticleField->setTimeStep( fieldTimeStep );
1436  mParticleField->setParticlesLifeTime( fieldLifeTime );
1437 }
1438 
1439 QgsMeshVectorTraceRenderer::QgsMeshVectorTraceRenderer(
1440  const QgsTriangularMesh &triangularMesh,
1441  const QgsMeshDataBlock &dataSetVectorValues,
1442  const QgsMeshDataBlock &scalarActiveFaceFlagValues,
1443  bool dataIsOnVertices,
1444  const QgsMeshRendererVectorSettings &settings,
1445  QgsRenderContext &rendererContext,
1446  const QgsRectangle &layerExtent,
1447  double magMax ):
1448  mRendererContext( rendererContext )
1449 {
1450  mParticleField = std::unique_ptr<QgsMeshParticleTracesField>( new QgsMeshParticleTracesField( triangularMesh,
1451  dataSetVectorValues,
1452  scalarActiveFaceFlagValues,
1453  layerExtent,
1454  magMax,
1455  dataIsOnVertices,
1456  rendererContext,
1457  settings.vectorStrokeColoring() ) ) ;
1458  mParticleField->updateSize( rendererContext ) ;
1459 
1460  mParticleField->setParticleSize( rendererContext.convertToPainterUnits(
1461  settings.lineWidth(), QgsUnitTypes::RenderUnit::RenderMillimeters ) );
1462  mParticleField->setParticlesCount( settings.tracesSettings().particlesCount() );
1463  mParticleField->setTailFactor( 1 );
1464  mParticleField->setStumpParticleWithLifeTime( false );
1465  mParticleField->setTimeStep( rendererContext.convertToPainterUnits( settings.tracesSettings().maximumTailLength(),
1466  settings.tracesSettings().maximumTailLengthUnit() ) ); //as the particles go through 1 pix for dt=1 and Vmax
1467  mParticleField->addRandomParticles();
1468  mParticleField->moveParticles();
1469 }
1470 
1471 void QgsMeshVectorTraceRenderer::draw()
1472 {
1473  if ( mRendererContext.renderingStopped() )
1474  return;
1475  mRendererContext.painter()->drawImage( mParticleField->topLeft(), mParticleField->image() );
1476 }
1477 
QgsMeshDatasetSourceInterface::datasetGroupCount
virtual int datasetGroupCount() const =0
Returns number of datasets groups loaded.
QgsMeshRendererVectorSettings
Represents a renderer settings for vector datasets.
Definition: qgsmeshrenderersettings.h:411
QgsRectangle::height
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
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:318
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:325
QgsTriangularMesh::vertices
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
Definition: qgstriangularmesh.cpp:287
QgsMapToPixel::mapUnitsPerPixel
double mapUnitsPerPixel() const
Returns current map units per pixel.
Definition: qgsmaptopixel.cpp:128
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:38
QgsMeshVectorTraceAnimationGenerator::setMaxSpeedPixel
void setMaxSpeedPixel(int max)
Sets the max number of pixels that can be go through by the particles in 1 second.
QgsPointXY::x
Q_GADGET double x
Definition: qgspointxy.h:47
QgsMeshLayer::reload
void reload() override
Synchronises with changes in the datasource.
Definition: qgsmeshlayer.cpp:1227
QgsMeshVectorTraceAnimationGenerator::setParticlesColor
void setParticlesColor(const QColor &c)
Sets colors of particle.
QgsMeshLayer::rendererCache
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering)
Definition: qgsmeshlayer.cpp:282
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsVector::rotateBy
QgsVector rotateBy(double rot) const SIP_HOLDGIL
Rotates the vector by a specified angle.
Definition: qgsvector.cpp:21
QgsTemporalRangeObject::temporalRange
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
Definition: qgstemporalrangeobject.cpp:43
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:58
QgsMeshVectorTraceAnimationGenerator::seedRandomParticles
void seedRandomParticles(int count)
seeds particles in the vector fields
QgsMeshVectorTraceAnimationGenerator
A wrapper for QgsMeshParticuleTracesField used to render the particles.
Definition: qgsmeshtracerenderer.h:567
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:248
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
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:287
QgsVector::length
double length() const SIP_HOLDGIL
Returns the length of the vector.
Definition: qgsvector.h:128
QgsRenderContext::coordinateTransform
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Definition: qgsrendercontext.h:245
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:357
QgsMeshLayer::dataProvider
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
Definition: qgsmeshlayer.cpp:156
QgsCsException
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
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
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:113
QgsMeshDatasetIndex
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
Definition: qgsmeshdataset.h:47
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
QgsMapToPixel::mapRotation
double mapRotation() const
Returns current map rotation in degrees (clockwise)
Definition: qgsmaptopixel.cpp:158
QgsVector::x
double x() const SIP_HOLDGIL
Returns the vector's x-component.
Definition: qgsvector.h:147
QgsMeshLayer
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:95
QgsMeshRendererVectorTracesSettings::maximumTailLength
double maximumTailLength() const
Returns the maximum tail length.
Definition: qgsmeshrenderersettings.cpp:725
QgsInterpolatedLineColor
Class defining color to render mesh datasets.
Definition: qgsinterpolatedlinerenderer.h:35
QgsVector::y
double y() const SIP_HOLDGIL
Returns the vector's y-component.
Definition: qgsvector.h:156
QgsMeshVectorTraceAnimationGenerator::imageRendered
QImage imageRendered()
Moves all the particles using frame per second (fps) to calculate the displacement and return the ren...
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:638
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsMeshVectorTraceAnimationGenerator::setMinimumTailLength
void setMinimumTailLength(int l)
Sets the minimum tail length.
QgsScopedQPainterState
Scoped object for saving and restoring a QPainter object's state.
Definition: qgsrendercontext.h:1120
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
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:44
QgsMeshDatasetGroupMetadata::dataType
DataType dataType() const
Returns whether dataset group data is defined on vertices or faces or volumes.
Definition: qgsmeshdataset.cpp:171
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
M_DEG2RAD
#define M_DEG2RAD
Definition: qgslayoututils.cpp:27
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
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
Definition: qgsmeshdataset.h:350
qgsmeshtracerenderer.h
qgsmeshlayerrenderer.h
QgsRenderContext::mapExtent
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
Definition: qgsrendercontext.h:318
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
QgsMeshDatasetGroupMetadata::maximum
double maximum() const
Returns maximum scalar value/vector magnitude present for whole dataset group.
Definition: qgsmeshdataset.cpp:181
QgsMapToPixel
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:38
QgsRectangle::width
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QgsVector
A class to represent a vector.
Definition: qgsvector.h:30
QgsAbstractGeometry::vertices
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
Definition: qgsabstractgeometry.cpp:282
QgsMesh::vertices
QVector< QgsMeshVertex > vertices
Definition: qgsmeshdataprovider.h:111
QgsMeshDataBlock
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e....
Definition: qgsmeshdataset.h:136
QgsMeshLayer::nativeMesh
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
Definition: qgsmeshlayer.cpp:238
QgsTriangularMesh
Triangular/Derived Mesh is mesh with vertices in map coordinates.
Definition: qgstriangularmesh.h:50
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:178
QgsMeshRendererVectorSettings::streamLinesSettings
QgsMeshRendererVectorStreamlineSettings streamLinesSettings() const
Returns settings for vector rendered with streamlines.
Definition: qgsmeshrenderersettings.cpp:587
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.
QgsMeshDataBlock::isValid
bool isValid() const
Whether the block is valid.
Definition: qgsmeshdataset.cpp:261