33#define M_DEG2RAD 0.0174532925
44 const double xMin = std::min( { topLeft.
x(), topRight.
x(), bottomLeft.
x(), bottomRight.
x() } );
45 const double xMax = std::max( { topLeft.
x(), topRight.
x(), bottomLeft.
x(), bottomRight.
x() } );
46 const double yMin = std::min( { topLeft.
y(), topRight.
y(), bottomLeft.
y(), bottomRight.
y() } );
47 const double yMax = std::max( { topLeft.
y(), topRight.
y(), bottomLeft.
y(), bottomRight.
y() } );
52QSize QgsVectorFieldStreamField::size()
const
57QPoint QgsVectorFieldStreamField::topLeft()
const
59 return mFieldTopLeftInDeviceCoordinates;
62int QgsVectorFieldStreamField::resolution()
const
64 return mFieldResolution;
67QgsPointXY QgsVectorFieldStreamField::positionToMapCoordinates(
const QPoint &pixelPosition,
const QgsPointXY &positionInPixel )
69 QgsPointXY mapPoint = mMapToFieldPixel.toMapCoordinates( pixelPosition );
70 mapPoint = mapPoint +
QgsVector( positionInPixel.
x() * mMapToFieldPixel.mapUnitsPerPixel(), positionInPixel.
y() * mMapToFieldPixel.mapUnitsPerPixel() );
74QgsVectorFieldStreamField::QgsVectorFieldStreamField( std::unique_ptr<QgsVectorFieldValueSource> source,
const QgsRenderContext &rendererContext,
const QgsInterpolatedLineColor &vectorColoring,
int resolution )
75 : mFieldResolution( resolution )
76 , mVectorColoring( vectorColoring )
77 , mRenderContext( rendererContext )
78 , mSource( std::move( source ) )
81QgsVectorFieldStreamField::QgsVectorFieldStreamField(
const QgsVectorFieldStreamField &other )
82 : mFieldSize( other.mFieldSize )
83 , mFieldResolution( other.mFieldResolution )
85 , mTraceImage( other.mTraceImage )
86 , mMapToFieldPixel( other.mMapToFieldPixel )
87 , mOutputExtent( other.mOutputExtent )
88 , mVectorColoring( other.mVectorColoring )
89 , mDirectionField( other.mDirectionField )
90 , mRenderContext( other.mRenderContext )
91 , mPixelFillingCount( other.mPixelFillingCount )
92 , mMaxPixelFillingCount( other.mMaxPixelFillingCount )
93 , mMapExtent( other.mMapExtent )
94 , mFieldTopLeftInDeviceCoordinates( other.mFieldTopLeftInDeviceCoordinates )
95 , mValid( other.mValid )
96 , mPixelFillingDensity( other.mPixelFillingDensity )
97 , mMinMagFilter( other.mMinMagFilter )
98 , mMaxMagFilter( other.mMaxMagFilter )
99 , mMinimizeFieldSize( other.mMinimizeFieldSize )
101 mPainter = std::make_unique<QPainter>( &mTraceImage );
102 mSource = other.mSource ? std::unique_ptr<QgsVectorFieldValueSource>( other.mSource->clone() ) : nullptr;
105QgsVectorFieldStreamField::~QgsVectorFieldStreamField()
111void QgsVectorFieldStreamField::updateSize(
const QgsRenderContext &renderContext )
126 layerExtent = mMapExtent;
130 if ( mMinimizeFieldSize )
131 interestZoneExtent = layerExtent.
intersect( mMapExtent );
133 interestZoneExtent = mMapExtent;
138 mFieldSize = QSize();
139 mFieldTopLeftInDeviceCoordinates = QPoint();
144 QgsRectangle fieldInterestZoneInDeviceCoordinates = ::boundingBoxToScreenRectangle( deviceMapToPixel, interestZoneExtent );
145 mFieldTopLeftInDeviceCoordinates
146 = QPoint(
static_cast<int>( std::round( fieldInterestZoneInDeviceCoordinates.
xMinimum() ) ),
static_cast<int>( std::round( fieldInterestZoneInDeviceCoordinates.
yMinimum() ) ) );
147 int fieldWidthInDeviceCoordinate = int( fieldInterestZoneInDeviceCoordinates.
width() );
148 int fieldHeightInDeviceCoordinate = int( fieldInterestZoneInDeviceCoordinates.
height() );
150 int fieldWidth = int( fieldWidthInDeviceCoordinate / mFieldResolution );
151 int fieldHeight = int( fieldHeightInDeviceCoordinate / mFieldResolution );
154 if ( fieldWidthInDeviceCoordinate % mFieldResolution > 0 )
156 if ( fieldHeightInDeviceCoordinate % mFieldResolution > 0 )
159 if ( fieldWidth == 0 || fieldHeight == 0 )
161 mFieldSize = QSize();
166 mFieldSize.setWidth( fieldWidth );
167 mFieldSize.setHeight( fieldHeight );
174 std::min( { pt1.
x(), pt2.
x(), pt3.
x(), pt4.
x() } ),
175 std::min( { pt1.
y(), pt2.
y(), pt3.
y(), pt4.
y() } ),
176 std::max( { pt1.
x(), pt2.
x(), pt3.
x(), pt4.
x() } ),
177 std::max( { pt1.
y(), pt2.
y(), pt3.
y(), pt4.
y() } ),
182 double mapUnitPerFieldPixel;
183 if ( interestZoneExtent.
width() > 0 )
184 mapUnitPerFieldPixel = deviceMapToPixel.
mapUnitsPerPixel() * mFieldResolution * mFieldSize.width() / ( fieldWidthInDeviceCoordinate /
static_cast<double>( mFieldResolution ) );
186 mapUnitPerFieldPixel = 1e-8;
188 int fieldRightDevice = mFieldTopLeftInDeviceCoordinates.x() + mFieldSize.width() * mFieldResolution;
189 int fieldBottomDevice = mFieldTopLeftInDeviceCoordinates.y() + mFieldSize.height() * mFieldResolution;
192 int fieldTopDevice = mFieldTopLeftInDeviceCoordinates.
x();
193 int fieldLeftDevice = mFieldTopLeftInDeviceCoordinates.y();
196 double xc = ( fieldRightBottomMap.
x() + fieldTopLeftMap.
x() ) / 2;
197 double yc = ( fieldTopLeftMap.
y() + fieldRightBottomMap.
y() ) / 2;
199 mMapToFieldPixel =
QgsMapToPixel( mapUnitPerFieldPixel, xc, yc, fieldWidth, fieldHeight, deviceMapToPixel.
mapRotation() );
205void QgsVectorFieldStreamField::updateSize(
const QgsRenderContext &renderContext,
int resolution )
207 if ( renderContext.
mapExtent() == mMapExtent && resolution == mFieldResolution )
209 mFieldResolution = resolution;
211 updateSize( renderContext );
214bool QgsVectorFieldStreamField::isValid()
const
219void QgsVectorFieldStreamField::addTrace(
QgsPointXY startPoint )
221 addTrace( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint() );
225void QgsVectorFieldStreamField::addRandomTraces()
227 if ( mSource && mSource->maximumMagnitude() > 0 )
228 while ( ( mPixelFillingCount < mMaxPixelFillingCount ) && ( !mRenderContext.feedback() || !mRenderContext.feedback()->isCanceled() || !mRenderContext.renderingStopped() ) )
232void QgsVectorFieldStreamField::addRandomTrace()
237 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) );
238 int yRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) );
239 addTrace( QPoint( xRandom, yRandom ) );
242void QgsVectorFieldStreamField::addGriddedTraces(
int dx,
int dy )
245 while ( i < mFieldSize.width() && mRenderContext.feedback() && !mRenderContext.feedback()->isCanceled() )
248 while ( j < mFieldSize.height() && mRenderContext.feedback() && !mRenderContext.feedback()->isCanceled() )
250 addTrace( QPoint( i, j ) );
257void QgsVectorFieldStreamField::addTracesOnDataPoints(
const QgsRectangle &extent )
262 const QVector<QgsPointXY> points = mSource->seedPoints( extent );
267void QgsVectorFieldStreamField::addTrace( QPoint startPixel )
273 if ( isTraceExists( startPixel ) || isTraceOutside( startPixel ) )
279 const double maximumMagnitude = mSource->maximumMagnitude();
280 if ( !( maximumMagnitude > 0 ) )
283 mPainter->setPen( mPen );
289 std::list<QPair<QPoint, FieldData>> chunkTrace;
291 QPoint currentPixel = startPixel;
299 vector = mSource->vectorValue( mapPosition );
301 if ( std::isnan( vector.
x() ) || std::isnan( vector.
y() ) )
303 mPixelFillingCount++;
304 setChunkTrace( chunkTrace );
312 QgsVector vu = vector / maximumMagnitude * 2;
313 data.magnitude = vector.
length();
317 double Vu = data.magnitude / maximumMagnitude * 2;
322 addPixelToChunkTrace( currentPixel, data, chunkTrace );
323 simplifyChunkTrace( chunkTrace );
324 setChunkTrace( chunkTrace );
332 if ( nextPosition.
x() > 1 )
334 if ( nextPosition.
x() < -1 )
336 if ( nextPosition.
y() > 1 )
338 if ( nextPosition.
y() < -1 )
341 if ( incX != 0 || incY != 0 )
343 data.directionX = incX;
344 data.directionY = -incY;
346 if ( chunkTrace.empty() )
348 storeInField( QPair<QPoint, FieldData>( currentPixel, data ) );
350 if ( addPixelToChunkTrace( currentPixel, data, chunkTrace ) )
352 setChunkTrace( chunkTrace );
353 clearChunkTrace( chunkTrace );
357 currentPixel += QPoint( incX, -incY );
358 x1 = nextPosition.
x() - 2 * incX;
359 y1 = nextPosition.
y() - 2 * incY;
390 x2 = x1 + ( 1 - y1 ) * Vx / fabs( Vy );
392 x2 = x1 + ( 1 + y1 ) * Vx / fabs( Vy );
394 y2 = y1 + ( 1 - x1 ) * Vy / fabs( Vx );
396 y2 = y1 + ( 1 + x1 ) * Vy / fabs( Vx );
414 double dl = sqrt( dx * dx + dy * dy );
416 data.time +=
static_cast<float>( dl / Vu );
417 if ( data.time > 10000 )
419 addPixelToChunkTrace( currentPixel, data, chunkTrace );
420 setChunkTrace( chunkTrace );
428 if ( isTraceExists( currentPixel ) )
431 setChunkTrace( chunkTrace );
432 addPixelToChunkTrace( currentPixel, data, chunkTrace );
436 if ( isTraceOutside( currentPixel ) )
438 setChunkTrace( chunkTrace );
442 if ( mRenderContext.feedback() && mRenderContext.feedback()->isCanceled() )
445 if ( mRenderContext.renderingStopped() )
449 drawTrace( startPixel );
452void QgsVectorFieldStreamField::setResolution(
int width )
454 mFieldResolution = width;
457QSize QgsVectorFieldStreamField::imageSize()
const
459 return mFieldSize * mFieldResolution;
462QPointF QgsVectorFieldStreamField::fieldToDevice(
const QPoint &pixel )
const
465 p = mFieldResolution * p + QPointF( mFieldResolution - 1, mFieldResolution - 1 ) / 2;
469bool QgsVectorFieldStreamField::addPixelToChunkTrace( QPoint &pixel, QgsVectorFieldStreamField::FieldData &data, std::list<QPair<QPoint, QgsVectorFieldStreamField::FieldData> > &chunkTrace )
471 chunkTrace.emplace_back( pixel, data );
472 if ( chunkTrace.size() == 3 )
474 simplifyChunkTrace( chunkTrace );
480void QgsVectorFieldStreamlinesField::initField()
482 mField = QVector<bool>( mFieldSize.width() * mFieldSize.height(),
false );
483 mDirectionField = QVector<unsigned char>( mFieldSize.width() * mFieldSize.height(),
static_cast<unsigned char>(
int( 0 ) ) );
487void QgsVectorFieldStreamlinesField::initImage()
489 mTraceImage = QImage();
490 switch ( mVectorColoring.coloringMethod() )
494 QSize imgSize = mFieldSize * mFieldResolution;
499 std::unique_ptr<QgsRasterInterface> magnitudeSource = mSource ? mSource->magnitudeSource( fieldContext, imgSize ) :
nullptr;
501 if ( magnitudeSource && imgSize.isValid() )
506 std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, mOutputExtent, imgSize.width(), imgSize.height(), mFeedBack ) );
507 mTraceImage = bl->image();
512 mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32_Premultiplied );
513 if ( !mTraceImage.isNull() )
514 mTraceImage.fill( mVectorColoring.singleColor() );
520 mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32_Premultiplied );
521 QColor col = mVectorColoring.singleColor();
522 mTraceImage.fill( col );
527 if ( !mTraceImage.isNull() )
529 mPainter = std::make_unique<QPainter>( &mTraceImage );
530 mPainter->setRenderHint( QPainter::Antialiasing,
true );
532 mDrawingTraceImage = QImage( mTraceImage.size(), QImage::Format_ARGB32_Premultiplied );
533 mDrawingTraceImage.fill( Qt::transparent );
534 mDrawingTracePainter = std::make_unique<QPainter>( &mDrawingTraceImage );
535 mDrawingTracePainter->setRenderHint( QPainter::Antialiasing,
true );
539void QgsVectorFieldStreamField::clearChunkTrace( std::list<QPair<QPoint, QgsVectorFieldStreamField::FieldData> > &chunkTrace )
541 auto one_before_end = std::prev( chunkTrace.end() );
542 chunkTrace.erase( chunkTrace.begin(), one_before_end );
545void QgsVectorFieldStreamField::simplifyChunkTrace( std::list<QPair<QPoint, FieldData> > &chunkTrace )
547 if ( chunkTrace.size() != 3 )
550 auto ip3 = chunkTrace.begin();
554 while ( ip3 != chunkTrace.end() && ip2 != chunkTrace.end() )
556 QPoint v1 = ( *ip1 ).first - ( *ip2 ).first;
557 QPoint v2 = ( *ip2 ).first - ( *ip3 ).first;
558 if ( v1.x() * v2.x() + v1.y() * v2.y() == 0 )
560 ( *ip1 ).second.time += ( ( *ip2 ).second.time ) / 2;
561 ( *ip3 ).second.time += ( ( *ip2 ).second.time ) / 2;
562 ( *ip1 ).second.directionX += ( *ip2 ).second.directionX;
563 ( *ip1 ).second.directionY += ( *ip2 ).second.directionY;
564 chunkTrace.erase( ip2 );
571QgsVectorFieldStreamlinesField::QgsVectorFieldStreamlinesField(
574 : QgsVectorFieldStreamField( std::move( source ), rendererContext, vectorColoring )
575 , mFeedBack( feedBack )
578void QgsVectorFieldStreamlinesField::compose()
582 mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
583 mPainter->drawImage( 0, 0, mDrawingTraceImage );
586void QgsVectorFieldStreamlinesField::storeInField(
const QPair<QPoint, FieldData> pixelData )
588 int i = pixelData.first.x();
589 int j = pixelData.first.y();
590 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
592 mField[j * mFieldSize.width() + i] =
true;
593 int d = pixelData.second.directionX + 2 + ( pixelData.second.directionY + 1 ) * 3;
594 mDirectionField[j * mFieldSize.width() + i] =
static_cast<unsigned char>( d );
598void QgsVectorFieldStreamField::setChunkTrace( std::list<QPair<QPoint, FieldData> > &chunkTrace )
600 auto p = chunkTrace.begin();
601 while ( p != chunkTrace.end() )
603 storeInField( ( *p ) );
604 mPixelFillingCount++;
609void QgsVectorFieldStreamlinesField::drawTrace(
const QPoint &start )
const
611 if ( !isTraceExists( start ) || isTraceOutside( start ) )
614 if ( !mDrawingTracePainter )
619 int fieldWidth = mFieldSize.width();
620 QSet<QgsPointXY> path;
621 unsigned char dir = 0;
622 unsigned char prevDir = mDirectionField.at( pt1.y() * fieldWidth + pt1.x() );
624 QVector<double> xPoly;
625 QVector<double> yPoly;
626 QPointF devicePt = fieldToDevice( pt1 );
627 xPoly.append( devicePt.x() );
628 yPoly.append( devicePt.y() );
630 while ( isTraceExists( curPt ) && !isTraceOutside( curPt ) && !path.contains( curPt ) )
632 dir = mDirectionField.at( curPt.y() * fieldWidth + curPt.x() );
636 const QPoint curPtDir( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
637 const QPoint pt2 = curPt + curPtDir;
639 if ( dir != prevDir )
641 path.insert( curPt );
642 devicePt = fieldToDevice( curPt );
643 xPoly.append( devicePt.x() );
644 yPoly.append( devicePt.y() );
650 if ( !isTraceExists( curPt ) || isTraceOutside( curPt ) )
653 devicePt = fieldToDevice( curPt - QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 ) );
654 xPoly.append( devicePt.x() );
655 yPoly.append( devicePt.y() );
659 geom = geom.simplify( 1.5 * mFieldResolution ).smooth( 1, 0.25, -1.0, 45 );
661 pen.setColor( QColor( 0, 0, 0, 255 ) );
662 mDrawingTracePainter->setPen( pen );
663 mDrawingTracePainter->drawPolyline( geom.asQPolygonF() );
666bool QgsVectorFieldStreamlinesField::isTraceExists(
const QPoint &pixel )
const
670 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
672 return mField[j * mFieldSize.width() + i];
678bool QgsVectorFieldStreamField::isTraceOutside(
const QPoint &pixel )
const
683 return !( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() );
686void QgsVectorFieldStreamField::setMinimizeFieldSize(
bool minimizeFieldSize )
688 mMinimizeFieldSize = minimizeFieldSize;
691QgsVectorFieldStreamField &QgsVectorFieldStreamField::operator=(
const QgsVectorFieldStreamField &other )
693 if ( &other ==
this )
696 mFieldSize = other.mFieldSize;
697 mFieldResolution = other.mFieldResolution;
699 mTraceImage = other.mTraceImage;
700 mMapToFieldPixel = other.mMapToFieldPixel;
701 mOutputExtent = other.mOutputExtent;
702 mVectorColoring = other.mVectorColoring;
703 mDirectionField = other.mDirectionField;
704 mRenderContext = other.mRenderContext;
705 mPixelFillingCount = other.mPixelFillingCount;
706 mMaxPixelFillingCount = other.mMaxPixelFillingCount;
707 mMapExtent = other.mMapExtent;
708 mFieldTopLeftInDeviceCoordinates = other.mFieldTopLeftInDeviceCoordinates;
709 mValid = other.mValid;
710 mPixelFillingDensity = other.mPixelFillingDensity;
711 mMinMagFilter = other.mMinMagFilter;
712 mMaxMagFilter = other.mMaxMagFilter;
713 mMinimizeFieldSize = other.mMinimizeFieldSize;
714 mSource = other.mSource ? std::unique_ptr<QgsVectorFieldValueSource>( other.mSource->clone() ) : nullptr;
716 mPainter = std::make_unique<QPainter>( &mTraceImage );
721void QgsVectorFieldStreamField::initImage()
723 mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
724 if ( !mTraceImage.isNull() )
726 mTraceImage.fill( 0X00000000 );
727 mPainter = std::make_unique<QPainter>( &mTraceImage );
728 mPainter->setRenderHint( QPainter::Antialiasing,
true );
729 mPainter->setPen( mPen );
733bool QgsVectorFieldStreamField::filterMag(
double value )
const
735 return ( mMinMagFilter < 0 || value > mMinMagFilter ) && ( mMaxMagFilter < 0 || value < mMaxMagFilter );
738QImage QgsVectorFieldStreamField::image()
const
740 if ( mTraceImage.isNull() )
742 return mTraceImage.scaled( mFieldSize * mFieldResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
745void QgsVectorFieldStreamField::setPixelFillingDensity(
double maxFilling )
747 mPixelFillingDensity = maxFilling;
748 mMaxPixelFillingCount = int( mPixelFillingDensity * mFieldSize.width() * mFieldSize.height() );
751void QgsVectorFieldStreamField::setColor( QColor color )
753 mPen.setColor( color );
756void QgsVectorFieldStreamField::setLineWidth(
double width )
758 mPen.setWidthF( width );
761void QgsVectorFieldStreamField::setFilter(
double min,
double max )
767QgsVectorFieldParticleTracesField::QgsVectorFieldParticleTracesField( std::unique_ptr<QgsVectorFieldValueSource> source,
const QgsRenderContext &rendererContext,
const QgsInterpolatedLineColor &vectorColoring )
768 : QgsVectorFieldStreamField( std::move( source ), rendererContext, vectorColoring )
770 std::srand( uint( ::time(
nullptr ) ) );
771 mPen.setCapStyle( Qt::RoundCap );
774QgsVectorFieldParticleTracesField::QgsVectorFieldParticleTracesField(
const QgsVectorFieldParticleTracesField &other )
775 : QgsVectorFieldStreamField( other )
776 , mTimeField( other.mTimeField )
777 , mMagnitudeField( other.mMagnitudeField )
778 , mParticles( other.mParticles )
779 , mStumpImage( other.mStumpImage )
780 , mTimeStep( other.mTimeStep )
781 , mParticlesLifeTime( other.mParticlesLifeTime )
782 , mParticlesCount( other.mParticlesCount )
783 , mTailFactor( other.mTailFactor )
784 , mMinTailLength( other.mMinTailLength )
785 , mParticleColor( other.mParticleColor )
786 , mParticleSize( other.mParticleSize )
787 , mStumpFactor( other.mStumpFactor )
788 , mStumpParticleWithLifeTime( other.mStumpParticleWithLifeTime )
791void QgsVectorFieldParticleTracesField::addParticle(
const QPoint &startPoint,
double lifeTime )
793 addTrace( startPoint );
794 if ( time( startPoint ) > 0 )
796 QgsVectorFieldTraceParticle p;
797 p.lifeTime = lifeTime;
798 p.position = startPoint;
799 mParticles.append( p );
803void QgsVectorFieldParticleTracesField::addParticleXY(
const QgsPointXY &startPoint,
double lifeTime )
805 addParticle( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint(), lifeTime );
808void QgsVectorFieldParticleTracesField::moveParticles()
811 for (
auto &p : mParticles )
813 double spentTime = p.remainingTime;
814 size_t countAdded = 0;
815 while ( spentTime < mTimeStep && p.lifeTime > 0 )
817 double timeToSpend = double( time( p.position ) );
818 if ( timeToSpend > 0 )
820 p.lifeTime -= timeToSpend;
821 spentTime += timeToSpend;
822 QPoint dir = direction( p.position );
823 if ( p.lifeTime > 0 )
826 p.tail.emplace_back( p.position );
841 if ( p.lifeTime <= 0 )
849 p.remainingTime = spentTime - mTimeStep;
850 while (
static_cast<int>( p.tail.size() ) > mMinTailLength &&
static_cast<double>( p.tail.size() ) > (
static_cast<double>( countAdded ) * mTailFactor ) )
851 p.tail.erase( p.tail.begin() );
852 drawParticleTrace( p );
858 while ( i < mParticles.count() )
860 if ( mParticles.at( i ).tail.size() == 0 )
861 mParticles.removeAt( i );
867 if ( mParticles.count() < mParticlesCount )
868 addRandomParticles();
871void QgsVectorFieldParticleTracesField::addRandomParticles()
876 if ( mParticlesCount < 0 )
878 addParticleXY(
QgsPointXY( mMapToFieldPixel.xCenter(), mMapToFieldPixel.yCenter() ), mParticlesLifeTime );
882 int count = mParticlesCount - mParticles.count();
884 for (
int i = 0; i < count; ++i )
886 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) );
887 int yRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) );
888 double lifeTime = ( std::rand() / ( ( RAND_MAX + 1u ) / mParticlesLifeTime ) );
889 addParticle( QPoint( xRandom, yRandom ), lifeTime );
893void QgsVectorFieldParticleTracesField::storeInField(
const QPair<QPoint, QgsVectorFieldStreamField::FieldData> pixelData )
895 int i = pixelData.first.x();
896 int j = pixelData.first.y();
897 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
899 mTimeField[j * mFieldSize.width() + i] = pixelData.second.time;
900 int d = pixelData.second.directionX + 2 + ( pixelData.second.directionY + 1 ) * 3;
901 mDirectionField[j * mFieldSize.width() + i] =
static_cast<unsigned char>( d );
902 mMagnitudeField[j * mFieldSize.width() + i] =
static_cast<float>( pixelData.second.magnitude );
906void QgsVectorFieldParticleTracesField::initField()
908 mTimeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), -1 );
909 mDirectionField = QVector<unsigned char>( mFieldSize.width() * mFieldSize.height(),
static_cast<unsigned char>(
int( 0 ) ) );
910 mMagnitudeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), 0 );
912 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
913 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
916bool QgsVectorFieldParticleTracesField::isTraceExists(
const QPoint &pixel )
const
920 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
922 return mTimeField[j * mFieldSize.width() + i] >= 0;
928void QgsVectorFieldParticleTracesField::setStumpParticleWithLifeTime(
bool stumpParticleWithLifeTime )
930 mStumpParticleWithLifeTime = stumpParticleWithLifeTime;
933void QgsVectorFieldParticleTracesField::setParticlesColor(
const QColor &
c )
935 mVectorColoring.setColor(
c );
938QgsVectorFieldParticleTracesField &QgsVectorFieldParticleTracesField::operator=(
const QgsVectorFieldParticleTracesField &other )
940 if ( &other ==
this )
943 QgsVectorFieldStreamField::operator=( other );
944 mTimeField = other.mTimeField;
945 mMagnitudeField = other.mMagnitudeField;
946 mDirectionField = other.mDirectionField;
947 mParticles = other.mParticles;
948 mStumpImage = other.mStumpImage;
949 mTimeStep = other.mTimeStep;
950 mParticlesLifeTime = other.mParticlesLifeTime;
951 mParticlesCount = other.mParticlesCount;
952 mMinTailLength = other.mMinTailLength;
953 mTailFactor = other.mTailFactor;
954 mParticleColor = other.mParticleColor;
955 mParticleSize = other.mParticleSize;
956 mStumpFactor = other.mStumpFactor;
957 mStumpParticleWithLifeTime = other.mStumpParticleWithLifeTime;
962void QgsVectorFieldParticleTracesField::setMinTailLength(
int minTailLength )
964 mMinTailLength = minTailLength;
967void QgsVectorFieldParticleTracesField::setTailFactor(
double tailFactor )
969 mTailFactor = tailFactor;
972void QgsVectorFieldParticleTracesField::setParticleSize(
double particleSize )
974 mParticleSize = particleSize;
977void QgsVectorFieldParticleTracesField::setTimeStep(
double timeStep )
979 mTimeStep = timeStep;
982void QgsVectorFieldParticleTracesField::setParticlesLifeTime(
double particlesLifeTime )
984 mParticlesLifeTime = particlesLifeTime;
987QImage QgsVectorFieldParticleTracesField::imageRendered()
const
992void QgsVectorFieldParticleTracesField::stump()
997 mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
998 mPainter->drawImage( QPoint( 0, 0 ), mStumpImage );
1001void QgsVectorFieldParticleTracesField::setStumpFactor(
int sf )
1004 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1005 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1008QPoint QgsVectorFieldParticleTracesField::direction( QPoint position )
const
1010 int i = position.x();
1011 int j = position.y();
1012 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1014 int dir =
static_cast<int>( mDirectionField[j * mFieldSize.width() + i] );
1015 if ( dir != 0 && dir < 10 )
1016 return QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
1018 return QPoint( 0, 0 );
1021float QgsVectorFieldParticleTracesField::time( QPoint position )
const
1023 int i = position.x();
1024 int j = position.y();
1025 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1027 return mTimeField[j * mFieldSize.width() + i];
1032float QgsVectorFieldParticleTracesField::magnitude( QPoint position )
const
1034 int i = position.x();
1035 int j = position.y();
1036 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1038 return mMagnitudeField[j * mFieldSize.width() + i];
1043void QgsVectorFieldParticleTracesField::drawParticleTrace(
const QgsVectorFieldTraceParticle &particle )
1047 const std::list<QPoint> &tail = particle.tail;
1048 if ( tail.size() == 0 )
1050 double iniWidth = mParticleSize;
1052 size_t pixelCount = tail.size();
1054 double transparency = 1;
1055 if ( mStumpParticleWithLifeTime )
1056 transparency = sin( M_PI * particle.lifeTime / mParticlesLifeTime );
1059 if ( pixelCount > 1 )
1060 dw = iniWidth /
static_cast<double>( pixelCount );
1064 auto ip1 = std::prev( tail.end() );
1065 auto ip2 = std::prev( ip1 );
1067 while ( ip1 != tail.begin() )
1069 QPointF p1 = fieldToDevice( ( *ip1 ) );
1070 QPointF p2 = fieldToDevice( ( *ip2 ) );
1071 QColor traceColor = mVectorColoring.color( magnitude( *ip1 ) );
1072 traceColor.setAlphaF( traceColor.alphaF() * transparency );
1073 mPen.setColor( traceColor );
1074 mPen.setWidthF( iniWidth - i * dw );
1075 mPainter->setPen( mPen );
1076 mPainter->drawLine( p1, p2 );
1083void QgsVectorFieldParticleTracesField::setParticlesCount(
int particlesCount )
1085 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).