32#define M_DEG2RAD 0.0174532925
43 const double xMin = std::min( { topLeft.
x(), topRight.
x(), bottomLeft.
x(), bottomRight.
x() } );
44 const double xMax = std::max( { topLeft.
x(), topRight.
x(), bottomLeft.
x(), bottomRight.
x() } );
45 const double yMin = std::min( { topLeft.
y(), topRight.
y(), bottomLeft.
y(), bottomRight.
y() } );
46 const double yMax = std::max( { topLeft.
y(), topRight.
y(), bottomLeft.
y(), bottomRight.
y() } );
51QSize QgsVectorFieldStreamField::size()
const
56QPoint QgsVectorFieldStreamField::topLeft()
const
58 return mFieldTopLeftInDeviceCoordinates;
61int QgsVectorFieldStreamField::resolution()
const
63 return mFieldResolution;
66QgsPointXY QgsVectorFieldStreamField::positionToMapCoordinates(
const QPoint &pixelPosition,
const QgsPointXY &positionInPixel )
68 QgsPointXY mapPoint = mMapToFieldPixel.toMapCoordinates( pixelPosition );
69 mapPoint = mapPoint +
QgsVector( positionInPixel.
x() * mMapToFieldPixel.mapUnitsPerPixel(), positionInPixel.
y() * mMapToFieldPixel.mapUnitsPerPixel() );
73QgsVectorFieldStreamField::QgsVectorFieldStreamField( std::unique_ptr<QgsVectorFieldValueSource> source,
const QgsRenderContext &rendererContext,
const QgsInterpolatedLineColor &vectorColoring,
int resolution )
74 : mFieldResolution( resolution )
75 , mVectorColoring( vectorColoring )
76 , mRenderContext( rendererContext )
77 , mSource( std::move( source ) )
80QgsVectorFieldStreamField::QgsVectorFieldStreamField(
const QgsVectorFieldStreamField &other )
81 : mFieldSize( other.mFieldSize )
82 , mFieldResolution( other.mFieldResolution )
84 , mTraceImage( other.mTraceImage )
85 , mMapToFieldPixel( other.mMapToFieldPixel )
86 , mOutputExtent( other.mOutputExtent )
87 , mVectorColoring( other.mVectorColoring )
88 , mDirectionField( other.mDirectionField )
89 , mRenderContext( other.mRenderContext )
90 , mPixelFillingCount( other.mPixelFillingCount )
91 , mMaxPixelFillingCount( other.mMaxPixelFillingCount )
92 , mMapExtent( other.mMapExtent )
93 , mFieldTopLeftInDeviceCoordinates( other.mFieldTopLeftInDeviceCoordinates )
94 , mValid( other.mValid )
95 , mPixelFillingDensity( other.mPixelFillingDensity )
96 , mMinMagFilter( other.mMinMagFilter )
97 , mMaxMagFilter( other.mMaxMagFilter )
98 , mMinimizeFieldSize( other.mMinimizeFieldSize )
100 mPainter = std::make_unique<QPainter>( &mTraceImage );
101 mSource = other.mSource ? std::unique_ptr<QgsVectorFieldValueSource>( other.mSource->clone() ) : nullptr;
104QgsVectorFieldStreamField::~QgsVectorFieldStreamField()
110void QgsVectorFieldStreamField::updateSize(
const QgsRenderContext &renderContext )
125 layerExtent = mMapExtent;
129 if ( mMinimizeFieldSize )
130 interestZoneExtent = layerExtent.
intersect( mMapExtent );
132 interestZoneExtent = mMapExtent;
137 mFieldSize = QSize();
138 mFieldTopLeftInDeviceCoordinates = QPoint();
143 QgsRectangle fieldInterestZoneInDeviceCoordinates = ::boundingBoxToScreenRectangle( deviceMapToPixel, interestZoneExtent );
144 mFieldTopLeftInDeviceCoordinates
145 = QPoint(
static_cast<int>( std::round( fieldInterestZoneInDeviceCoordinates.
xMinimum() ) ),
static_cast<int>( std::round( fieldInterestZoneInDeviceCoordinates.
yMinimum() ) ) );
146 int fieldWidthInDeviceCoordinate = int( fieldInterestZoneInDeviceCoordinates.
width() );
147 int fieldHeightInDeviceCoordinate = int( fieldInterestZoneInDeviceCoordinates.
height() );
149 int fieldWidth = int( fieldWidthInDeviceCoordinate / mFieldResolution );
150 int fieldHeight = int( fieldHeightInDeviceCoordinate / mFieldResolution );
153 if ( fieldWidthInDeviceCoordinate % mFieldResolution > 0 )
155 if ( fieldHeightInDeviceCoordinate % mFieldResolution > 0 )
158 if ( fieldWidth == 0 || fieldHeight == 0 )
160 mFieldSize = QSize();
165 mFieldSize.setWidth( fieldWidth );
166 mFieldSize.setHeight( fieldHeight );
173 std::min( { pt1.
x(), pt2.
x(), pt3.
x(), pt4.
x() } ),
174 std::min( { pt1.
y(), pt2.
y(), pt3.
y(), pt4.
y() } ),
175 std::max( { pt1.
x(), pt2.
x(), pt3.
x(), pt4.
x() } ),
176 std::max( { pt1.
y(), pt2.
y(), pt3.
y(), pt4.
y() } ),
181 double mapUnitPerFieldPixel;
182 if ( interestZoneExtent.
width() > 0 )
183 mapUnitPerFieldPixel = deviceMapToPixel.
mapUnitsPerPixel() * mFieldResolution * mFieldSize.width() / ( fieldWidthInDeviceCoordinate /
static_cast<double>( mFieldResolution ) );
185 mapUnitPerFieldPixel = 1e-8;
187 int fieldRightDevice = mFieldTopLeftInDeviceCoordinates.x() + mFieldSize.width() * mFieldResolution;
188 int fieldBottomDevice = mFieldTopLeftInDeviceCoordinates.y() + mFieldSize.height() * mFieldResolution;
191 int fieldTopDevice = mFieldTopLeftInDeviceCoordinates.
x();
192 int fieldLeftDevice = mFieldTopLeftInDeviceCoordinates.y();
195 double xc = ( fieldRightBottomMap.
x() + fieldTopLeftMap.
x() ) / 2;
196 double yc = ( fieldTopLeftMap.
y() + fieldRightBottomMap.
y() ) / 2;
198 mMapToFieldPixel =
QgsMapToPixel( mapUnitPerFieldPixel, xc, yc, fieldWidth, fieldHeight, deviceMapToPixel.
mapRotation() );
204void QgsVectorFieldStreamField::updateSize(
const QgsRenderContext &renderContext,
int resolution )
206 if ( renderContext.
mapExtent() == mMapExtent && resolution == mFieldResolution )
208 mFieldResolution = resolution;
210 updateSize( renderContext );
213bool QgsVectorFieldStreamField::isValid()
const
218void QgsVectorFieldStreamField::addTrace(
QgsPointXY startPoint )
220 addTrace( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint() );
224void QgsVectorFieldStreamField::addRandomTraces()
226 if ( mSource && mSource->maximumMagnitude() > 0 )
227 while ( ( mPixelFillingCount < mMaxPixelFillingCount ) && ( !mRenderContext.feedback() || !mRenderContext.feedback()->isCanceled() || !mRenderContext.renderingStopped() ) )
231void QgsVectorFieldStreamField::addRandomTrace()
236 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) );
237 int yRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) );
238 addTrace( QPoint( xRandom, yRandom ) );
241void QgsVectorFieldStreamField::addGriddedTraces(
int dx,
int dy )
244 while ( i < mFieldSize.width() && mRenderContext.feedback() && !mRenderContext.feedback()->isCanceled() )
247 while ( j < mFieldSize.height() && mRenderContext.feedback() && !mRenderContext.feedback()->isCanceled() )
249 addTrace( QPoint( i, j ) );
256void QgsVectorFieldStreamField::addTracesOnDataPoints(
const QgsRectangle &extent )
261 const QVector<QgsPointXY> points = mSource->seedPoints( extent );
266void QgsVectorFieldStreamField::addTrace( QPoint startPixel )
272 if ( isTraceExists( startPixel ) || isTraceOutside( startPixel ) )
278 const double maximumMagnitude = mSource->maximumMagnitude();
279 if ( !( maximumMagnitude > 0 ) )
282 mPainter->setPen( mPen );
288 std::list<QPair<QPoint, FieldData>> chunkTrace;
290 QPoint currentPixel = startPixel;
298 vector = mSource->vectorValue( mapPosition );
300 if ( std::isnan( vector.
x() ) || std::isnan( vector.
y() ) )
302 mPixelFillingCount++;
303 setChunkTrace( chunkTrace );
311 QgsVector vu = vector / maximumMagnitude * 2;
312 data.magnitude = vector.
length();
316 double Vu = data.magnitude / maximumMagnitude * 2;
321 addPixelToChunkTrace( currentPixel, data, chunkTrace );
322 simplifyChunkTrace( chunkTrace );
323 setChunkTrace( chunkTrace );
331 if ( nextPosition.
x() > 1 )
333 if ( nextPosition.
x() < -1 )
335 if ( nextPosition.
y() > 1 )
337 if ( nextPosition.
y() < -1 )
340 if ( incX != 0 || incY != 0 )
342 data.directionX = incX;
343 data.directionY = -incY;
345 if ( chunkTrace.empty() )
347 storeInField( QPair<QPoint, FieldData>( currentPixel, data ) );
349 if ( addPixelToChunkTrace( currentPixel, data, chunkTrace ) )
351 setChunkTrace( chunkTrace );
352 clearChunkTrace( chunkTrace );
356 currentPixel += QPoint( incX, -incY );
357 x1 = nextPosition.
x() - 2 * incX;
358 y1 = nextPosition.
y() - 2 * incY;
389 x2 = x1 + ( 1 - y1 ) * Vx / fabs( Vy );
391 x2 = x1 + ( 1 + y1 ) * Vx / fabs( Vy );
393 y2 = y1 + ( 1 - x1 ) * Vy / fabs( Vx );
395 y2 = y1 + ( 1 + x1 ) * Vy / fabs( Vx );
413 double dl = sqrt( dx * dx + dy * dy );
415 data.time +=
static_cast<float>( dl / Vu );
416 if ( data.time > 10000 )
418 addPixelToChunkTrace( currentPixel, data, chunkTrace );
419 setChunkTrace( chunkTrace );
427 if ( isTraceExists( currentPixel ) )
430 setChunkTrace( chunkTrace );
431 addPixelToChunkTrace( currentPixel, data, chunkTrace );
435 if ( isTraceOutside( currentPixel ) )
437 setChunkTrace( chunkTrace );
441 if ( mRenderContext.feedback() && mRenderContext.feedback()->isCanceled() )
444 if ( mRenderContext.renderingStopped() )
448 drawTrace( startPixel );
451void QgsVectorFieldStreamField::setResolution(
int width )
453 mFieldResolution = width;
456QSize QgsVectorFieldStreamField::imageSize()
const
458 return mFieldSize * mFieldResolution;
461QPointF QgsVectorFieldStreamField::fieldToDevice(
const QPoint &pixel )
const
464 p = mFieldResolution * p + QPointF( mFieldResolution - 1, mFieldResolution - 1 ) / 2;
468bool QgsVectorFieldStreamField::addPixelToChunkTrace( QPoint &pixel, QgsVectorFieldStreamField::FieldData &data, std::list<QPair<QPoint, QgsVectorFieldStreamField::FieldData> > &chunkTrace )
470 chunkTrace.emplace_back( pixel, data );
471 if ( chunkTrace.size() == 3 )
473 simplifyChunkTrace( chunkTrace );
479void QgsVectorFieldStreamlinesField::initField()
481 mField = QVector<bool>( mFieldSize.width() * mFieldSize.height(),
false );
482 mDirectionField = QVector<unsigned char>( mFieldSize.width() * mFieldSize.height(),
static_cast<unsigned char>(
int( 0 ) ) );
486void QgsVectorFieldStreamlinesField::initImage()
488 mTraceImage = QImage();
489 switch ( mVectorColoring.coloringMethod() )
493 QSize imgSize = mFieldSize * mFieldResolution;
498 std::unique_ptr<QgsRasterInterface> magnitudeSource = mSource ? mSource->magnitudeSource( fieldContext, imgSize ) :
nullptr;
500 if ( magnitudeSource && imgSize.isValid() )
505 std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, mOutputExtent, imgSize.width(), imgSize.height(), mFeedBack ) );
506 mTraceImage = bl->image();
511 mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32_Premultiplied );
512 if ( !mTraceImage.isNull() )
513 mTraceImage.fill( mVectorColoring.singleColor() );
519 mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32_Premultiplied );
520 QColor col = mVectorColoring.singleColor();
521 mTraceImage.fill( col );
526 if ( !mTraceImage.isNull() )
528 mPainter = std::make_unique<QPainter>( &mTraceImage );
529 mPainter->setRenderHint( QPainter::Antialiasing,
true );
531 mDrawingTraceImage = QImage( mTraceImage.size(), QImage::Format_ARGB32_Premultiplied );
532 mDrawingTraceImage.fill( Qt::transparent );
533 mDrawingTracePainter = std::make_unique<QPainter>( &mDrawingTraceImage );
534 mDrawingTracePainter->setRenderHint( QPainter::Antialiasing,
true );
538void QgsVectorFieldStreamField::clearChunkTrace( std::list<QPair<QPoint, QgsVectorFieldStreamField::FieldData> > &chunkTrace )
540 auto one_before_end = std::prev( chunkTrace.end() );
541 chunkTrace.erase( chunkTrace.begin(), one_before_end );
544void QgsVectorFieldStreamField::simplifyChunkTrace( std::list<QPair<QPoint, FieldData> > &chunkTrace )
546 if ( chunkTrace.size() != 3 )
549 auto ip3 = chunkTrace.begin();
553 while ( ip3 != chunkTrace.end() && ip2 != chunkTrace.end() )
555 QPoint v1 = ( *ip1 ).first - ( *ip2 ).first;
556 QPoint v2 = ( *ip2 ).first - ( *ip3 ).first;
557 if ( v1.x() * v2.x() + v1.y() * v2.y() == 0 )
559 ( *ip1 ).second.time += ( ( *ip2 ).second.time ) / 2;
560 ( *ip3 ).second.time += ( ( *ip2 ).second.time ) / 2;
561 ( *ip1 ).second.directionX += ( *ip2 ).second.directionX;
562 ( *ip1 ).second.directionY += ( *ip2 ).second.directionY;
563 chunkTrace.erase( ip2 );
570QgsVectorFieldStreamlinesField::QgsVectorFieldStreamlinesField(
573 : QgsVectorFieldStreamField( std::move( source ), rendererContext, vectorColoring )
574 , mFeedBack( feedBack )
577void QgsVectorFieldStreamlinesField::compose()
581 mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
582 mPainter->drawImage( 0, 0, mDrawingTraceImage );
585void QgsVectorFieldStreamlinesField::storeInField(
const QPair<QPoint, FieldData> pixelData )
587 int i = pixelData.first.x();
588 int j = pixelData.first.y();
589 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
591 mField[j * mFieldSize.width() + i] =
true;
592 int d = pixelData.second.directionX + 2 + ( pixelData.second.directionY + 1 ) * 3;
593 mDirectionField[j * mFieldSize.width() + i] =
static_cast<unsigned char>( d );
597void QgsVectorFieldStreamField::setChunkTrace( std::list<QPair<QPoint, FieldData> > &chunkTrace )
599 auto p = chunkTrace.begin();
600 while ( p != chunkTrace.end() )
602 storeInField( ( *p ) );
603 mPixelFillingCount++;
608void QgsVectorFieldStreamlinesField::drawTrace(
const QPoint &start )
const
610 if ( !isTraceExists( start ) || isTraceOutside( start ) )
613 if ( !mDrawingTracePainter )
618 int fieldWidth = mFieldSize.width();
619 QSet<QgsPointXY> path;
620 unsigned char dir = 0;
621 unsigned char prevDir = mDirectionField.at( pt1.y() * fieldWidth + pt1.x() );
623 QVector<double> xPoly;
624 QVector<double> yPoly;
625 QPointF devicePt = fieldToDevice( pt1 );
626 xPoly.append( devicePt.x() );
627 yPoly.append( devicePt.y() );
629 while ( isTraceExists( curPt ) && !isTraceOutside( curPt ) && !path.contains( curPt ) )
631 dir = mDirectionField.at( curPt.y() * fieldWidth + curPt.x() );
635 const QPoint curPtDir( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
636 const QPoint pt2 = curPt + curPtDir;
638 if ( dir != prevDir )
640 path.insert( curPt );
641 devicePt = fieldToDevice( curPt );
642 xPoly.append( devicePt.x() );
643 yPoly.append( devicePt.y() );
649 if ( !isTraceExists( curPt ) || isTraceOutside( curPt ) )
652 devicePt = fieldToDevice( curPt - QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 ) );
653 xPoly.append( devicePt.x() );
654 yPoly.append( devicePt.y() );
658 geom = geom.simplify( 1.5 * mFieldResolution ).smooth( 1, 0.25, -1.0, 45 );
660 pen.setColor( QColor( 0, 0, 0, 255 ) );
661 mDrawingTracePainter->setPen( pen );
662 mDrawingTracePainter->drawPolyline( geom.asQPolygonF() );
665bool QgsVectorFieldStreamlinesField::isTraceExists(
const QPoint &pixel )
const
669 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
671 return mField[j * mFieldSize.width() + i];
677bool QgsVectorFieldStreamField::isTraceOutside(
const QPoint &pixel )
const
682 return !( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() );
685void QgsVectorFieldStreamField::setMinimizeFieldSize(
bool minimizeFieldSize )
687 mMinimizeFieldSize = minimizeFieldSize;
690QgsVectorFieldStreamField &QgsVectorFieldStreamField::operator=(
const QgsVectorFieldStreamField &other )
692 if ( &other ==
this )
695 mFieldSize = other.mFieldSize;
696 mFieldResolution = other.mFieldResolution;
698 mTraceImage = other.mTraceImage;
699 mMapToFieldPixel = other.mMapToFieldPixel;
700 mOutputExtent = other.mOutputExtent;
701 mVectorColoring = other.mVectorColoring;
702 mDirectionField = other.mDirectionField;
703 mRenderContext = other.mRenderContext;
704 mPixelFillingCount = other.mPixelFillingCount;
705 mMaxPixelFillingCount = other.mMaxPixelFillingCount;
706 mMapExtent = other.mMapExtent;
707 mFieldTopLeftInDeviceCoordinates = other.mFieldTopLeftInDeviceCoordinates;
708 mValid = other.mValid;
709 mPixelFillingDensity = other.mPixelFillingDensity;
710 mMinMagFilter = other.mMinMagFilter;
711 mMaxMagFilter = other.mMaxMagFilter;
712 mMinimizeFieldSize = other.mMinimizeFieldSize;
713 mSource = other.mSource ? std::unique_ptr<QgsVectorFieldValueSource>( other.mSource->clone() ) : nullptr;
715 mPainter = std::make_unique<QPainter>( &mTraceImage );
720void QgsVectorFieldStreamField::initImage()
722 mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
723 if ( !mTraceImage.isNull() )
725 mTraceImage.fill( 0X00000000 );
726 mPainter = std::make_unique<QPainter>( &mTraceImage );
727 mPainter->setRenderHint( QPainter::Antialiasing,
true );
728 mPainter->setPen( mPen );
732bool QgsVectorFieldStreamField::filterMag(
double value )
const
734 return ( mMinMagFilter < 0 || value > mMinMagFilter ) && ( mMaxMagFilter < 0 || value < mMaxMagFilter );
737QImage QgsVectorFieldStreamField::image()
const
739 if ( mTraceImage.isNull() )
741 return mTraceImage.scaled( mFieldSize * mFieldResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
744void QgsVectorFieldStreamField::setPixelFillingDensity(
double maxFilling )
746 mPixelFillingDensity = maxFilling;
747 mMaxPixelFillingCount = int( mPixelFillingDensity * mFieldSize.width() * mFieldSize.height() );
750void QgsVectorFieldStreamField::setColor( QColor color )
752 mPen.setColor( color );
755void QgsVectorFieldStreamField::setLineWidth(
double width )
757 mPen.setWidthF( width );
760void QgsVectorFieldStreamField::setFilter(
double min,
double max )
766QgsVectorFieldParticleTracesField::QgsVectorFieldParticleTracesField( std::unique_ptr<QgsVectorFieldValueSource> source,
const QgsRenderContext &rendererContext,
const QgsInterpolatedLineColor &vectorColoring )
767 : QgsVectorFieldStreamField( std::move( source ), rendererContext, vectorColoring )
769 std::srand( uint( ::time(
nullptr ) ) );
770 mPen.setCapStyle( Qt::RoundCap );
773QgsVectorFieldParticleTracesField::QgsVectorFieldParticleTracesField(
const QgsVectorFieldParticleTracesField &other )
774 : QgsVectorFieldStreamField( other )
775 , mTimeField( other.mTimeField )
776 , mMagnitudeField( other.mMagnitudeField )
777 , mParticles( other.mParticles )
778 , mStumpImage( other.mStumpImage )
779 , mTimeStep( other.mTimeStep )
780 , mParticlesLifeTime( other.mParticlesLifeTime )
781 , mParticlesCount( other.mParticlesCount )
782 , mTailFactor( other.mTailFactor )
783 , mMinTailLength( other.mMinTailLength )
784 , mParticleColor( other.mParticleColor )
785 , mParticleSize( other.mParticleSize )
786 , mStumpFactor( other.mStumpFactor )
787 , mStumpParticleWithLifeTime( other.mStumpParticleWithLifeTime )
790void QgsVectorFieldParticleTracesField::addParticle(
const QPoint &startPoint,
double lifeTime )
792 addTrace( startPoint );
793 if ( time( startPoint ) > 0 )
795 QgsVectorFieldTraceParticle p;
796 p.lifeTime = lifeTime;
797 p.position = startPoint;
798 mParticles.append( p );
802void QgsVectorFieldParticleTracesField::addParticleXY(
const QgsPointXY &startPoint,
double lifeTime )
804 addParticle( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint(), lifeTime );
807void QgsVectorFieldParticleTracesField::moveParticles()
810 for (
auto &p : mParticles )
812 double spentTime = p.remainingTime;
813 size_t countAdded = 0;
814 while ( spentTime < mTimeStep && p.lifeTime > 0 )
816 double timeToSpend = double( time( p.position ) );
817 if ( timeToSpend > 0 )
819 p.lifeTime -= timeToSpend;
820 spentTime += timeToSpend;
821 QPoint dir = direction( p.position );
822 if ( p.lifeTime > 0 )
825 p.tail.emplace_back( p.position );
840 if ( p.lifeTime <= 0 )
848 p.remainingTime = spentTime - mTimeStep;
849 while (
static_cast<int>( p.tail.size() ) > mMinTailLength &&
static_cast<double>( p.tail.size() ) > (
static_cast<double>( countAdded ) * mTailFactor ) )
850 p.tail.erase( p.tail.begin() );
851 drawParticleTrace( p );
857 while ( i < mParticles.count() )
859 if ( mParticles.at( i ).tail.size() == 0 )
860 mParticles.removeAt( i );
866 if ( mParticles.count() < mParticlesCount )
867 addRandomParticles();
870void QgsVectorFieldParticleTracesField::addRandomParticles()
875 if ( mParticlesCount < 0 )
877 addParticleXY(
QgsPointXY( mMapToFieldPixel.xCenter(), mMapToFieldPixel.yCenter() ), mParticlesLifeTime );
881 int count = mParticlesCount - mParticles.count();
883 for (
int i = 0; i < count; ++i )
885 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) );
886 int yRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) );
887 double lifeTime = ( std::rand() / ( ( RAND_MAX + 1u ) / mParticlesLifeTime ) );
888 addParticle( QPoint( xRandom, yRandom ), lifeTime );
892void QgsVectorFieldParticleTracesField::storeInField(
const QPair<QPoint, QgsVectorFieldStreamField::FieldData> pixelData )
894 int i = pixelData.first.x();
895 int j = pixelData.first.y();
896 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
898 mTimeField[j * mFieldSize.width() + i] = pixelData.second.time;
899 int d = pixelData.second.directionX + 2 + ( pixelData.second.directionY + 1 ) * 3;
900 mDirectionField[j * mFieldSize.width() + i] =
static_cast<unsigned char>( d );
901 mMagnitudeField[j * mFieldSize.width() + i] =
static_cast<float>( pixelData.second.magnitude );
905void QgsVectorFieldParticleTracesField::initField()
907 mTimeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), -1 );
908 mDirectionField = QVector<unsigned char>( mFieldSize.width() * mFieldSize.height(),
static_cast<unsigned char>(
int( 0 ) ) );
909 mMagnitudeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), 0 );
911 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
912 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
915bool QgsVectorFieldParticleTracesField::isTraceExists(
const QPoint &pixel )
const
919 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
921 return mTimeField[j * mFieldSize.width() + i] >= 0;
927void QgsVectorFieldParticleTracesField::setStumpParticleWithLifeTime(
bool stumpParticleWithLifeTime )
929 mStumpParticleWithLifeTime = stumpParticleWithLifeTime;
932void QgsVectorFieldParticleTracesField::setParticlesColor(
const QColor &
c )
934 mVectorColoring.setColor(
c );
937QgsVectorFieldParticleTracesField &QgsVectorFieldParticleTracesField::operator=(
const QgsVectorFieldParticleTracesField &other )
939 if ( &other ==
this )
942 QgsVectorFieldStreamField::operator=( other );
943 mTimeField = other.mTimeField;
944 mMagnitudeField = other.mMagnitudeField;
945 mDirectionField = other.mDirectionField;
946 mParticles = other.mParticles;
947 mStumpImage = other.mStumpImage;
948 mTimeStep = other.mTimeStep;
949 mParticlesLifeTime = other.mParticlesLifeTime;
950 mParticlesCount = other.mParticlesCount;
951 mMinTailLength = other.mMinTailLength;
952 mTailFactor = other.mTailFactor;
953 mParticleColor = other.mParticleColor;
954 mParticleSize = other.mParticleSize;
955 mStumpFactor = other.mStumpFactor;
956 mStumpParticleWithLifeTime = other.mStumpParticleWithLifeTime;
961void QgsVectorFieldParticleTracesField::setMinTailLength(
int minTailLength )
963 mMinTailLength = minTailLength;
966void QgsVectorFieldParticleTracesField::setTailFactor(
double tailFactor )
968 mTailFactor = tailFactor;
971void QgsVectorFieldParticleTracesField::setParticleSize(
double particleSize )
973 mParticleSize = particleSize;
976void QgsVectorFieldParticleTracesField::setTimeStep(
double timeStep )
978 mTimeStep = timeStep;
981void QgsVectorFieldParticleTracesField::setParticlesLifeTime(
double particlesLifeTime )
983 mParticlesLifeTime = particlesLifeTime;
986QImage QgsVectorFieldParticleTracesField::imageRendered()
const
991void QgsVectorFieldParticleTracesField::stump()
996 mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
997 mPainter->drawImage( QPoint( 0, 0 ), mStumpImage );
1000void QgsVectorFieldParticleTracesField::setStumpFactor(
int sf )
1003 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1004 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1007QPoint QgsVectorFieldParticleTracesField::direction( QPoint position )
const
1009 int i = position.x();
1010 int j = position.y();
1011 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1013 int dir =
static_cast<int>( mDirectionField[j * mFieldSize.width() + i] );
1014 if ( dir != 0 && dir < 10 )
1015 return QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
1017 return QPoint( 0, 0 );
1020float QgsVectorFieldParticleTracesField::time( QPoint position )
const
1022 int i = position.x();
1023 int j = position.y();
1024 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1026 return mTimeField[j * mFieldSize.width() + i];
1031float QgsVectorFieldParticleTracesField::magnitude( QPoint position )
const
1033 int i = position.x();
1034 int j = position.y();
1035 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1037 return mMagnitudeField[j * mFieldSize.width() + i];
1042void QgsVectorFieldParticleTracesField::drawParticleTrace(
const QgsVectorFieldTraceParticle &particle )
1046 const std::list<QPoint> &tail = particle.tail;
1047 if ( tail.size() == 0 )
1049 double iniWidth = mParticleSize;
1051 size_t pixelCount = tail.size();
1053 double transparency = 1;
1054 if ( mStumpParticleWithLifeTime )
1055 transparency = sin( M_PI * particle.lifeTime / mParticlesLifeTime );
1058 if ( pixelCount > 1 )
1059 dw = iniWidth /
static_cast<double>( pixelCount );
1063 auto ip1 = std::prev( tail.end() );
1064 auto ip2 = std::prev( ip1 );
1066 while ( ip1 != tail.begin() )
1068 QPointF p1 = fieldToDevice( ( *ip1 ) );
1069 QPointF p2 = fieldToDevice( ( *ip2 ) );
1070 QColor traceColor = mVectorColoring.color( magnitude( *ip1 ) );
1071 traceColor.setAlphaF( traceColor.alphaF() * transparency );
1072 mPen.setColor( traceColor );
1073 mPen.setWidthF( iniWidth - i * dw );
1074 mPainter->setPen( mPen );
1075 mPainter->drawLine( p1, p2 );
1082void QgsVectorFieldParticleTracesField::setParticlesCount(
int particlesCount )
1084 mParticlesCount = particlesCount;
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
Custom exception class for Coordinate Reference System related exceptions.
A geometry is the spatial representation of a feature.
Defines color interpolation for rendering mesh datasets.
@ ColorRamp
Render with a color ramp.
@ SingleColor
Render with a single color.
Line string geometry type, with support for z-dimension and m-values.
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.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Feedback object tailored for raster block reading.
Interface for all raster shaders.
void setRasterShaderFunction(QgsRasterShaderFunction *function)
A public method that allows the user to set their own shader function.
A rectangle specified with double values.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Contains information about the context of a rendering operation.
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...
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Scoped object for saving and restoring a QPainter object's state.
Raster renderer pipe for single band pseudocolor.
Represent a 2-dimensional vector.
double y() const
Returns the vector's y-component.
QgsVector rotateBy(double rot) const
Rotates the vector by a specified angle.
double x() const
Returns the vector's x-component.
double length() const
Returns the length of the vector.
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).