24 if ( mCacheFaceIndex != -1 && mCacheFaceIndex < mTriangularMesh.triangles().count() )
26 QgsVector res = interpolatedValuePrivate( mCacheFaceIndex, point );
27 if ( isVectorValid( res ) )
29 activeFaceFilter( res, mCacheFaceIndex );
35 QList<int> potentialFaceIndexes = mTriangularMesh.faceIndexesForRectangle(
QgsRectangle( point, point ) );
37 for (
const int faceIndex : potentialFaceIndexes )
39 QgsVector res = interpolatedValuePrivate( faceIndex, point );
40 if ( isVectorValid( res ) )
42 mCacheFaceIndex = faceIndex;
43 activeFaceFilter( res, mCacheFaceIndex );
49 return (
QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) );
53 QgsMeshVectorValueInterpolator &QgsMeshVectorValueInterpolator::operator=(
const QgsMeshVectorValueInterpolator &other )
55 mTriangularMesh = other.mTriangularMesh;
56 mDatasetValues = other.mDatasetValues;
57 mActiveFaceFlagValues = other.mActiveFaceFlagValues;
58 mFaceCache = other.mFaceCache;
59 mCacheFaceIndex = other.mCacheFaceIndex;
60 mUseScalarActiveFaceFlagValues = other.mUseScalarActiveFaceFlagValues;
65 QgsMeshVectorValueInterpolatorFromVertex::
67 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
72 QgsMeshVectorValueInterpolatorFromVertex::
73 QgsMeshVectorValueInterpolatorFromVertex(
const QgsTriangularMesh &triangularMesh,
76 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
81 QgsMeshVectorValueInterpolatorFromVertex::QgsMeshVectorValueInterpolatorFromVertex(
const QgsMeshVectorValueInterpolatorFromVertex &other ):
82 QgsMeshVectorValueInterpolator( other )
85 QgsMeshVectorValueInterpolatorFromVertex *QgsMeshVectorValueInterpolatorFromVertex::clone()
87 return new QgsMeshVectorValueInterpolatorFromVertex( *
this );
90 QgsMeshVectorValueInterpolatorFromVertex &QgsMeshVectorValueInterpolatorFromVertex::
91 operator=(
const QgsMeshVectorValueInterpolatorFromVertex &other )
93 QgsMeshVectorValueInterpolator::operator=( other );
97 QgsVector QgsMeshVectorValueInterpolatorFromVertex::interpolatedValuePrivate(
int faceIndex,
const QgsPointXY point )
const 99 QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
106 mDatasetValues.value( face.at( 0 ) ).y() );
109 mDatasetValues.value( face.at( 1 ) ).y() );
112 mDatasetValues.value( face.at( 2 ) ).y() );
114 return QgsMeshLayerUtils::interpolateVectorFromVerticesData(
124 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator(
const QgsTriangularMesh &triangularMesh,
126 mTriangularMesh( triangularMesh ),
127 mDatasetValues( datasetVectorValues ),
128 mUseScalarActiveFaceFlagValues( false )
131 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator(
const QgsTriangularMesh &triangularMesh,
134 mTriangularMesh( triangularMesh ),
135 mDatasetValues( datasetVectorValues ),
136 mActiveFaceFlagValues( scalarActiveFaceFlagValues ),
137 mUseScalarActiveFaceFlagValues( true )
140 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator(
const QgsMeshVectorValueInterpolator &other ):
141 mTriangularMesh( other.mTriangularMesh ),
142 mDatasetValues( other.mDatasetValues ),
143 mActiveFaceFlagValues( other.mActiveFaceFlagValues ),
144 mFaceCache( other.mFaceCache ),
145 mCacheFaceIndex( other.mCacheFaceIndex ),
146 mUseScalarActiveFaceFlagValues( other.mUseScalarActiveFaceFlagValues )
149 void QgsMeshVectorValueInterpolator::updateCacheFaceIndex(
const QgsPointXY &point )
const 153 mCacheFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
157 bool QgsMeshVectorValueInterpolator::isVectorValid(
const QgsVector &v )
const 159 return !( std::isnan( v.
x() ) || std::isnan( v.
y() ) );
163 void QgsMeshVectorValueInterpolator::activeFaceFilter(
QgsVector &vector,
int faceIndex )
const 165 if ( mUseScalarActiveFaceFlagValues && ! mActiveFaceFlagValues.active( mTriangularMesh.trianglesToNativeFaces()[faceIndex] ) )
166 vector =
QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) ;
169 QSize QgsMeshStreamField::size()
const 174 QPoint QgsMeshStreamField::topLeft()
const 176 return mFieldTopLeftInDeviceCoordinates;
179 int QgsMeshStreamField::resolution()
const 181 return mFieldResolution;
184 QgsPointXY QgsMeshStreamField::positionToMapCoordinates(
const QPoint &pixelPosition,
const QgsPointXY &positionInPixel )
186 QgsPointXY mapPoint = mMapToFieldPixel.toMapCoordinates( pixelPosition );
187 mapPoint = mapPoint +
QgsVector( positionInPixel.
x() * mMapToFieldPixel.mapUnitsPerPixel(),
188 positionInPixel.
y() * mMapToFieldPixel.mapUnitsPerPixel() );
193 mFieldResolution( resolution ),
194 mLayerExtent( layerExtent ),
195 mMaximumMagnitude( magnitudeMaximum ),
196 mRenderContext( rendererContext )
198 if ( dataIsOnVertices )
200 if ( scalarActiveFaceFlagValues.
isValid() )
201 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
203 scalarActiveFaceFlagValues ) );
205 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
206 dataSetVectorValues ) );
210 if ( scalarActiveFaceFlagValues.
isValid() )
211 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
213 scalarActiveFaceFlagValues ) );
215 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
216 dataSetVectorValues ) );
220 QgsMeshStreamField::QgsMeshStreamField(
const QgsMeshStreamField &other ):
221 mFieldSize( other.mFieldSize ),
222 mFieldResolution( other.mFieldResolution ),
224 mTraceImage( other.mTraceImage ),
225 mMapToFieldPixel( other.mMapToFieldPixel ),
226 mPixelFillingCount( other.mPixelFillingCount ),
227 mMaxPixelFillingCount( other.mMaxPixelFillingCount ),
228 mLayerExtent( other.mLayerExtent ),
229 mMapExtent( other.mMapExtent ),
230 mFieldTopLeftInDeviceCoordinates( other.mFieldTopLeftInDeviceCoordinates ),
231 mValid( other.mValid ),
232 mMaximumMagnitude( other.mMaximumMagnitude ),
233 mPixelFillingDensity( other.mPixelFillingDensity ),
234 mMinMagFilter( other.mMinMagFilter ),
235 mMaxMagFilter( other.mMaxMagFilter ),
236 mRenderContext( other.mRenderContext ),
237 mMinimizeFieldSize( other.mMinimizeFieldSize )
239 mPainter.reset(
new QPainter( &mTraceImage ) );
240 mVectorValueInterpolator =
241 std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
244 QgsMeshStreamField::~QgsMeshStreamField()
250 void QgsMeshStreamField::updateSize(
const QgsRenderContext &renderContext )
263 layerExtent = mMapExtent;
267 if ( mMinimizeFieldSize )
268 interestZoneExtent = layerExtent.
intersect( mMapExtent );
270 interestZoneExtent = mMapExtent;
275 mFieldSize = QSize();
276 mFieldTopLeftInDeviceCoordinates = QPoint();
288 mFieldTopLeftInDeviceCoordinates = interestZoneTopLeft.
toQPointF().toPoint();
289 QPoint mFieldBottomRightInDeviceCoordinates = interestZoneBottomRight.
toQPointF().toPoint();
290 int fieldWidthInDeviceCoordinate = mFieldBottomRightInDeviceCoordinates.x() - mFieldTopLeftInDeviceCoordinates.x();
291 int fieldHeightInDeviceCoordinate = mFieldBottomRightInDeviceCoordinates.y() - mFieldTopLeftInDeviceCoordinates.y();
293 int fieldWidth = int( fieldWidthInDeviceCoordinate / mFieldResolution );
294 int fieldHeight = int( fieldHeightInDeviceCoordinate / mFieldResolution );
297 if ( fieldWidthInDeviceCoordinate % mFieldResolution > 0 )
299 if ( fieldHeightInDeviceCoordinate % mFieldResolution > 0 )
302 if ( fieldWidth == 0 || fieldHeight == 0 )
304 mFieldSize = QSize();
308 mFieldSize.setWidth( fieldWidth );
309 mFieldSize.setHeight( fieldHeight );
313 double mapUnitPerFieldPixel;
314 if ( interestZoneExtent.
width() > 0 )
315 mapUnitPerFieldPixel = interestZoneExtent.
width() / fieldWidthInDeviceCoordinate * mFieldResolution;
317 mapUnitPerFieldPixel = 1e-8;
319 int fieldRightDevice = mFieldTopLeftInDeviceCoordinates.x() + mFieldSize.width() * mFieldResolution;
320 int fieldBottomDevice = mFieldTopLeftInDeviceCoordinates.y() + mFieldSize.height() * mFieldResolution;
323 int fieldTopDevice = mFieldTopLeftInDeviceCoordinates.
x();
324 int fieldLeftDevice = mFieldTopLeftInDeviceCoordinates.y();
327 double xc = ( fieldRightBottomMap.
x() + fieldTopLeftMap.
x() ) / 2;
328 double yc = ( fieldTopLeftMap.
y() + fieldRightBottomMap.
y() ) / 2;
340 void QgsMeshStreamField::updateSize(
const QgsRenderContext &renderContext,
int resolution )
342 if ( renderContext.
mapExtent() == mMapExtent && resolution == mFieldResolution )
344 mFieldResolution = resolution;
346 updateSize( renderContext );
349 bool QgsMeshStreamField::isValid()
const 354 void QgsMeshStreamField::addTrace(
QgsPointXY startPoint )
357 sp = mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint();
358 addTrace( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint() );
362 void QgsMeshStreamField::addRandomTraces()
364 while ( mPixelFillingCount < mMaxPixelFillingCount && !mRenderContext.renderingStopped() )
368 void QgsMeshStreamField::addRandomTrace()
373 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
374 int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
375 addTrace( QPoint( xRandom, yRandom ) );
378 void QgsMeshStreamField::addGriddedTraces(
int dx,
int dy )
381 while ( i < mFieldSize.width() && !mRenderContext.renderingStopped() )
384 while ( j < mFieldSize.height() && !mRenderContext.renderingStopped() )
386 addTrace( QPoint( i, j ) );
397 for (
auto f : qgis::as_const( facesInExtent ) )
400 for (
auto i : qgis::as_const( face ) )
401 vertices.insert( i );
404 for (
auto i : qgis::as_const( vertices ) )
406 addTrace( mesh.
vertices().at( i ) );
410 void QgsMeshStreamField::addTrace( QPoint startPixel )
416 if ( isTraceExists( startPixel ) || isTraceOutside( startPixel ) )
419 if ( !mVectorValueInterpolator )
422 mPainter->setPen( mPen );
428 std::list<QPair<QPoint, FieldData>> chunkTrace;
430 QPoint currentPixel = startPixel;
435 while ( !mRenderContext.renderingStopped() )
438 vector = mVectorValueInterpolator->vectorValue( mapPosition ) ;
440 if ( std::isnan( vector.
x() ) || std::isnan( vector.
y() ) )
442 mPixelFillingCount++;
443 setChunkTrace( chunkTrace );
444 drawChunkTrace( chunkTrace );
451 QgsVector vu = vector / mMaximumMagnitude * 2;
452 data.magnitude = vector.
length();
455 double Vu = data.magnitude / mMaximumMagnitude * 2;
460 addPixelToChunkTrace( currentPixel, data, chunkTrace );
461 simplifyChunkTrace( chunkTrace );
462 setChunkTrace( chunkTrace );
463 drawChunkTrace( chunkTrace );
471 if ( nextPosition.
x() > 1 )
473 if ( nextPosition.
x() < -1 )
475 if ( nextPosition.
y() > 1 )
477 if ( nextPosition.
y() < -1 )
482 if ( incX != 0 || incY != 0 )
484 data.directionX = incX;
485 data.directionY = -incY;
487 if ( chunkTrace.empty() )
489 storeInField( QPair<QPoint, FieldData>( currentPixel, data ) );
491 if ( addPixelToChunkTrace( currentPixel, data, chunkTrace ) )
493 setChunkTrace( chunkTrace );
494 drawChunkTrace( chunkTrace );
495 clearChunkTrace( chunkTrace );
499 currentPixel += QPoint( incX, -incY );
500 x1 = nextPosition.
x() - 2 * incX;
501 y1 = nextPosition.
y() - 2 * incY;
531 x2 = x1 + ( 1 - y1 ) * Vx / fabs( Vy ) ;
533 x2 = x1 + ( 1 + y1 ) * Vx / fabs( Vy ) ;
535 y2 = y1 + ( 1 - x1 ) * Vy / fabs( Vx ) ;
537 y2 = y1 + ( 1 + x1 ) * Vy / fabs( Vx ) ;
564 double dl = sqrt( dx * dx + dy * dy );
566 data.time += dl / Vu ;
567 if ( data.time > 10000 )
569 addPixelToChunkTrace( currentPixel, data, chunkTrace );
570 setChunkTrace( chunkTrace );
571 drawChunkTrace( chunkTrace );
579 if ( isTraceExists( currentPixel ) )
582 setChunkTrace( chunkTrace );
583 addPixelToChunkTrace( currentPixel, data, chunkTrace );
584 drawChunkTrace( chunkTrace );
588 if ( isTraceOutside( currentPixel ) )
590 setChunkTrace( chunkTrace );
591 drawChunkTrace( chunkTrace );
597 void QgsMeshStreamField::setResolution(
int width )
599 mFieldResolution = width;
602 QSize QgsMeshStreamField::imageSize()
const 604 return mFieldSize * mFieldResolution;
607 QPointF QgsMeshStreamField::fieldToDevice(
const QPoint &pixel )
const 610 p = mFieldResolution * p + QPointF( mFieldResolution - 1, mFieldResolution - 1 ) / 2;
614 bool QgsMeshStreamField::addPixelToChunkTrace( QPoint &pixel,
615 QgsMeshStreamField::FieldData &data,
616 std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
618 chunkTrace.emplace_back( pixel, data );
619 if ( chunkTrace.size() == 3 )
621 simplifyChunkTrace( chunkTrace );
627 void QgsMeshStreamlinesField::initField()
629 mField = QVector<bool>( mFieldSize.width() * mFieldSize.height(), false );
633 QgsMeshStreamlinesField::QgsMeshStreamlinesField(
const QgsTriangularMesh &triangularMesh,
638 bool dataIsOnVertices,
640 QgsMeshStreamField( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues, layerExtent, magMax, dataIsOnVertices, rendererContext )
643 QgsMeshStreamlinesField::QgsMeshStreamlinesField(
const QgsMeshStreamlinesField &other ):
644 QgsMeshStreamField( other ),
645 mField( other.mField )
648 QgsMeshStreamlinesField &QgsMeshStreamlinesField::operator=(
const QgsMeshStreamlinesField &other )
650 QgsMeshStreamField::operator=( other );
651 mField = other.mField;
655 void QgsMeshStreamlinesField::storeInField(
const QPair<QPoint, FieldData> pixelData )
657 int i = pixelData.first.x();
658 int j = pixelData.first.y();
659 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
661 mField[j * mFieldSize.width() + i] =
true;
665 void QgsMeshStreamField::setChunkTrace( std::list<QPair<QPoint, FieldData> > &chunkTrace )
667 auto p = chunkTrace.begin();
668 while ( p != chunkTrace.end() )
670 storeInField( ( *p ) );
671 mPixelFillingCount++;
676 void QgsMeshStreamlinesField::drawChunkTrace(
const std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
678 auto p1 = chunkTrace.begin();
681 while ( p2 != chunkTrace.end() )
683 if ( filterMag( ( *p1 ).second.magnitude ) && filterMag( ( *p2 ).second.magnitude ) )
684 mPainter->drawLine( fieldToDevice( ( *p1 ).first ), fieldToDevice( ( *p2 ).first ) );
690 void QgsMeshStreamField::clearChunkTrace( std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
692 auto one_before_end = std::prev( chunkTrace.end() );
693 chunkTrace.erase( chunkTrace.begin(), one_before_end );
696 void QgsMeshStreamField::simplifyChunkTrace( std::list<QPair<QPoint, FieldData> > &shunkTrace )
698 if ( shunkTrace.size() != 3 )
701 auto ip3 = shunkTrace.begin();
705 while ( ip3 != shunkTrace.end() && ip2 != shunkTrace.end() )
707 QPoint v1 = ( *ip1 ).first - ( *ip2 ).first;
708 QPoint v2 = ( *ip2 ).first - ( *ip3 ).first;
709 if ( v1.x()*v2.x() + v1.y()*v2.y() == 0 )
711 ( *ip1 ).second.time += ( ( *ip2 ).second.time ) / 2;
712 ( *ip3 ).second.time += ( ( *ip2 ).second.time ) / 2;
713 ( *ip1 ).second.directionX += ( *ip2 ).second.directionX;
714 ( *ip1 ).second.directionY += ( *ip2 ).second.directionY;
715 shunkTrace.erase( ip2 );
722 bool QgsMeshStreamlinesField::isTraceExists(
const QPoint &pixel )
const 726 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
728 return mField[j * mFieldSize.width() + i];
734 bool QgsMeshStreamField::isTraceOutside(
const QPoint &pixel )
const 738 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
745 void QgsMeshStreamField::setMinimizeFieldSize(
bool minimizeFieldSize )
747 mMinimizeFieldSize = minimizeFieldSize;
750 QgsMeshStreamField &QgsMeshStreamField::operator=(
const QgsMeshStreamField &other )
752 mFieldSize = other.mFieldSize ;
753 mFieldResolution = other.mFieldResolution;
755 mTraceImage = other.mTraceImage ;
756 mMapToFieldPixel = other.mMapToFieldPixel ;
757 mPixelFillingCount = other.mPixelFillingCount ;
758 mMaxPixelFillingCount = other.mMaxPixelFillingCount ;
759 mLayerExtent = other.mLayerExtent ;
760 mMapExtent = other.mMapExtent;
761 mFieldTopLeftInDeviceCoordinates = other.mFieldTopLeftInDeviceCoordinates ;
762 mValid = other.mValid ;
763 mMaximumMagnitude = other.mMaximumMagnitude ;
764 mPixelFillingDensity = other.mPixelFillingDensity ;
765 mMinMagFilter = other.mMinMagFilter ;
766 mMaxMagFilter = other.mMaxMagFilter ;
767 mMinimizeFieldSize = other.mMinimizeFieldSize ;
768 mVectorValueInterpolator =
769 std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
771 mPainter.reset(
new QPainter( &mTraceImage ) );
776 void QgsMeshStreamField::initImage()
779 mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
780 mTraceImage.fill( 0X00000000 );
782 mPainter.reset(
new QPainter( &mTraceImage ) );
783 mPainter->setRenderHint( QPainter::Antialiasing,
true );
784 mPainter->setPen( mPen );
787 bool QgsMeshStreamField::filterMag(
double value )
const 789 return ( mMinMagFilter < 0 || value > mMinMagFilter ) && ( mMaxMagFilter < 0 || value < mMaxMagFilter );
792 QImage QgsMeshStreamField::image()
794 return mTraceImage.scaled( mFieldSize * mFieldResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
797 void QgsMeshStreamField::setPixelFillingDensity(
double maxFilling )
799 mPixelFillingDensity = maxFilling;
800 mMaxPixelFillingCount = int( mPixelFillingDensity * mFieldSize.width() * mFieldSize.height() );
803 void QgsMeshStreamField::setColor( QColor color )
805 mPen.setColor( color );
808 void QgsMeshStreamField::setLineWidth(
double width )
810 mPen.setWidthF( width );
813 void QgsMeshStreamField::setFilter(
double min,
double max )
819 QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace(
const QgsTriangularMesh &triangularMesh,
const QgsMeshDataBlock &datasetVectorValues ):
820 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
824 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
827 QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace(
const QgsMeshVectorValueInterpolatorFromFace &other ):
828 QgsMeshVectorValueInterpolator( other )
831 QgsMeshVectorValueInterpolatorFromFace *QgsMeshVectorValueInterpolatorFromFace::clone()
833 return new QgsMeshVectorValueInterpolatorFromFace( *
this );
836 QgsMeshVectorValueInterpolatorFromFace &QgsMeshVectorValueInterpolatorFromFace::operator=(
const QgsMeshVectorValueInterpolatorFromFace &other )
838 QgsMeshVectorValueInterpolator::operator=( other );
842 QgsVector QgsMeshVectorValueInterpolatorFromFace::interpolatedValuePrivate(
int faceIndex,
const QgsPointXY point )
const 844 QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
850 QgsVector vect =
QgsVector( mDatasetValues.value( mTriangularMesh.trianglesToNativeFaces().at( faceIndex ) ).x(),
851 mDatasetValues.value( mTriangularMesh.trianglesToNativeFaces().at( faceIndex ) ).y() );
853 return QgsMeshLayerUtils::interpolateVectorFromFacesData(
862 mRendererContext( rendererContext )
864 mStreamlineField.reset(
new QgsMeshStreamlinesField( triangularMesh,
866 scalarActiveFaceFlagValues,
868 magMax, dataIsOnVertices, rendererContext ) );
870 mStreamlineField->updateSize( rendererContext );
873 QgsUnitTypes::RenderUnit::RenderMillimeters ) ) ;
874 mStreamlineField->setColor( settings.
color() );
884 mStreamlineField->addTracesOnMesh( triangularMesh, rendererContext.
mapExtent() );
887 mStreamlineField->addRandomTraces();
892 void QgsMeshVectorStreamlineRenderer::draw()
894 if ( mRendererContext.renderingStopped() )
896 mRendererContext.painter()->drawImage( mStreamlineField->topLeft(), mStreamlineField->image() );
899 QgsMeshParticleTracesField::QgsMeshParticleTracesField(
const QgsTriangularMesh &triangularMesh,
904 bool dataIsOnVertices,
906 QgsMeshStreamField( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues, layerExtent, magMax, dataIsOnVertices, rendererContext )
908 std::srand( uint( ::time(
nullptr ) ) );
909 mPen.setCapStyle( Qt::RoundCap );
912 QgsMeshParticleTracesField::QgsMeshParticleTracesField(
const QgsMeshParticleTracesField &other ):
913 QgsMeshStreamField( other ),
914 mTimeField( other.mTimeField ),
915 mDirectionField( other.mDirectionField ),
916 mParticles( other.mParticles ),
917 mStumpImage( other.mStumpImage ),
918 mTimeStep( other.mTimeStep ),
919 mParticlesLifeTime( other.mParticlesLifeTime ),
920 mParticlesCount( other.mParticlesCount ),
921 mTailFactor( other.mTailFactor ),
922 mParticleColor( other.mParticleColor ),
923 mParticleSize( other.mParticleSize ),
924 mStumpFactor( other.mStumpFactor )
927 void QgsMeshParticleTracesField::addParticle(
const QPoint &startPoint,
double lifeTime )
929 addTrace( startPoint );
930 if ( time( startPoint ) > 0 )
932 QgsMeshTraceParticle p;
933 p.lifeTime = lifeTime;
934 p.position = startPoint;
935 mParticles.append( p );
940 void QgsMeshParticleTracesField::addParticleXY(
const QgsPointXY &startPoint,
double lifeTime )
942 addParticle( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint(), lifeTime );
945 void QgsMeshParticleTracesField::moveParticles()
948 for (
auto &p : mParticles )
950 double spentTime = p.remainingTime;
951 size_t countAdded = 0;
952 while ( spentTime < mTimeStep && p.lifeTime > 0 )
954 double timeToSpend = double( time( p.position ) );
955 if ( timeToSpend > 0 )
957 p.lifeTime -= timeToSpend;
958 spentTime += timeToSpend;
959 QPoint dir = direction( p.position );
960 if ( p.lifeTime > 0 )
963 p.tail.emplace_back( p.position );
978 if ( p.lifeTime <= 0 )
986 p.remainingTime = spentTime - mTimeStep;
987 while (
int( p.tail.size() ) > mMinTailLength && p.tail.size() > countAdded * mTailFactor )
988 p.tail.erase( p.tail.begin() );
989 drawParticleTrace( p );
995 while ( i < mParticles.count() )
997 if ( mParticles.at( i ).tail.size() == 0 )
998 mParticles.removeAt( i );
1004 if ( mParticles.count() < mParticlesCount )
1005 addRandomParticles();
1008 void QgsMeshParticleTracesField::addRandomParticles()
1013 if ( mParticlesCount < 0 )
1015 addParticleXY(
QgsPointXY( mMapToFieldPixel.xCenter(), mMapToFieldPixel.yCenter() ), mParticlesLifeTime );
1019 int count = mParticlesCount - mParticles.count();
1021 for (
int i = 0; i < count; ++i )
1023 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
1024 int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
1025 double lifeTime = ( std::rand() / ( ( RAND_MAX + 1u ) / mParticlesLifeTime ) );
1026 addParticle( QPoint( xRandom, yRandom ), lifeTime );
1030 void QgsMeshParticleTracesField::storeInField(
const QPair<QPoint, QgsMeshStreamField::FieldData> pixelData )
1032 int i = pixelData.first.x();
1033 int j = pixelData.first.y();
1034 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1036 mTimeField[j * mFieldSize.width() + i] = pixelData.second.time;
1037 int d = pixelData.second.directionX + 2 + ( pixelData.second.directionY + 1 ) * 3;
1038 mDirectionField[j * mFieldSize.width() + i] =
static_cast<char>( d );
1042 void QgsMeshParticleTracesField::initField()
1044 mTimeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), -1 );
1045 mDirectionField = QVector<char>( mFieldSize.width() * mFieldSize.height(),
static_cast<char>( int( 0 ) ) );
1047 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1048 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1051 bool QgsMeshParticleTracesField::isTraceExists(
const QPoint &pixel )
const 1055 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1057 return mTimeField[j * mFieldSize.width() + i] >= 0;
1063 void QgsMeshParticleTracesField::setStumpParticleWithLifeTime(
bool stumpParticleWithLifeTime )
1065 mStumpParticleWithLifeTime = stumpParticleWithLifeTime;
1068 void QgsMeshParticleTracesField::setMinTailLength(
int minTailLength )
1070 mMinTailLength = minTailLength;
1073 QgsMeshParticleTracesField &QgsMeshParticleTracesField::operator=(
const QgsMeshParticleTracesField &other )
1075 QgsMeshStreamField::operator=( other );
1076 mTimeField = other.mTimeField;
1077 mDirectionField = other.mDirectionField;
1078 mParticles = other.mParticles;
1079 mStumpImage = other.mStumpImage;
1080 mTimeStep = other.mTimeStep;
1081 mParticlesLifeTime = other.mParticlesLifeTime;
1082 mParticlesCount = other.mParticlesCount;
1083 mTailFactor = other.mTailFactor;
1084 mParticleColor = other.mParticleColor;
1085 mParticleSize = other.mParticleSize;
1086 mStumpFactor = other.mStumpFactor;
1091 void QgsMeshParticleTracesField::setTailFactor(
double tailFactor )
1093 mTailFactor = tailFactor;
1096 void QgsMeshParticleTracesField::setParticleSize(
double particleSize )
1098 mParticleSize = particleSize;
1101 void QgsMeshParticleTracesField::setParticleColor(
const QColor &particleColor )
1103 mParticleColor = particleColor;
1106 void QgsMeshParticleTracesField::setTimeStep(
double timeStep )
1108 mTimeStep = timeStep;
1111 void QgsMeshParticleTracesField::setParticlesLifeTime(
double particlesLifeTime )
1113 mParticlesLifeTime = particlesLifeTime;
1116 QImage QgsMeshParticleTracesField::imageRendered()
const 1121 void QgsMeshParticleTracesField::stump()
1124 mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1125 mPainter->drawImage( QPoint( 0, 0 ), mStumpImage );
1126 mPainter->restore();
1129 void QgsMeshParticleTracesField::setStumpFactor(
int sf )
1132 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1133 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1136 QPoint QgsMeshParticleTracesField::direction( QPoint position )
const 1138 int i = position.x();
1139 int j = position.y();
1140 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1142 int dir =
static_cast<int>( mDirectionField[j * mFieldSize.width() + i] );
1143 if ( dir != 0 && dir < 10 )
1144 return QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
1146 return QPoint( 0, 0 );
1149 float QgsMeshParticleTracesField::time( QPoint position )
const 1151 int i = position.x();
1152 int j = position.y();
1153 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1155 return mTimeField[j * mFieldSize.width() + i];
1160 void QgsMeshParticleTracesField::drawParticleTrace(
const QgsMeshTraceParticle &particle )
1162 const std::list<QPoint> &tail = particle.tail;
1163 if ( tail.size() == 0 )
1165 double iniWidth = mParticleSize;
1166 double finWidth = 0;
1168 size_t pixelCount = tail.size();
1170 if ( mStumpParticleWithLifeTime )
1172 QColor traceColor = mParticleColor;
1173 double transparency = sin( M_PI * particle.lifeTime / mParticlesLifeTime );
1174 traceColor.setAlphaF( transparency );
1175 mPen.setColor( traceColor );
1178 mPen.setColor( mParticleColor );
1181 if ( pixelCount > 1 )
1182 dw = ( iniWidth - finWidth ) / ( pixelCount );
1186 auto ip1 = std::prev( tail.end() );
1187 auto ip2 = std::prev( ip1 );
1189 while ( ip1 != tail.begin() )
1191 QPointF p1 = fieldToDevice( ( *ip1 ) );
1192 QPointF p2 = fieldToDevice( ( *ip2 ) );
1193 mPen.setWidthF( iniWidth - i * dw );
1194 mPainter->setPen( mPen );
1195 mPainter->drawLine( p1, p2 );
1202 void QgsMeshParticleTracesField::setParticlesCount(
int particlesCount )
1204 mParticlesCount = particlesCount;
1211 bool dataIsOnVertices,
1214 mRendererContext( rendererContext )
1216 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
new QgsMeshParticleTracesField( triangularMesh,
1217 dataSetVectorValues,
1218 scalarActiveFaceFlagValues,
1222 rendererContext ) ) ;
1223 mParticleField->updateSize( rendererContext ) ;
1227 mRendererContext( rendererContext )
1234 bool vectorDataOnVertices;
1242 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
1243 ( cache->mActiveVectorDatasetIndex == datasetIndex ) )
1245 vectorDatasetValues = cache->mVectorDatasetValues;
1246 scalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
1247 magMax = cache->mVectorDatasetMagMaximum;
1248 vectorDataOnVertices = cache->mVectorDataOnVertices;
1258 if ( vectorDataOnVertices )
1274 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
new QgsMeshParticleTracesField( ( *layer->
triangularMesh() ),
1275 vectorDatasetValues,
1276 scalarActiveFaceFlagValues,
1279 vectorDataOnVertices,
1280 rendererContext ) ) ;
1282 mParticleField->setMinimizeFieldSize(
false );
1283 mParticleField->updateSize( mRendererContext );
1287 mRendererContext( other.mRendererContext ),
1289 mVpixMax( other.mVpixMax ),
1290 mParticleLifeTime( other.mParticleLifeTime )
1292 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
1293 new QgsMeshParticleTracesField( *other.mParticleField ) );
1299 mParticleField->setParticlesCount( count );
1300 mParticleField->addRandomParticles();
1305 mParticleField->moveParticles();
1306 return mParticleField->image();
1316 updateFieldParameter();
1322 updateFieldParameter();
1327 mParticleLifeTime = particleLifeTime;
1328 updateFieldParameter();
1333 mParticleField->setParticleColor( c );
1338 mParticleField->setParticleSize( width );
1343 mParticleField->setTailFactor( fct );
1348 mParticleField->setMinTailLength( l );
1357 mParticleField->setStumpFactor(
int( 255 * p ) );
1362 mParticleField.reset(
new QgsMeshParticleTracesField( *mParticleField ) );
1363 const_cast<QgsRenderContext &
>( mRendererContext ) = other.mRendererContext;
1365 mVpixMax = other.mVpixMax;
1366 mParticleLifeTime = other.mParticleLifeTime;
1371 void QgsMeshVectorTraceAnimationGenerator::updateFieldParameter()
1373 double fieldTimeStep = mVpixMax / mFPS;
1374 double fieldLifeTime = mParticleLifeTime * mFPS * fieldTimeStep;
1375 mParticleField->setTimeStep( fieldTimeStep );
1376 mParticleField->setParticlesLifeTime( fieldLifeTime );
1380 mRendererContext( rendererContext )
1382 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
new QgsMeshParticleTracesField( triangularMesh,
1383 dataSetVectorValues,
1384 scalarActiveFaceFlagValues,
1388 rendererContext ) ) ;
1389 mParticleField->updateSize( rendererContext ) ;
1391 mParticleField->setParticleColor( settings.
color() );
1393 settings.
lineWidth(), QgsUnitTypes::RenderUnit::RenderMillimeters ) );
1395 mParticleField->setTailFactor( 1 );
1396 mParticleField->setStumpParticleWithLifeTime(
false );
1399 mParticleField->addRandomParticles();
1400 mParticleField->moveParticles();
1403 void QgsMeshVectorTraceRenderer::draw()
1405 if ( mRendererContext.renderingStopped() )
1407 mRendererContext.painter()->drawImage( mParticleField->topLeft(), mParticleField->image() );
QImage imageRendered()
Moves all the particles using frame per second (fps) to calculate the displacement and return the ren...
void setFPS(int FPS)
Sets the number of frames per seconds that will be rendered.
A rectangle specified with double values.
Triangular/Derived Mesh is mesh with vertices in map coordinates.
void seedRandomParticles(int count)
seeds particles in the vector fields
QgsMeshRendererVectorTracesSettings tracesSettings() const
Returns settings for vector rendered with traces.
A class to represent a 2D point.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e...
void setMaxSpeedPixel(int max)
Sets the max number of pixels that can be go through by the particles in 1 second.
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
int particlesCount() const
Returns particles count.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering)
Seeds start points on the vertices mesh or user regular grid.
SeedingStartPointsMethod seedingMethod() const
Returns the method used for seeding start points of strealines.
QVector< QgsMeshVertex > vertices
vertices
double filterMin() const
Returns filter value for vector magnitudes.
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
Perform transforms between map coordinates and device coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transform the point from map (world) coordinates to device coordinates.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
A wrapper for QgsMeshParticuleTracesField used to render the particles.
QgsMeshDatasetIndex activeVectorDataset() const
Returns active vector dataset.
double maximumTailLength() const
Returns the maximum tail length.
QgsRectangle extent() const override
Returns the extent of the layer.
QPointF toQPointF() const
Converts a point to a QPointF.
Represents a streamline renderer settings for vector datasets.
Seeds start points randomly on the mesh.
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
double width() const
Returns the width of the rectangle.
QColor color() const
Returns color used for drawing arrows.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
bool isInTriangleFace(const QgsPointXY point, const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Tests if point p is on the face defined with vertices.
QgsMeshVectorTraceAnimationGenerator(const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &dataSetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues, bool dataIsOnVertices, const QgsRenderContext &rendererContext, const QgsRectangle &layerExtent, double magMax)
Constructor to use from QgsMeshVectorRenderer.
void setParticlesSize(double width)
Sets particle size in px.
double filterMax() const
Returns filter value for vector magnitudes.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
virtual QgsMeshDataBlock datasetValues(QgsMeshDatasetIndex index, int valueIndex, int count) const =0
Returns N vector/scalar values from the index from the dataset.
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
Point geometry type, with support for z-dimension and m-values.
void setParticlesColor(const QColor &c)
Sets colors of particle.
double length() const
Returns the length of the vector.
A class to represent a vector.
void reload() override
Synchronises with changes in the datasource.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
void setTailPersitence(double p)
Sets the visual persistence of the tail.
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
void setMinimumTailLength(int l)
Sets the minimum tail length.
QgsMeshVectorTraceAnimationGenerator & operator=(const QgsMeshVectorTraceAnimationGenerator &other)
Assignment operator.
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
int userGridCellWidth() const
Returns width in pixels of user grid cell.
QgsUnitTypes::RenderUnit maximumTailLengthUnit() const
Returns the maximum tail length unit.
bool isValid() const
Whether the block is valid.
bool isOnUserDefinedGrid() const
Returns whether vectors are drawn on user-defined grid.
QVector< int > QgsMeshFace
List of vertex indexes.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
void setTailFactor(double fct)
Sets the tail factor, used to adjust the length of the tail. 0 : minimum length, >1 increase the tail...
QgsMeshRendererVectorStreamlineSettings streamLinesSettings() const
Returns settings for vector rendered with streamlines.
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering)
int userGridCellHeight() const
Returns height in pixels of user grid cell.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double lineWidth() const
Returns line width of the arrow (in millimeters)
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QVector< QgsMeshFace > faces
faces
double x() const
Returns the vector's x-component.
virtual int datasetGroupCount() const =0
Returns number of datasets groups loaded.
Custom exception class for Coordinate Reference System related exceptions.
void setParticlesLifeTime(double particleLifeTime)
Sets maximum life time of particles in seconds.
double seedingDensity() const
Returns the density used for seeding start points.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry, including all geometry parts and rings.
double y() const
Returns the vector's y-component.
QgsTriangularMesh * triangularMesh()
Returns triangular mesh (nullptr before rendering)
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
virtual QgsMeshDataBlock areFacesActive(QgsMeshDatasetIndex index, int faceIndex, int count) const =0
Returns whether the faces are active for particular dataset.
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.