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