1 /*************************************************************************** 2 qgsmeshtracerenderer.h 3 ------------------------- 4 begin : November 2019 5 copyright : (C) 2019 by Vincent Cloarec 6 email : vcloarec at gmail dot com 7 ***************************************************************************/ 8 9 /*************************************************************************** 10 * * 11 * This program is free software; you can redistribute it and/or modify * 12 * it under the terms of the GNU General Public License as published by * 13 * the Free Software Foundation; either version 2 of the License, or * 14 * (at your option) any later version. * 15 * * 16 ***************************************************************************/ 17 18 #ifndef QGSMESHTRACERENDERER_H 19 #define QGSMESHTRACERENDERER_H 20 21 22 #include <QVector> 23 #include <QSize> 24 25 #include "qgis_core.h" 26 #include "qgis.h" 27 #include "qgstriangularmesh.h" 28 #include "qgsmeshlayer.h" 29 #include "qgsmeshlayerutils.h" 30 #include "qgsmeshvectorrenderer.h" 31 32 ///@cond PRIVATE 33 34 #ifndef SIP_RUN 35 36 /** 37 * \ingroup core 38 * 39 * \brief Abstract class used to interpolate the value of the vector for a pixel 40 * 41 * \note not available in Python bindings 42 * \since QGIS 3.12 43 */ 44 class QgsMeshVectorValueInterpolator 45 { 46 public: 47 //! Constructor 48 QgsMeshVectorValueInterpolator( const QgsTriangularMesh &triangularMesh, 49 const QgsMeshDataBlock &datasetVectorValues ); 50 51 //! Constructor with scalar active face flag values to not interpolate on inactive face 52 QgsMeshVectorValueInterpolator( const QgsTriangularMesh &triangularMesh, 53 const QgsMeshDataBlock &datasetVectorValues, 54 const QgsMeshDataBlock &scalarActiveFaceFlagValues ); 55 56 //! Copy constructor 57 QgsMeshVectorValueInterpolator( const QgsMeshVectorValueInterpolator &other ); 58 59 //! Clone 60 virtual QgsMeshVectorValueInterpolator *clone() = 0; 61 62 //! Destructor 63 virtual ~QgsMeshVectorValueInterpolator() = default; 64 65 /** 66 * Returns the interpolated vector 67 * \param point point in map coordinates 68 */ 69 virtual QgsVector vectorValue( const QgsPointXY &point ) const; 70 71 //! Assignment operator 72 QgsMeshVectorValueInterpolator &operator=( const QgsMeshVectorValueInterpolator &other ); 73 74 protected: 75 void updateCacheFaceIndex( const QgsPointXY &point ) const; 76 77 QgsTriangularMesh mTriangularMesh; 78 QgsMeshDataBlock mDatasetValues; 79 QgsMeshDataBlock mActiveFaceFlagValues; 80 mutable QgsMeshFace mFaceCache; 81 mutable int mCacheFaceIndex = -1; 82 bool mUseScalarActiveFaceFlagValues = false; 83 bool isVectorValid( const QgsVector &v ) const; 84 85 private: 86 87 void activeFaceFilter( QgsVector &vector, int faceIndex ) const; 88 89 virtual QgsVector interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const = 0; 90 }; 91 92 /** 93 * \ingroup core 94 * 95 * \brief Class used to retrieve the value of the vector for a pixel from vertex 96 * 97 * \note not available in Python bindings 98 * \since QGIS 3.12 99 */ 100 class QgsMeshVectorValueInterpolatorFromVertex: public QgsMeshVectorValueInterpolator 101 { 102 public: 103 //! Constructor 104 QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh, 105 const QgsMeshDataBlock &datasetVectorValues ); 106 107 //! Constructor with scalar active face flag value to not interpolate on inactive face 108 QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh, 109 const QgsMeshDataBlock &datasetVectorValues, 110 const QgsMeshDataBlock &scalarActiveFaceFlagValues ); 111 112 //! Copy constructor 113 QgsMeshVectorValueInterpolatorFromVertex( const QgsMeshVectorValueInterpolatorFromVertex &other ); 114 115 //! Clone the instance 116 virtual QgsMeshVectorValueInterpolatorFromVertex *clone() override; 117 118 //! Assignment operator 119 QgsMeshVectorValueInterpolatorFromVertex &operator=( const QgsMeshVectorValueInterpolatorFromVertex &other ); 120 121 private: 122 QgsVector interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const override; 123 }; 124 125 /** 126 * \ingroup core 127 * 128 * \brief Class used to retrieve the value of the vector for a pixel from vertex 129 * 130 * \note not available in Python bindings 131 * \since QGIS 3.12 132 */ 133 class QgsMeshVectorValueInterpolatorFromFace: public QgsMeshVectorValueInterpolator 134 { 135 public: 136 //! Constructor 137 QgsMeshVectorValueInterpolatorFromFace( const QgsTriangularMesh &triangularMesh, 138 const QgsMeshDataBlock &datasetVectorValues ); 139 140 //! Constructor with scalar active face flag value to not interpolate on inactive face 141 QgsMeshVectorValueInterpolatorFromFace( const QgsTriangularMesh &triangularMesh, 142 const QgsMeshDataBlock &datasetVectorValues, 143 const QgsMeshDataBlock &scalarActiveFaceFlagValues ); 144 145 //! Copy constructor 146 QgsMeshVectorValueInterpolatorFromFace( const QgsMeshVectorValueInterpolatorFromFace &other ); 147 148 //! Clone the instance 149 virtual QgsMeshVectorValueInterpolatorFromFace *clone() override; 150 151 //! Assignment operator 152 QgsMeshVectorValueInterpolatorFromFace &operator=( const QgsMeshVectorValueInterpolatorFromFace &other ); 153 154 private: 155 QgsVector interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const override; 156 }; 157 158 /** 159 * \ingroup core 160 * 161 * \brief Abstract class used to handle information about stream field 162 * 163 * \note not available in Python bindings 164 * \since QGIS 3.12 165 */ 166 class QgsMeshStreamField 167 { 168 public: 169 struct FieldData 170 { 171 double magnitude; 172 float time; 173 int directionX; 174 int directionY; 175 }; 176 177 //! Constructor 178 QgsMeshStreamField( const QgsTriangularMesh &triangularMesh, 179 const QgsMeshDataBlock &dataSetVectorValues, 180 const QgsMeshDataBlock &scalarActiveFaceFlagValues, 181 const QgsRectangle &layerExtent, 182 double magnitudeMaximum, 183 bool dataIsOnVertices, 184 const QgsRenderContext &rendererContext, 185 const QgsInterpolatedLineColor &vectorColoring, 186 int resolution = 1 ); 187 188 //! Copy constructor 189 QgsMeshStreamField( const QgsMeshStreamField &other ); 190 191 //! Destructor 192 virtual ~QgsMeshStreamField(); 193 194 /** 195 * Updates the size of the field and the QgsMapToPixel instance to retrieve map point 196 * from pixel in the field depending on the resolution of the device 197 * If the extent of renderer context and the resolution are not changed, do nothing 198 * else, updates the size and cleans 199 */ 200 void updateSize( const QgsRenderContext &renderContext ); 201 202 /** 203 * Updates the size of the field and the QgsMapToPixel instance to retrieve map point 204 * from pixel in the field depending on the resolution of the device 205 */ 206 void updateSize( const QgsRenderContext &renderContext, int resolution ); 207 208 //! Returns true if the field is valid 209 bool isValid() const; 210 211 //! Returns the size of the field 212 QSize size() const; 213 214 //! Returns the topLeft of the field in the device coordinate 215 QPoint topLeft() const; 216 217 //! Adds a trace in the field from a start pixel 218 void addTrace( QPoint startPixel ); 219 220 //! Adds a trace in the field from a map point 221 void addTrace( QgsPointXY startPoint ); 222 223 //! Adds random traces in the field from random start points, the number of traces depends on the max filling density 224 void addRandomTraces(); 225 226 //! Adds a trace in the field from one random start point 227 void addRandomTrace(); 228 229 //! Adds traces in the field from gridded start points, pixelSpace is the space between points in pixel field 230 void addGriddedTraces( int dx, int dy ); 231 232 //! Adds traces in the field from vertex on a mesh 233 void addTracesOnMesh( const QgsTriangularMesh &mesh, const QgsRectangle &extent ); 234 235 //! Sets the resolution of the field 236 void setResolution( int width ); 237 238 //! Returns the width of particle 239 int resolution() const; 240 241 //! Returns the size of the image that represents the trace field 242 QSize imageSize() const; 243 244 //! Returns the current render image of the field 245 virtual QImage image(); 246 247 //! Sets the maximum pixel filling, eg, the rate of number pixel that can be filled with way. 248 void setPixelFillingDensity( double maxFilling ); 249 250 //! Sets color of the streamlines 251 void setColor( QColor color ); 252 253 //! Sets line width of the streamlines (in px) 254 void setLineWidth( double width ); 255 256 //! Sets min/max filter 257 void setFilter( double min, double max ); 258 259 //! Sets if the size of the field has to be minimized of all the mesh is in the device 260 void setMinimizeFieldSize( bool minimizeFieldSize ); 261 262 //! Assignment operator 263 QgsMeshStreamField &operator=( const QgsMeshStreamField &other ); 264 265 protected: 266 void initImage(); 267 QPointF fieldToDevice( const QPoint &pixel ) const; 268 bool filterMag( double value ) const; 269 270 private: 271 QgsPointXY positionToMapCoordinates( const QPoint &pixelPosition, const QgsPointXY &positionInPixel ); 272 bool addPixelToChunkTrace( QPoint &pixel, 273 QgsMeshStreamField::FieldData &data, 274 std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace ); 275 void setChunkTrace( std::list<QPair<QPoint, FieldData>> &chunkTrace ); 276 virtual void drawChunkTrace( const std::list<QPair<QPoint, FieldData>> &chunkTrace ) = 0; 277 void clearChunkTrace( std::list<QPair<QPoint, FieldData>> &chunkTrace ); 278 virtual void storeInField( const QPair<QPoint, FieldData> pixelData ) = 0; 279 virtual void initField() = 0; 280 void simplifyChunkTrace( std::list<QPair<QPoint, FieldData>> &shunkTrace ); 281 282 virtual bool isTraceExists( const QPoint &pixel ) const = 0; 283 bool isTraceOutside( const QPoint &pixel ) const; 284 285 protected: 286 287 QSize mFieldSize; 288 std::unique_ptr<QPainter> mPainter = std::unique_ptr<QPainter>( nullptr ); 289 int mFieldResolution = 1; 290 QPen mPen; 291 QImage mTraceImage; 292 293 QgsMapToPixel mMapToFieldPixel; 294 QgsInterpolatedLineColor mVectorColoring; 295 296 private: 297 int mPixelFillingCount = 0; 298 int mMaxPixelFillingCount = 0; 299 std::unique_ptr<QgsMeshVectorValueInterpolator> mVectorValueInterpolator; 300 QgsRectangle mLayerExtent; 301 QgsRectangle mMapExtent; 302 QPoint mFieldTopLeftInDeviceCoordinates; 303 bool mValid = false; 304 double mMaximumMagnitude = 0; 305 double mPixelFillingDensity = 0; 306 double mMinMagFilter = -1; 307 double mMaxMagFilter = -1; 308 const QgsRenderContext &mRenderContext; //keep the renderer context only to know if the renderer is stopped 309 bool mMinimizeFieldSize = true; // 310 }; 311 312 /** 313 * \ingroup core 314 * 315 * \brief Class used to draw streamlines from vector field 316 * 317 * \note not available in Python bindings 318 * \since QGIS 3.12 319 */ 320 class QgsMeshStreamlinesField: public QgsMeshStreamField 321 { 322 public: 323 //! Constructor 324 QgsMeshStreamlinesField( const QgsTriangularMesh &triangularMesh, 325 const QgsMeshDataBlock &datasetVectorValues, 326 const QgsMeshDataBlock &scalarActiveFaceFlagValues, 327 const QgsRectangle &layerExtent, 328 double magMax, 329 bool dataIsOnVertices, 330 QgsRenderContext &rendererContext, 331 const QgsInterpolatedLineColor vectorColoring ); 332 333 //! Copy constructor 334 QgsMeshStreamlinesField( const QgsMeshStreamlinesField &other ); 335 336 //! Assignment operator 337 QgsMeshStreamlinesField &operator=( const QgsMeshStreamlinesField &other ); 338 339 private: 340 void storeInField( const QPair<QPoint, FieldData> pixelData ) override; 341 void initField() override; 342 bool isTraceExists( const QPoint &pixel ) const override; 343 void drawChunkTrace( const std::list<QPair<QPoint, FieldData> > &chunkTrace ) override; 344 345 QVector<bool> mField; 346 347 }; 348 349 class QgsMeshParticleTracesField; 350 351 /** 352 * \ingroup core 353 * 354 * \brief Used to simulation moving particle 355 * 356 * \note not available in Python bindings 357 * \since QGIS 3.12 358 */ 359 struct QgsMeshTraceParticle 360 { 361 double lifeTime = 0; 362 QPoint position; 363 std::list<QPoint> tail; 364 double remainingTime = 0; //time remaining to spend in the current pixel at the end of the time step 365 }; 366 367 /** 368 * \ingroup core 369 * 370 * \brief Class used to draw streamlines from vector field 371 * 372 * \note not available in Python bindings 373 * \since QGIS 3.12 374 */ 375 class QgsMeshParticleTracesField: public QgsMeshStreamField 376 { 377 public: 378 //! Constructor 379 QgsMeshParticleTracesField( const QgsTriangularMesh &triangularMesh, 380 const QgsMeshDataBlock &datasetVectorValues, 381 const QgsMeshDataBlock &scalarActiveFaceFlagValues, 382 const QgsRectangle &layerExtent, 383 double magMax, 384 bool dataIsOnVertices, 385 const QgsRenderContext &rendererContext, 386 const QgsInterpolatedLineColor vectorColoring ); 387 388 //! Copy constructor 389 QgsMeshParticleTracesField( const QgsMeshParticleTracesField &other ); 390 391 //! Adds a particle in the vector field from a start point (pixel) with a specified life time 392 void addParticle( const QPoint &startPoint, double lifeTime ); 393 394 //! Adds a particle in the vector field from a start point (map point) with a specified life time 395 void addParticleXY( const QgsPointXY &startPoint, double lifeTime ); 396 397 //! Adds particle randomly (position and life time 398 void addRandomParticles(); 399 400 //! Moves all the particles with a displacement corresponding to a nondimensional time 401 void moveParticles(); 402 403 //! Returns the current image of the particles 404 QImage imageRendered() const; 405 406 //! Sets the total number of particles generated randomly 407 void setParticlesCount( int particlesCount ); 408 409 //! Sets the maximum life time (nondimensional) of particle generated 410 void setParticlesLifeTime( double particlesLifeTime ); 411 412 //! Stumps particles image and leave a persistent effect 413 void stump(); 414 415 /** 416 * Sets stump factor from 0 to 255 : 417 * 0, stump completely, no persistence 418 * 255, no stump, total persistence 419 */ 420 void setStumpFactor( int sf ); 421 422 //! Sets the time step 423 void setTimeStep( double timeStep ); 424 425 //! Sets particles size (in px) 426 void setParticleSize( double particleSize ); 427 428 //! Sets the tail factor 429 void setTailFactor( double tailFactor ); 430 431 //! Sets the minimum tail length 432 void setMinTailLength( int minTailLength ); 433 434 //! Assignment operator 435 QgsMeshParticleTracesField &operator=( const QgsMeshParticleTracesField &other ); 436 437 //! Sets if the particle has to be stumped dependiong on liketime 438 void setStumpParticleWithLifeTime( bool stumpParticleWithLifeTime ); 439 440 //! Sets the color of the particles, overwrite the color provided by vector settings 441 void setParticlesColor( const QColor &c ); 442 private: 443 QPoint direction( QPoint position ) const; 444 445 float time( QPoint position ) const; 446 float magnitude( QPoint position ) const; 447 448 void drawParticleTrace( const QgsMeshTraceParticle &particle ); 449 450 void storeInField( const QPair<QPoint, FieldData> pixelData ) override; 451 void initField() override; 452 bool isTraceExists( const QPoint &pixel ) const override; drawChunkTrace(const std::list<QPair<QPoint,FieldData>> & chunkTrace)453 void drawChunkTrace( const std::list<QPair<QPoint, FieldData>> &chunkTrace ) override {Q_UNUSED( chunkTrace )} 454 455 /* Nondimensional time 456 * This field store the time spent by the particle in the pixel 457 * 458 * This time is nondimensional and value 1 is equivalent to the time spent by the particle in a pixel 459 * for Vmax, the maximum magnitude of the vector field. 460 * 461 */ 462 QVector<float> mTimeField; 463 QVector<float> mMagnitudeField; 464 465 /*the direction for a pixel is defined with a char value 466 * 467 * 1 2 3 468 * 4 5 6 469 * 7 8 9 470 * 471 * convenient to retrieve the indexes of the next pixel from the direction d: 472 * Xnext= (d-1)%3-1 473 * Ynext = (d-1)/3-1 474 * 475 * and the direction is defined by : 476 * d=incX + 2 + (incY+1)*3 477 */ 478 QVector<char> mDirectionField; 479 QList<QgsMeshTraceParticle> mParticles; 480 QImage mStumpImage; 481 482 double mTimeStep = 200; 483 double mParticlesLifeTime = 5000; 484 int mParticlesCount = 1000; 485 double mTailFactor = 5; 486 int mMinTailLength = 3; 487 QColor mParticleColor = Qt::white; 488 double mParticleSize = 2.5; 489 int mStumpFactor = 50; 490 bool mStumpParticleWithLifeTime = true; 491 }; 492 493 /** 494 * \ingroup core 495 * 496 * \brief A class derived from QgsMeshVectorRenderer used to render the particles traces 497 * 498 * Not available for data defined on edges 499 * 500 * \note not available in Python bindings 501 * \since QGIS 3.12 502 */ 503 class QgsMeshVectorStreamlineRenderer: public QgsMeshVectorRenderer 504 { 505 public: 506 //!Constructor 507 QgsMeshVectorStreamlineRenderer( const QgsTriangularMesh &triangularMesh, 508 const QgsMeshDataBlock &dataSetVectorValues, 509 const QgsMeshDataBlock &scalarActiveFaceFlagValues, 510 bool dataIsOnVertices, 511 const QgsMeshRendererVectorSettings &settings, 512 QgsRenderContext &rendererContext, 513 const QgsRectangle &layerExtent, 514 double magMax ); 515 516 void draw() override; 517 518 private: 519 std::unique_ptr<QgsMeshStreamField> mStreamlineField; 520 QgsRenderContext &mRendererContext; 521 }; 522 523 524 /** 525 * \ingroup core 526 * 527 * \brief A class derived from QgsMeshVectorRenderer used to render the particles traces. 528 * 529 * Not available for data defined on edges 530 * 531 * \note not available in Python bindings 532 * \since QGIS 3.12 533 */ 534 class QgsMeshVectorTraceRenderer: public QgsMeshVectorRenderer 535 { 536 public: 537 //!Constructor 538 QgsMeshVectorTraceRenderer( const QgsTriangularMesh &triangularMesh, 539 const QgsMeshDataBlock &dataSetVectorValues, 540 const QgsMeshDataBlock &scalarActiveFaceFlagValues, 541 bool dataIsOnVertices, 542 const QgsMeshRendererVectorSettings &settings, 543 QgsRenderContext &rendererContext, 544 const QgsRectangle &layerExtent, 545 double magMax ); 546 547 void draw() override; 548 549 private: 550 std::unique_ptr<QgsMeshParticleTracesField> mParticleField; 551 QgsRenderContext &mRendererContext; 552 }; 553 554 555 #endif //SIP_RUN 556 557 ///@endcond 558 559 /** 560 * \ingroup core 561 * 562 * \brief A wrapper for QgsMeshParticuleTracesField used to render the particles. Available for Python binding 563 * 564 * \since QGIS 3.12 565 */ 566 class CORE_EXPORT QgsMeshVectorTraceAnimationGenerator 567 { 568 public: 569 //!Constructor to use from QgsMeshVectorRenderer 570 QgsMeshVectorTraceAnimationGenerator( const QgsTriangularMesh &triangularMesh, 571 const QgsMeshDataBlock &dataSetVectorValues, 572 const QgsMeshDataBlock &scalarActiveFaceFlagValues, 573 bool dataIsOnVertices, 574 const QgsRenderContext &rendererContext, 575 const QgsRectangle &layerExtent, 576 double magMax, 577 const QgsMeshRendererVectorSettings &vectorSettings ) SIP_SKIP; 578 579 //!Constructor to use with Python binding 580 QgsMeshVectorTraceAnimationGenerator( QgsMeshLayer *layer, const QgsRenderContext &rendererContext ); 581 582 //! Copy constructor 583 QgsMeshVectorTraceAnimationGenerator( const QgsMeshVectorTraceAnimationGenerator &other ); 584 585 //! Destructor 586 ~QgsMeshVectorTraceAnimationGenerator() = default; 587 588 //! seeds particles in the vector fields 589 void seedRandomParticles( int count ); 590 591 //! Moves all the particles using frame per second (fps) to calculate the displacement and return the rendered frame 592 QImage imageRendered(); 593 594 //! Sets the number of frames per seconds that will be rendered 595 void setFPS( int FPS ); 596 597 //! Sets the max number of pixels that can be go through by the particles in 1 second 598 void setMaxSpeedPixel( int max ); 599 600 //! Sets maximum life time of particles in seconds 601 void setParticlesLifeTime( double particleLifeTime ); 602 603 //! Sets colors of particle 604 void setParticlesColor( const QColor &c ); 605 606 //! Sets particle size in px 607 void setParticlesSize( double width ); 608 609 //! Sets the tail factor, used to adjust the length of the tail. 0 : minimum length, >1 increase the tail 610 void setTailFactor( double fct ); 611 612 //! Sets the minimum tail length 613 void setMinimumTailLength( int l ); 614 615 //! Sets the visual persistence of the tail 616 void setTailPersitence( double p ); 617 618 //! Assignment operator 619 QgsMeshVectorTraceAnimationGenerator &operator=( const QgsMeshVectorTraceAnimationGenerator &other ); 620 private: 621 std::unique_ptr<QgsMeshParticleTracesField> mParticleField; 622 const QgsRenderContext &mRendererContext; 623 int mFPS = 15; //frame per second of the output, used to calculate orher parameters of the field 624 int mVpixMax = 2000; //is the number of pixels that are going through for 1 s 625 double mParticleLifeTime = 5; 626 627 void updateFieldParameter(); 628 }; 629 630 #endif // QGSMESHTRACERENDERER_H 631