27#define M_DEG2RAD 0.0174532925
33 if ( mCacheFaceIndex != -1 && mCacheFaceIndex < mTriangularMesh.triangles().count() )
35 QgsVector res = interpolatedValuePrivate( mCacheFaceIndex, point );
36 if ( isVectorValid( res ) )
38 activeFaceFilter( res, mCacheFaceIndex );
44 QList<int> potentialFaceIndexes = mTriangularMesh.faceIndexesForRectangle(
QgsRectangle( point, point ) );
46 for (
const int faceIndex : potentialFaceIndexes )
48 QgsVector res = interpolatedValuePrivate( faceIndex, point );
49 if ( isVectorValid( res ) )
51 mCacheFaceIndex = faceIndex;
52 activeFaceFilter( res, mCacheFaceIndex );
58 return (
QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) );
62QgsMeshVectorValueInterpolator &QgsMeshVectorValueInterpolator::operator=(
const QgsMeshVectorValueInterpolator &other )
64 mTriangularMesh = other.mTriangularMesh;
65 mDatasetValues = other.mDatasetValues;
66 mActiveFaceFlagValues = other.mActiveFaceFlagValues;
67 mFaceCache = other.mFaceCache;
68 mCacheFaceIndex = other.mCacheFaceIndex;
69 mUseScalarActiveFaceFlagValues = other.mUseScalarActiveFaceFlagValues;
74QgsMeshVectorValueInterpolatorFromVertex::
76 : QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
81QgsMeshVectorValueInterpolatorFromVertex::
85 : QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
90QgsMeshVectorValueInterpolatorFromVertex::QgsMeshVectorValueInterpolatorFromVertex(
const QgsMeshVectorValueInterpolatorFromVertex &other ):
91 QgsMeshVectorValueInterpolator( other )
94QgsMeshVectorValueInterpolatorFromVertex *QgsMeshVectorValueInterpolatorFromVertex::clone()
96 return new QgsMeshVectorValueInterpolatorFromVertex( *
this );
99QgsMeshVectorValueInterpolatorFromVertex &QgsMeshVectorValueInterpolatorFromVertex::
100operator=(
const QgsMeshVectorValueInterpolatorFromVertex &other )
102 QgsMeshVectorValueInterpolator::operator=( other );
106QgsVector QgsMeshVectorValueInterpolatorFromVertex::interpolatedValuePrivate(
int faceIndex,
const QgsPointXY point )
const
108 QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
115 mDatasetValues.value( face.at( 0 ) ).y() );
118 mDatasetValues.value( face.at( 1 ) ).y() );
121 mDatasetValues.value( face.at( 2 ) ).y() );
123 return QgsMeshLayerUtils::interpolateVectorFromVerticesData(
133QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator(
const QgsTriangularMesh &triangularMesh,
135 mTriangularMesh( triangularMesh ),
136 mDatasetValues( datasetVectorValues ),
137 mUseScalarActiveFaceFlagValues( false )
140QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator(
const QgsTriangularMesh &triangularMesh,
143 mTriangularMesh( triangularMesh ),
144 mDatasetValues( datasetVectorValues ),
145 mActiveFaceFlagValues( scalarActiveFaceFlagValues ),
146 mUseScalarActiveFaceFlagValues( true )
149QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator(
const QgsMeshVectorValueInterpolator &other ):
150 mTriangularMesh( other.mTriangularMesh ),
151 mDatasetValues( other.mDatasetValues ),
152 mActiveFaceFlagValues( other.mActiveFaceFlagValues ),
153 mFaceCache( other.mFaceCache ),
154 mCacheFaceIndex( other.mCacheFaceIndex ),
155 mUseScalarActiveFaceFlagValues( other.mUseScalarActiveFaceFlagValues )
158void QgsMeshVectorValueInterpolator::updateCacheFaceIndex(
const QgsPointXY &point )
const
162 mCacheFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
166bool QgsMeshVectorValueInterpolator::isVectorValid(
const QgsVector &v )
const
168 return !( std::isnan( v.
x() ) || std::isnan( v.
y() ) );
172void QgsMeshVectorValueInterpolator::activeFaceFilter(
QgsVector &vector,
int faceIndex )
const
174 if ( mUseScalarActiveFaceFlagValues && ! mActiveFaceFlagValues.active( mTriangularMesh.trianglesToNativeFaces()[faceIndex] ) )
175 vector =
QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) ;
178QSize QgsMeshStreamField::size()
const
183QPoint QgsMeshStreamField::topLeft()
const
185 return mFieldTopLeftInDeviceCoordinates;
188int QgsMeshStreamField::resolution()
const
190 return mFieldResolution;
193QgsPointXY QgsMeshStreamField::positionToMapCoordinates(
const QPoint &pixelPosition,
const QgsPointXY &positionInPixel )
195 QgsPointXY mapPoint = mMapToFieldPixel.toMapCoordinates( pixelPosition );
196 mapPoint = mapPoint +
QgsVector( positionInPixel.
x() * mMapToFieldPixel.mapUnitsPerPixel(),
197 positionInPixel.
y() * mMapToFieldPixel.mapUnitsPerPixel() );
201QgsMeshStreamField::QgsMeshStreamField(
206 double magnitudeMaximum,
bool dataIsOnVertices,
210 mFieldResolution( resolution ),
211 mVectorColoring( vectorColoring ),
212 mLayerExtent( layerExtent ),
213 mMaximumMagnitude( magnitudeMaximum ),
214 mRenderContext( rendererContext )
216 if ( dataIsOnVertices )
218 if ( scalarActiveFaceFlagValues.
isValid() )
219 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
221 scalarActiveFaceFlagValues ) );
223 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
224 dataSetVectorValues ) );
228 if ( scalarActiveFaceFlagValues.
isValid() )
229 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
231 scalarActiveFaceFlagValues ) );
233 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
234 dataSetVectorValues ) );
238QgsMeshStreamField::QgsMeshStreamField(
const QgsMeshStreamField &other ):
239 mFieldSize( other.mFieldSize ),
240 mFieldResolution( other.mFieldResolution ),
242 mTraceImage( other.mTraceImage ),
243 mMapToFieldPixel( other.mMapToFieldPixel ),
244 mVectorColoring( other.mVectorColoring ),
245 mPixelFillingCount( other.mPixelFillingCount ),
246 mMaxPixelFillingCount( other.mMaxPixelFillingCount ),
247 mLayerExtent( other.mLayerExtent ),
248 mMapExtent( other.mMapExtent ),
249 mFieldTopLeftInDeviceCoordinates( other.mFieldTopLeftInDeviceCoordinates ),
250 mValid( other.mValid ),
251 mMaximumMagnitude( other.mMaximumMagnitude ),
252 mPixelFillingDensity( other.mPixelFillingDensity ),
253 mMinMagFilter( other.mMinMagFilter ),
254 mMaxMagFilter( other.mMaxMagFilter ),
255 mRenderContext( other.mRenderContext ),
256 mMinimizeFieldSize( other.mMinimizeFieldSize )
258 mPainter.reset(
new QPainter( &mTraceImage ) );
259 mVectorValueInterpolator =
260 std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
263QgsMeshStreamField::~QgsMeshStreamField()
284 layerExtent = mMapExtent;
288 if ( mMinimizeFieldSize )
289 interestZoneExtent = layerExtent.
intersect( mMapExtent );
291 interestZoneExtent = mMapExtent;
296 mFieldSize = QSize();
297 mFieldTopLeftInDeviceCoordinates = QPoint();
303 QgsRectangle fieldInterestZoneInDeviceCoordinates = QgsMeshLayerUtils::boundingBoxToScreenRectangle( deviceMapToPixel, interestZoneExtent );
304 mFieldTopLeftInDeviceCoordinates = QPoint(
int( fieldInterestZoneInDeviceCoordinates.
xMinimum() ),
int( fieldInterestZoneInDeviceCoordinates.
yMinimum() ) );
305 int fieldWidthInDeviceCoordinate = int( fieldInterestZoneInDeviceCoordinates.
width() );
306 int fieldHeightInDeviceCoordinate = int ( fieldInterestZoneInDeviceCoordinates.
height() );
308 int fieldWidth = int( fieldWidthInDeviceCoordinate / mFieldResolution );
309 int fieldHeight = int( fieldHeightInDeviceCoordinate / mFieldResolution );
312 if ( fieldWidthInDeviceCoordinate % mFieldResolution > 0 )
314 if ( fieldHeightInDeviceCoordinate % mFieldResolution > 0 )
317 if ( fieldWidth == 0 || fieldHeight == 0 )
319 mFieldSize = QSize();
323 mFieldSize.setWidth( fieldWidth );
324 mFieldSize.setHeight( fieldHeight );
327 double mapUnitPerFieldPixel;
328 if ( interestZoneExtent.
width() > 0 )
329 mapUnitPerFieldPixel = deviceMapToPixel.
mapUnitsPerPixel() * mFieldResolution * mFieldSize.width() / ( fieldWidthInDeviceCoordinate / mFieldResolution ) ;
331 mapUnitPerFieldPixel = 1e-8;
333 int fieldRightDevice = mFieldTopLeftInDeviceCoordinates.x() + mFieldSize.width() * mFieldResolution;
334 int fieldBottomDevice = mFieldTopLeftInDeviceCoordinates.y() + mFieldSize.height() * mFieldResolution;
337 int fieldTopDevice = mFieldTopLeftInDeviceCoordinates.
x();
338 int fieldLeftDevice = mFieldTopLeftInDeviceCoordinates.y();
341 double xc = ( fieldRightBottomMap.
x() + fieldTopLeftMap.
x() ) / 2;
342 double yc = ( fieldTopLeftMap.
y() + fieldRightBottomMap.
y() ) / 2;
356void QgsMeshStreamField::updateSize(
const QgsRenderContext &renderContext,
int resolution )
358 if ( renderContext.
mapExtent() == mMapExtent && resolution == mFieldResolution )
360 mFieldResolution = resolution;
362 updateSize( renderContext );
365bool QgsMeshStreamField::isValid()
const
370void QgsMeshStreamField::addTrace(
QgsPointXY startPoint )
372 addTrace( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint() );
376void QgsMeshStreamField::addRandomTraces()
378 if ( mMaximumMagnitude > 0 )
379 while ( mPixelFillingCount < mMaxPixelFillingCount && !mRenderContext.renderingStopped() )
383void QgsMeshStreamField::addRandomTrace()
388 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
389 int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
390 addTrace( QPoint( xRandom, yRandom ) );
393void QgsMeshStreamField::addGriddedTraces(
int dx,
int dy )
396 while ( i < mFieldSize.width() && !mRenderContext.renderingStopped() )
399 while ( j < mFieldSize.height() && !mRenderContext.renderingStopped() )
401 addTrace( QPoint( i, j ) );
412 for (
auto f : std::as_const( facesInExtent ) )
415 for (
auto i : std::as_const( face ) )
416 vertices.insert( i );
419 for (
auto i : std::as_const( vertices ) )
421 addTrace( mesh.
vertices().at( i ) );
425void QgsMeshStreamField::addTrace( QPoint startPixel )
431 if ( isTraceExists( startPixel ) || isTraceOutside( startPixel ) )
434 if ( !mVectorValueInterpolator )
437 if ( !( mMaximumMagnitude > 0 ) )
440 mPainter->setPen( mPen );
446 std::list<QPair<QPoint, FieldData>> chunkTrace;
448 QPoint currentPixel = startPixel;
453 while ( !mRenderContext.renderingStopped() )
456 vector = mVectorValueInterpolator->vectorValue( mapPosition ) ;
458 if ( std::isnan( vector.
x() ) || std::isnan( vector.
y() ) )
460 mPixelFillingCount++;
461 setChunkTrace( chunkTrace );
462 drawChunkTrace( chunkTrace );
470 QgsVector vu = vector / mMaximumMagnitude * 2;
471 data.magnitude = vector.
length();
475 double Vu = data.magnitude / mMaximumMagnitude * 2;
480 addPixelToChunkTrace( currentPixel, data, chunkTrace );
481 simplifyChunkTrace( chunkTrace );
482 setChunkTrace( chunkTrace );
483 drawChunkTrace( chunkTrace );
491 if ( nextPosition.
x() > 1 )
493 if ( nextPosition.
x() < -1 )
495 if ( nextPosition.
y() > 1 )
497 if ( nextPosition.
y() < -1 )
502 if ( incX != 0 || incY != 0 )
504 data.directionX = incX;
505 data.directionY = -incY;
507 if ( chunkTrace.empty() )
509 storeInField( QPair<QPoint, FieldData>( currentPixel, data ) );
511 if ( addPixelToChunkTrace( currentPixel, data, chunkTrace ) )
513 setChunkTrace( chunkTrace );
514 drawChunkTrace( chunkTrace );
515 clearChunkTrace( chunkTrace );
519 currentPixel += QPoint( incX, -incY );
520 x1 = nextPosition.
x() - 2 * incX;
521 y1 = nextPosition.
y() - 2 * incY;
551 x2 = x1 + ( 1 - y1 ) * Vx / fabs( Vy ) ;
553 x2 = x1 + ( 1 + y1 ) * Vx / fabs( Vy ) ;
555 y2 = y1 + ( 1 - x1 ) * Vy / fabs( Vx ) ;
557 y2 = y1 + ( 1 + x1 ) * Vy / fabs( Vx ) ;
584 double dl = sqrt( dx * dx + dy * dy );
586 data.time += dl / Vu ;
587 if ( data.time > 10000 )
589 addPixelToChunkTrace( currentPixel, data, chunkTrace );
590 setChunkTrace( chunkTrace );
591 drawChunkTrace( chunkTrace );
599 if ( isTraceExists( currentPixel ) )
602 setChunkTrace( chunkTrace );
603 addPixelToChunkTrace( currentPixel, data, chunkTrace );
604 drawChunkTrace( chunkTrace );
608 if ( isTraceOutside( currentPixel ) )
610 setChunkTrace( chunkTrace );
611 drawChunkTrace( chunkTrace );
617void QgsMeshStreamField::setResolution(
int width )
619 mFieldResolution = width;
622QSize QgsMeshStreamField::imageSize()
const
624 return mFieldSize * mFieldResolution;
627QPointF QgsMeshStreamField::fieldToDevice(
const QPoint &pixel )
const
630 p = mFieldResolution * p + QPointF( mFieldResolution - 1, mFieldResolution - 1 ) / 2;
634bool QgsMeshStreamField::addPixelToChunkTrace( QPoint &pixel,
635 QgsMeshStreamField::FieldData &data,
636 std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
638 chunkTrace.emplace_back( pixel, data );
639 if ( chunkTrace.size() == 3 )
641 simplifyChunkTrace( chunkTrace );
647void QgsMeshStreamlinesField::initField()
649 mField = QVector<bool>( mFieldSize.width() * mFieldSize.height(),
false );
653QgsMeshStreamlinesField::QgsMeshStreamlinesField(
const QgsTriangularMesh &triangularMesh,
658 bool dataIsOnVertices,
661 QgsMeshStreamField( triangularMesh,
663 scalarActiveFaceFlagValues,
671QgsMeshStreamlinesField::QgsMeshStreamlinesField(
const QgsMeshStreamlinesField &other ):
672 QgsMeshStreamField( other ),
673 mField( other.mField )
676QgsMeshStreamlinesField &QgsMeshStreamlinesField::operator=(
const QgsMeshStreamlinesField &other )
678 QgsMeshStreamField::operator=( other );
679 mField = other.mField;
683void QgsMeshStreamlinesField::storeInField(
const QPair<QPoint, FieldData> pixelData )
685 int i = pixelData.first.x();
686 int j = pixelData.first.y();
687 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
689 mField[j * mFieldSize.width() + i] =
true;
693void QgsMeshStreamField::setChunkTrace( std::list<QPair<QPoint, FieldData> > &chunkTrace )
695 auto p = chunkTrace.begin();
696 while ( p != chunkTrace.end() )
698 storeInField( ( *p ) );
699 mPixelFillingCount++;
704void QgsMeshStreamlinesField::drawChunkTrace(
const std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
706 auto p1 = chunkTrace.begin();
709 while ( p2 != chunkTrace.end() )
711 double mag1 = ( *p1 ).second.magnitude;
712 double mag2 = ( *p2 ).second.magnitude;
713 if ( filterMag( mag1 ) && filterMag( mag2 ) )
715 QPen pen = mPainter->pen();
716 pen.setColor( mVectorColoring.color( ( mag1 + mag2 ) / 2 ) );
717 mPainter->setPen( pen );
718 mPainter->drawLine( fieldToDevice( ( *p1 ).first ), fieldToDevice( ( *p2 ).first ) );
726void QgsMeshStreamField::clearChunkTrace( std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
728 auto one_before_end = std::prev( chunkTrace.end() );
729 chunkTrace.erase( chunkTrace.begin(), one_before_end );
732void QgsMeshStreamField::simplifyChunkTrace( std::list<QPair<QPoint, FieldData> > &shunkTrace )
734 if ( shunkTrace.size() != 3 )
737 auto ip3 = shunkTrace.begin();
741 while ( ip3 != shunkTrace.end() && ip2 != shunkTrace.end() )
743 QPoint v1 = ( *ip1 ).first - ( *ip2 ).first;
744 QPoint v2 = ( *ip2 ).first - ( *ip3 ).first;
745 if ( v1.x()*v2.x() + v1.y()*v2.y() == 0 )
747 ( *ip1 ).second.time += ( ( *ip2 ).second.time ) / 2;
748 ( *ip3 ).second.time += ( ( *ip2 ).second.time ) / 2;
749 ( *ip1 ).second.directionX += ( *ip2 ).second.directionX;
750 ( *ip1 ).second.directionY += ( *ip2 ).second.directionY;
751 shunkTrace.erase( ip2 );
758bool QgsMeshStreamlinesField::isTraceExists(
const QPoint &pixel )
const
762 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
764 return mField[j * mFieldSize.width() + i];
770bool QgsMeshStreamField::isTraceOutside(
const QPoint &pixel )
const
774 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
781void QgsMeshStreamField::setMinimizeFieldSize(
bool minimizeFieldSize )
783 mMinimizeFieldSize = minimizeFieldSize;
786QgsMeshStreamField &QgsMeshStreamField::operator=(
const QgsMeshStreamField &other )
788 mFieldSize = other.mFieldSize ;
789 mFieldResolution = other.mFieldResolution;
791 mTraceImage = other.mTraceImage ;
792 mMapToFieldPixel = other.mMapToFieldPixel ;
793 mVectorColoring = other.mVectorColoring;
794 mPixelFillingCount = other.mPixelFillingCount ;
795 mMaxPixelFillingCount = other.mMaxPixelFillingCount ;
796 mLayerExtent = other.mLayerExtent ;
797 mMapExtent = other.mMapExtent;
798 mFieldTopLeftInDeviceCoordinates = other.mFieldTopLeftInDeviceCoordinates ;
799 mValid = other.mValid ;
800 mMaximumMagnitude = other.mMaximumMagnitude ;
801 mPixelFillingDensity = other.mPixelFillingDensity ;
802 mMinMagFilter = other.mMinMagFilter ;
803 mMaxMagFilter = other.mMaxMagFilter ;
804 mMinimizeFieldSize = other.mMinimizeFieldSize ;
805 mVectorValueInterpolator =
806 std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
808 mPainter.reset(
new QPainter( &mTraceImage ) );
813void QgsMeshStreamField::initImage()
816 mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
817 mTraceImage.fill( 0X00000000 );
819 mPainter.reset(
new QPainter( &mTraceImage ) );
820 mPainter->setRenderHint( QPainter::Antialiasing,
true );
821 mPainter->setPen( mPen );
824bool QgsMeshStreamField::filterMag(
double value )
const
826 return ( mMinMagFilter < 0 || value > mMinMagFilter ) && ( mMaxMagFilter < 0 || value < mMaxMagFilter );
829QImage QgsMeshStreamField::image()
831 return mTraceImage.scaled( mFieldSize * mFieldResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
834void QgsMeshStreamField::setPixelFillingDensity(
double maxFilling )
836 mPixelFillingDensity = maxFilling;
837 mMaxPixelFillingCount = int( mPixelFillingDensity * mFieldSize.width() * mFieldSize.height() );
840void QgsMeshStreamField::setColor( QColor color )
842 mPen.setColor( color );
845void QgsMeshStreamField::setLineWidth(
double width )
847 mPen.setWidthF( width );
850void QgsMeshStreamField::setFilter(
double min,
double max )
856QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace(
const QgsTriangularMesh &triangularMesh,
const QgsMeshDataBlock &datasetVectorValues ):
857 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
861 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
864QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace(
const QgsMeshVectorValueInterpolatorFromFace &other ):
865 QgsMeshVectorValueInterpolator( other )
868QgsMeshVectorValueInterpolatorFromFace *QgsMeshVectorValueInterpolatorFromFace::clone()
870 return new QgsMeshVectorValueInterpolatorFromFace( *
this );
873QgsMeshVectorValueInterpolatorFromFace &QgsMeshVectorValueInterpolatorFromFace::operator=(
const QgsMeshVectorValueInterpolatorFromFace &other )
875 QgsMeshVectorValueInterpolator::operator=( other );
879QgsVector QgsMeshVectorValueInterpolatorFromFace::interpolatedValuePrivate(
int faceIndex,
const QgsPointXY point )
const
881 QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
887 QgsVector vect =
QgsVector( mDatasetValues.value( mTriangularMesh.trianglesToNativeFaces().at( faceIndex ) ).x(),
888 mDatasetValues.value( mTriangularMesh.trianglesToNativeFaces().at( faceIndex ) ).y() );
890 return QgsMeshLayerUtils::interpolateVectorFromFacesData(
898QgsMeshVectorStreamlineRenderer::QgsMeshVectorStreamlineRenderer(
902 bool dataIsOnVertices,
906 mRendererContext( rendererContext )
908 mStreamlineField.reset(
new QgsMeshStreamlinesField( triangularMesh,
910 scalarActiveFaceFlagValues,
917 mStreamlineField->updateSize( rendererContext );
920 QgsUnitTypes::RenderUnit::RenderMillimeters ) ) ;
921 mStreamlineField->setColor( settings.
color() );
930 mStreamlineField->addTracesOnMesh( triangularMesh, rendererContext.
mapExtent() );
933 mStreamlineField->addRandomTraces();
938void QgsMeshVectorStreamlineRenderer::draw()
940 if ( mRendererContext.renderingStopped() )
942 mRendererContext.painter()->drawImage( mStreamlineField->topLeft(), mStreamlineField->image() );
945QgsMeshParticleTracesField::QgsMeshParticleTracesField(
const QgsTriangularMesh &triangularMesh,
950 bool dataIsOnVertices,
953 QgsMeshStreamField( triangularMesh,
955 scalarActiveFaceFlagValues,
962 std::srand( uint( ::time(
nullptr ) ) );
963 mPen.setCapStyle( Qt::RoundCap );
966QgsMeshParticleTracesField::QgsMeshParticleTracesField(
const QgsMeshParticleTracesField &other ):
967 QgsMeshStreamField( other ),
968 mTimeField( other.mTimeField ),
969 mMagnitudeField( other.mMagnitudeField ),
970 mDirectionField( other.mDirectionField ),
971 mParticles( other.mParticles ),
972 mStumpImage( other.mStumpImage ),
973 mTimeStep( other.mTimeStep ),
974 mParticlesLifeTime( other.mParticlesLifeTime ),
975 mParticlesCount( other.mParticlesCount ),
976 mTailFactor( other.mTailFactor ),
977 mParticleColor( other.mParticleColor ),
978 mParticleSize( other.mParticleSize ),
979 mStumpFactor( other.mStumpFactor )
982void QgsMeshParticleTracesField::addParticle(
const QPoint &startPoint,
double lifeTime )
984 addTrace( startPoint );
985 if ( time( startPoint ) > 0 )
987 QgsMeshTraceParticle p;
988 p.lifeTime = lifeTime;
989 p.position = startPoint;
990 mParticles.append( p );
995void QgsMeshParticleTracesField::addParticleXY(
const QgsPointXY &startPoint,
double lifeTime )
997 addParticle( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint(), lifeTime );
1000void QgsMeshParticleTracesField::moveParticles()
1003 for (
auto &p : mParticles )
1005 double spentTime = p.remainingTime;
1006 size_t countAdded = 0;
1007 while ( spentTime < mTimeStep && p.lifeTime > 0 )
1009 double timeToSpend = double( time( p.position ) );
1010 if ( timeToSpend > 0 )
1012 p.lifeTime -= timeToSpend;
1013 spentTime += timeToSpend;
1014 QPoint dir = direction( p.position );
1015 if ( p.lifeTime > 0 )
1018 p.tail.emplace_back( p.position );
1033 if ( p.lifeTime <= 0 )
1041 p.remainingTime = spentTime - mTimeStep;
1042 while (
int( p.tail.size() ) > mMinTailLength && p.tail.size() > countAdded * mTailFactor )
1043 p.tail.erase( p.tail.begin() );
1044 drawParticleTrace( p );
1050 while ( i < mParticles.count() )
1052 if ( mParticles.at( i ).tail.size() == 0 )
1053 mParticles.removeAt( i );
1059 if ( mParticles.count() < mParticlesCount )
1060 addRandomParticles();
1063void QgsMeshParticleTracesField::addRandomParticles()
1068 if ( mParticlesCount < 0 )
1070 addParticleXY(
QgsPointXY( mMapToFieldPixel.xCenter(), mMapToFieldPixel.yCenter() ), mParticlesLifeTime );
1074 int count = mParticlesCount - mParticles.count();
1076 for (
int i = 0; i < count; ++i )
1078 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
1079 int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
1080 double lifeTime = ( std::rand() / ( ( RAND_MAX + 1u ) / mParticlesLifeTime ) );
1081 addParticle( QPoint( xRandom, yRandom ), lifeTime );
1085void QgsMeshParticleTracesField::storeInField(
const QPair<QPoint, QgsMeshStreamField::FieldData> pixelData )
1087 int i = pixelData.first.x();
1088 int j = pixelData.first.y();
1089 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1091 mTimeField[j * mFieldSize.width() + i] = pixelData.second.time;
1092 int d = pixelData.second.directionX + 2 + ( pixelData.second.directionY + 1 ) * 3;
1093 mDirectionField[j * mFieldSize.width() + i] =
static_cast<char>( d );
1094 mMagnitudeField[j * mFieldSize.width() + i] = pixelData.second.magnitude;
1098void QgsMeshParticleTracesField::initField()
1100 mTimeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), -1 );
1101 mDirectionField = QVector<char>( mFieldSize.width() * mFieldSize.height(),
static_cast<char>(
int( 0 ) ) );
1102 mMagnitudeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), 0 );
1104 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1105 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1108bool QgsMeshParticleTracesField::isTraceExists(
const QPoint &pixel )
const
1112 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1114 return mTimeField[j * mFieldSize.width() + i] >= 0;
1120void QgsMeshParticleTracesField::setStumpParticleWithLifeTime(
bool stumpParticleWithLifeTime )
1122 mStumpParticleWithLifeTime = stumpParticleWithLifeTime;
1125void QgsMeshParticleTracesField::setParticlesColor(
const QColor &
c )
1127 mVectorColoring.setColor(
c );
1130void QgsMeshParticleTracesField::setMinTailLength(
int minTailLength )
1132 mMinTailLength = minTailLength;
1135QgsMeshParticleTracesField &QgsMeshParticleTracesField::operator=(
const QgsMeshParticleTracesField &other )
1137 QgsMeshStreamField::operator=( other );
1138 mTimeField = other.mTimeField;
1139 mDirectionField = other.mDirectionField;
1140 mParticles = other.mParticles;
1141 mStumpImage = other.mStumpImage;
1142 mTimeStep = other.mTimeStep;
1143 mParticlesLifeTime = other.mParticlesLifeTime;
1144 mParticlesCount = other.mParticlesCount;
1145 mTailFactor = other.mTailFactor;
1146 mParticleColor = other.mParticleColor;
1147 mParticleSize = other.mParticleSize;
1148 mStumpFactor = other.mStumpFactor;
1153void QgsMeshParticleTracesField::setTailFactor(
double tailFactor )
1155 mTailFactor = tailFactor;
1158void QgsMeshParticleTracesField::setParticleSize(
double particleSize )
1160 mParticleSize = particleSize;
1163void QgsMeshParticleTracesField::setTimeStep(
double timeStep )
1165 mTimeStep = timeStep;
1168void QgsMeshParticleTracesField::setParticlesLifeTime(
double particlesLifeTime )
1170 mParticlesLifeTime = particlesLifeTime;
1173QImage QgsMeshParticleTracesField::imageRendered()
const
1178void QgsMeshParticleTracesField::stump()
1181 mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1182 mPainter->drawImage( QPoint( 0, 0 ), mStumpImage );
1185void QgsMeshParticleTracesField::setStumpFactor(
int sf )
1188 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1189 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1192QPoint QgsMeshParticleTracesField::direction( QPoint position )
const
1194 int i = position.x();
1195 int j = position.y();
1196 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1198 int dir =
static_cast<int>( mDirectionField[j * mFieldSize.width() + i] );
1199 if ( dir != 0 && dir < 10 )
1200 return QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
1202 return QPoint( 0, 0 );
1205float QgsMeshParticleTracesField::time( QPoint position )
const
1207 int i = position.x();
1208 int j = position.y();
1209 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1211 return mTimeField[j * mFieldSize.width() + i];
1216float QgsMeshParticleTracesField::magnitude( QPoint position )
const
1218 int i = position.x();
1219 int j = position.y();
1220 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1222 return mMagnitudeField[j * mFieldSize.width() + i];
1227void QgsMeshParticleTracesField::drawParticleTrace(
const QgsMeshTraceParticle &particle )
1229 const std::list<QPoint> &tail = particle.tail;
1230 if ( tail.size() == 0 )
1232 double iniWidth = mParticleSize;
1233 double finWidth = 0;
1235 size_t pixelCount = tail.size();
1237 double transparency = 1;
1238 if ( mStumpParticleWithLifeTime )
1239 transparency = sin( M_PI * particle.lifeTime / mParticlesLifeTime );
1242 if ( pixelCount > 1 )
1243 dw = ( iniWidth - finWidth ) / ( pixelCount );
1247 auto ip1 = std::prev( tail.end() );
1248 auto ip2 = std::prev( ip1 );
1250 while ( ip1 != tail.begin() )
1252 QPointF p1 = fieldToDevice( ( *ip1 ) );
1253 QPointF p2 = fieldToDevice( ( *ip2 ) );
1254 QColor traceColor = mVectorColoring.color( magnitude( *ip1 ) );
1255 traceColor.setAlphaF( traceColor.alphaF()*transparency );
1256 mPen.setColor( traceColor );
1257 mPen.setWidthF( iniWidth - i * dw );
1258 mPainter->setPen( mPen );
1259 mPainter->drawLine( p1, p2 );
1266void QgsMeshParticleTracesField::setParticlesCount(
int particlesCount )
1268 mParticlesCount = particlesCount;
1274 bool dataIsOnVertices,
1279 mRendererContext( rendererContext )
1281 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
new QgsMeshParticleTracesField( triangularMesh,
1282 dataSetVectorValues,
1283 scalarActiveFaceFlagValues,
1289 mParticleField->updateSize( rendererContext ) ;
1293 mRendererContext( rendererContext )
1300 bool vectorDataOnVertices;
1310 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
1311 ( cache->mActiveVectorDatasetIndex == datasetIndex ) )
1313 vectorDatasetValues = cache->mVectorDatasetValues;
1314 scalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
1315 magMax = cache->mVectorDatasetMagMaximum;
1326 if ( vectorDataOnVertices )
1331 vectorDatasetValues = QgsMeshLayerUtils::datasetValues( layer, datasetIndex, 0, count );
1339 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
new QgsMeshParticleTracesField( ( *layer->
triangularMesh() ),
1340 vectorDatasetValues,
1341 scalarActiveFaceFlagValues,
1344 vectorDataOnVertices,
1348 mParticleField->setMinimizeFieldSize(
false );
1349 mParticleField->updateSize( mRendererContext );
1353 mRendererContext( other.mRendererContext ),
1355 mVpixMax( other.mVpixMax ),
1356 mParticleLifeTime( other.mParticleLifeTime )
1358 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
1359 new QgsMeshParticleTracesField( *other.mParticleField ) );
1365 mParticleField->setParticlesCount( count );
1366 mParticleField->addRandomParticles();
1371 mParticleField->moveParticles();
1372 return mParticleField->image();
1382 updateFieldParameter();
1388 updateFieldParameter();
1393 mParticleLifeTime = particleLifeTime;
1394 updateFieldParameter();
1399 mParticleField->setParticlesColor(
c );
1404 mParticleField->setParticleSize( width );
1409 mParticleField->setTailFactor( fct );
1414 mParticleField->setMinTailLength( l );
1423 mParticleField->setStumpFactor(
int( 255 * p ) );
1428 mParticleField.reset(
new QgsMeshParticleTracesField( *mParticleField ) );
1429 const_cast<QgsRenderContext &
>( mRendererContext ) = other.mRendererContext;
1431 mVpixMax = other.mVpixMax;
1432 mParticleLifeTime = other.mParticleLifeTime;
1437void QgsMeshVectorTraceAnimationGenerator::updateFieldParameter()
1439 double fieldTimeStep = mVpixMax / mFPS;
1440 double fieldLifeTime = mParticleLifeTime * mFPS * fieldTimeStep;
1441 mParticleField->setTimeStep( fieldTimeStep );
1442 mParticleField->setParticlesLifeTime( fieldLifeTime );
1445QgsMeshVectorTraceRenderer::QgsMeshVectorTraceRenderer(
1449 bool dataIsOnVertices,
1454 mRendererContext( rendererContext )
1456 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
new QgsMeshParticleTracesField( triangularMesh,
1457 dataSetVectorValues,
1458 scalarActiveFaceFlagValues,
1464 mParticleField->updateSize( rendererContext ) ;
1467 settings.
lineWidth(), QgsUnitTypes::RenderUnit::RenderMillimeters ) );
1469 mParticleField->setTailFactor( 1 );
1470 mParticleField->setStumpParticleWithLifeTime(
false );
1473 mParticleField->addRandomParticles();
1474 mParticleField->moveParticles();
1477void QgsMeshVectorTraceRenderer::draw()
1479 if ( mRendererContext.renderingStopped() )
1481 mRendererContext.painter()->drawImage( mParticleField->topLeft(), mParticleField->image() );
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
Custom exception class for Coordinate Reference System related exceptions.
Class defining color to render mesh datasets.
Perform transforms between map coordinates and device coordinates.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e....
bool isValid() const
Whether the block is valid.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
int group() const
Returns a group index.
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.
virtual QgsMeshDataBlock areFacesActive(QgsMeshDatasetIndex index, int faceIndex, int count) const =0
Returns whether the faces are active for particular dataset.
virtual int datasetGroupCount() const =0
Returns number of datasets groups loaded.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsRectangle extent() const override
Returns the extent of the layer.
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
void reload() override
Synchronises with changes in the datasource.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
QgsMeshDatasetIndex activeVectorDatasetAtTime(const QgsDateTimeRange &timeRange) const
Returns dataset index from active vector group depending on the time range If the temporal properties...
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering)
QgsMeshRendererVectorSettings vectorSettings(int groupIndex) const
Returns renderer settings.
Represents a renderer settings for vector datasets.
int userGridCellWidth() const
Returns width in pixels of user grid cell.
QgsMeshRendererVectorTracesSettings tracesSettings() const
Returns settings for vector rendered with traces.
QColor color() const
Returns color used for drawing arrows.
int userGridCellHeight() const
Returns height in pixels of user grid cell.
double lineWidth() const
Returns line width of the arrow (in millimeters)
double filterMax() const
Returns filter value for vector magnitudes.
QgsInterpolatedLineColor vectorStrokeColoring() const
Returns the stroke coloring used to render vector datasets.
bool isOnUserDefinedGrid() const
Returns whether vectors are drawn on user-defined grid.
double filterMin() const
Returns filter value for vector magnitudes.
QgsMeshRendererVectorStreamlineSettings streamLinesSettings() const
Returns settings for vector rendered with streamlines.
SeedingStartPointsMethod seedingMethod() const
Returns the method used for seeding start points of strealines.
@ Random
Seeds start points randomly on the mesh.
@ MeshGridded
Seeds start points on the vertices mesh or user regular grid.
double seedingDensity() const
Returns the density used for seeding start points.
double maximumTailLength() const
Returns the maximum tail length.
QgsUnitTypes::RenderUnit maximumTailLengthUnit() const
Returns the maximum tail length unit.
int particlesCount() const
Returns particles count.
A wrapper for QgsMeshParticuleTracesField used to render the particles.
void setParticlesLifeTime(double particleLifeTime)
Sets maximum life time of particles in seconds.
QgsMeshVectorTraceAnimationGenerator & operator=(const QgsMeshVectorTraceAnimationGenerator &other)
Assignment operator.
void setMinimumTailLength(int l)
Sets the minimum tail length.
void setTailPersitence(double p)
Sets the visual persistence of the tail.
void setParticlesColor(const QColor &c)
Sets colors of particle.
QImage imageRendered()
Moves all the particles using frame per second (fps) to calculate the displacement and return the ren...
void setTailFactor(double fct)
Sets the tail factor, used to adjust the length of the tail. 0 : minimum length, >1 increase the tail...
void setFPS(int FPS)
Sets the number of frames per seconds that will be rendered.
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.
void setParticlesSize(double width)
Sets particle size in px.
void setMaxSpeedPixel(int max)
Sets the max number of pixels that can be go through by the particles in 1 second.
void seedRandomParticles(int count)
seeds particles in the vector fields
A class to represent a 2D point.
Point geometry type, with support for z-dimension and m-values.
A rectangle specified with double values.
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Scoped object for saving and restoring a QPainter object's state.
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
Triangular/Derived Mesh is mesh with vertices in map coordinates.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
A class to represent a vector.
QgsVector rotateBy(double rot) const SIP_HOLDGIL
Rotates the vector by a specified angle.
double y() const SIP_HOLDGIL
Returns the vector's y-component.
double x() const SIP_HOLDGIL
Returns the vector's x-component.
double length() const SIP_HOLDGIL
Returns the length of the vector.
bool isInTriangleFace(const QgsPointXY point, const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Tests if point p is on the face defined with vertices.
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QVector< int > QgsMeshFace
List of vertex indexes.
QVector< QgsMeshVertex > vertices
QVector< QgsMeshFace > faces