mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-09 00:04:30 -05:00
[FEATURE] API for rendering frames for mesh vector dataset animation (particles) (#33110)
* [FEATURE] API for rendering frames for mesh vector dataset animation (particles) Adds a renderer to generate frames that represent particle traces in a vector field of a mesh layer. The renderer cannot be chosen in the current GUI, however crayfish plugin can use API to generate avi/gif files with nice animations representing the movement of (random) particles in the mesh layer vector field.
This commit is contained in:
parent
a1002c4574
commit
6d4c995a28
@ -342,9 +342,9 @@ Represents a streamline renderer settings for vector datasets
|
|||||||
|
|
||||||
enum Symbology
|
enum Symbology
|
||||||
{
|
{
|
||||||
//! Displying vector dataset with arrows
|
//! Displaying vector dataset with arrows
|
||||||
Arrows,
|
Arrows,
|
||||||
//! Displying vector dataset with streamlines
|
//! Displaying vector dataset with streamlines
|
||||||
Streamlines
|
Streamlines
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
102
python/core/auto_generated/mesh/qgsmeshtracerenderer.sip.in
Normal file
102
python/core/auto_generated/mesh/qgsmeshtracerenderer.sip.in
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/core/mesh/qgsmeshtracerenderer.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class QgsMeshVectorTraceRenderer
|
||||||
|
{
|
||||||
|
%Docstring
|
||||||
|
|
||||||
|
A wrapper for QgsMeshParticuleTracesField used to render the particles. Available for Python binding
|
||||||
|
|
||||||
|
.. versionadded:: 3.12
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgsmeshtracerenderer.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
|
||||||
|
QgsMeshVectorTraceRenderer( QgsMeshLayer *layer, const QgsRenderContext &rendererContext );
|
||||||
|
%Docstring
|
||||||
|
Constructor to use with Python binding
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsMeshVectorTraceRenderer( const QgsMeshVectorTraceRenderer &other );
|
||||||
|
%Docstring
|
||||||
|
Copy constructor
|
||||||
|
%End
|
||||||
|
|
||||||
|
~QgsMeshVectorTraceRenderer();
|
||||||
|
|
||||||
|
void seedRandomParticles( int count );
|
||||||
|
%Docstring
|
||||||
|
seeds particles in the vector fields
|
||||||
|
%End
|
||||||
|
|
||||||
|
QImage imageRendered();
|
||||||
|
%Docstring
|
||||||
|
Moves all the particles using frame per second (fps) to calculate the displacement
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setFPS( int FPS );
|
||||||
|
%Docstring
|
||||||
|
Sets the number of frames per seconds that will be rendered
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setMaxSpeedPixel( int max );
|
||||||
|
%Docstring
|
||||||
|
Sets the max number of pixels that can be go through by the particles in 1 second
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setParticlesLifeTime( double particleLifeTime );
|
||||||
|
%Docstring
|
||||||
|
Sets maximum life time of particles in seconds
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setParticlesColor( const QColor &c );
|
||||||
|
%Docstring
|
||||||
|
Sets colors of particle
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setParticlesSize( double width );
|
||||||
|
%Docstring
|
||||||
|
Sets particle size
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setTailFactor( double fct );
|
||||||
|
%Docstring
|
||||||
|
Sets the tail factor, used to adjust the length of the tail. 0 : minimum length, >1 increase the tail
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setMinimumTailLength( int l );
|
||||||
|
%Docstring
|
||||||
|
Sets the minimum tail length
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setTailPersitence( double p );
|
||||||
|
%Docstring
|
||||||
|
Sets the visual persistence of the tail
|
||||||
|
%End
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/core/mesh/qgsmeshtracerenderer.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||||
|
************************************************************************/
|
||||||
@ -385,6 +385,7 @@
|
|||||||
%Include auto_generated/mesh/qgsmeshrenderersettings.sip
|
%Include auto_generated/mesh/qgsmeshrenderersettings.sip
|
||||||
%Include auto_generated/mesh/qgsmeshspatialindex.sip
|
%Include auto_generated/mesh/qgsmeshspatialindex.sip
|
||||||
%Include auto_generated/mesh/qgsmeshtimesettings.sip
|
%Include auto_generated/mesh/qgsmeshtimesettings.sip
|
||||||
|
%Include auto_generated/mesh/qgsmeshtracerenderer.sip
|
||||||
%Include auto_generated/metadata/qgsabstractmetadatabase.sip
|
%Include auto_generated/metadata/qgsabstractmetadatabase.sip
|
||||||
%Include auto_generated/metadata/qgslayermetadata.sip
|
%Include auto_generated/metadata/qgslayermetadata.sip
|
||||||
%Include auto_generated/metadata/qgslayermetadataformatter.sip
|
%Include auto_generated/metadata/qgslayermetadataformatter.sip
|
||||||
|
|||||||
@ -323,9 +323,9 @@ class CORE_EXPORT QgsMeshRendererVectorSettings
|
|||||||
*/
|
*/
|
||||||
enum Symbology
|
enum Symbology
|
||||||
{
|
{
|
||||||
//! Displying vector dataset with arrows
|
//! Displaying vector dataset with arrows
|
||||||
Arrows = 0,
|
Arrows = 0,
|
||||||
//! Displying vector dataset with streamlines
|
//! Displaying vector dataset with streamlines
|
||||||
Streamlines
|
Streamlines
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include "qgsmeshtracerenderer.h"
|
#include "qgsmeshtracerenderer.h"
|
||||||
|
#include "qgsmeshlayerrenderer.h"
|
||||||
///@cond PRIVATE
|
///@cond PRIVATE
|
||||||
|
|
||||||
QgsVector QgsMeshVectorValueInterpolator::vectorValue( const QgsPointXY &point ) const
|
QgsVector QgsMeshVectorValueInterpolator::vectorValue( const QgsPointXY &point ) const
|
||||||
@ -50,18 +50,50 @@ QgsVector QgsMeshVectorValueInterpolator::vectorValue( const QgsPointXY &point )
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsMeshVectorValueInterpolatorFromVertex::QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &datasetVectorValues ):
|
QgsMeshVectorValueInterpolator &QgsMeshVectorValueInterpolator::operator=( const QgsMeshVectorValueInterpolator &other )
|
||||||
|
{
|
||||||
|
mTriangularMesh = other.mTriangularMesh;
|
||||||
|
mDatasetValues = other.mDatasetValues;
|
||||||
|
mActiveFaceFlagValues = other.mActiveFaceFlagValues;
|
||||||
|
mFaceCache = other.mFaceCache;
|
||||||
|
mCacheFaceIndex = other.mCacheFaceIndex;
|
||||||
|
mUseScalarActiveFaceFlagValues = other.mUseScalarActiveFaceFlagValues;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshVectorValueInterpolatorFromVertex::
|
||||||
|
QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &datasetVectorValues ):
|
||||||
QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
|
QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsMeshVectorValueInterpolatorFromVertex::QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &datasetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues ):
|
QgsMeshVectorValueInterpolatorFromVertex::
|
||||||
|
QgsMeshVectorValueInterpolatorFromVertex( const QgsTriangularMesh &triangularMesh,
|
||||||
|
const QgsMeshDataBlock &datasetVectorValues,
|
||||||
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues ):
|
||||||
QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
|
QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsMeshVectorValueInterpolatorFromVertex::QgsMeshVectorValueInterpolatorFromVertex( const QgsMeshVectorValueInterpolatorFromVertex &other ):
|
||||||
|
QgsMeshVectorValueInterpolator( other )
|
||||||
|
{}
|
||||||
|
|
||||||
|
QgsMeshVectorValueInterpolatorFromVertex *QgsMeshVectorValueInterpolatorFromVertex::clone()
|
||||||
|
{
|
||||||
|
return new QgsMeshVectorValueInterpolatorFromVertex( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshVectorValueInterpolatorFromVertex &QgsMeshVectorValueInterpolatorFromVertex::
|
||||||
|
operator=( const QgsMeshVectorValueInterpolatorFromVertex &other )
|
||||||
|
{
|
||||||
|
QgsMeshVectorValueInterpolator::operator=( other );
|
||||||
|
return ( *this );
|
||||||
|
}
|
||||||
|
|
||||||
QgsVector QgsMeshVectorValueInterpolatorFromVertex::interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const
|
QgsVector QgsMeshVectorValueInterpolatorFromVertex::interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const
|
||||||
{
|
{
|
||||||
QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
|
QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
|
||||||
@ -105,6 +137,15 @@ QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator( const QgsTriangu
|
|||||||
mUseScalarActiveFaceFlagValues( true )
|
mUseScalarActiveFaceFlagValues( true )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator( const QgsMeshVectorValueInterpolator &other ):
|
||||||
|
mTriangularMesh( other.mTriangularMesh ),
|
||||||
|
mDatasetValues( other.mDatasetValues ),
|
||||||
|
mActiveFaceFlagValues( other.mActiveFaceFlagValues ),
|
||||||
|
mFaceCache( other.mFaceCache ),
|
||||||
|
mCacheFaceIndex( other.mCacheFaceIndex ),
|
||||||
|
mUseScalarActiveFaceFlagValues( other.mUseScalarActiveFaceFlagValues )
|
||||||
|
{}
|
||||||
|
|
||||||
void QgsMeshVectorValueInterpolator::updateCacheFaceIndex( const QgsPointXY &point ) const
|
void QgsMeshVectorValueInterpolator::updateCacheFaceIndex( const QgsPointXY &point ) const
|
||||||
{
|
{
|
||||||
if ( ! QgsMeshUtils::isInTriangleFace( point, mFaceCache, mTriangularMesh.vertices() ) )
|
if ( ! QgsMeshUtils::isInTriangleFace( point, mFaceCache, mTriangularMesh.vertices() ) )
|
||||||
@ -148,10 +189,10 @@ QgsPointXY QgsMeshStreamField::positionToMapCoordinates( const QPoint &pixelPosi
|
|||||||
return mapPoint;
|
return mapPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsMeshStreamField::QgsMeshStreamField( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &dataSetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues, const QgsRectangle &layerExtent, double magMax, bool dataIsOnVertices, QgsRenderContext &rendererContext, int resolution ):
|
QgsMeshStreamField::QgsMeshStreamField( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &dataSetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues, const QgsRectangle &layerExtent, double magnitudeMaximum, bool dataIsOnVertices, const QgsRenderContext &rendererContext, int resolution ):
|
||||||
mFieldResolution( resolution ),
|
mFieldResolution( resolution ),
|
||||||
mLayerExtent( layerExtent ),
|
mLayerExtent( layerExtent ),
|
||||||
mMagMax( magMax ),
|
mMagMax( magnitudeMaximum ),
|
||||||
mRenderContext( rendererContext )
|
mRenderContext( rendererContext )
|
||||||
{
|
{
|
||||||
if ( dataIsOnVertices )
|
if ( dataIsOnVertices )
|
||||||
@ -176,6 +217,36 @@ QgsMeshStreamField::QgsMeshStreamField( const QgsTriangularMesh &triangularMesh,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsMeshStreamField::QgsMeshStreamField( const QgsMeshStreamField &other ):
|
||||||
|
mFieldSize( other.mFieldSize ),
|
||||||
|
mFieldResolution( other.mFieldResolution ),
|
||||||
|
mPen( other.mPen ),
|
||||||
|
mTraceImage( other.mTraceImage ),
|
||||||
|
mMapToFieldPixel( other.mMapToFieldPixel ),
|
||||||
|
mPixelFillingCount( other.mPixelFillingCount ),
|
||||||
|
mMaxPixelFillingCount( other.mMaxPixelFillingCount ),
|
||||||
|
mLayerExtent( other.mLayerExtent ),
|
||||||
|
mMapExtent( other.mMapExtent ),
|
||||||
|
mFieldTopLeftInDeviceCoordinates( other.mFieldTopLeftInDeviceCoordinates ),
|
||||||
|
mValid( other.mValid ),
|
||||||
|
mMagMax( other.mMagMax ),
|
||||||
|
mPixelFillingDensity( other.mPixelFillingDensity ),
|
||||||
|
mMinMagFilter( other.mMinMagFilter ),
|
||||||
|
mMaxMagFilter( other.mMaxMagFilter ),
|
||||||
|
mRenderContext( other.mRenderContext ),
|
||||||
|
mMinimizeFieldSize( other.mMinimizeFieldSize )
|
||||||
|
{
|
||||||
|
mPainter = new QPainter( &mTraceImage );
|
||||||
|
mVectorValueInterpolator =
|
||||||
|
std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshStreamField::~QgsMeshStreamField()
|
||||||
|
{
|
||||||
|
if ( mPainter )
|
||||||
|
delete mPainter;
|
||||||
|
}
|
||||||
|
|
||||||
void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
|
void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
|
||||||
{
|
{
|
||||||
mMapExtent = renderContext.mapExtent();
|
mMapExtent = renderContext.mapExtent();
|
||||||
@ -184,7 +255,6 @@ void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
layerExtent = renderContext.coordinateTransform().transform( mLayerExtent );
|
layerExtent = renderContext.coordinateTransform().transform( mLayerExtent );
|
||||||
|
|
||||||
}
|
}
|
||||||
catch ( QgsCsException &cse )
|
catch ( QgsCsException &cse )
|
||||||
{
|
{
|
||||||
@ -192,7 +262,12 @@ void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
|
|||||||
layerExtent = mLayerExtent;
|
layerExtent = mLayerExtent;
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsRectangle interestZoneExtent = layerExtent.intersect( mMapExtent );
|
QgsRectangle interestZoneExtent;
|
||||||
|
if ( mMinimizeFieldSize )
|
||||||
|
interestZoneExtent = layerExtent.intersect( mMapExtent );
|
||||||
|
else
|
||||||
|
interestZoneExtent = mMapExtent;
|
||||||
|
|
||||||
if ( interestZoneExtent == QgsRectangle() )
|
if ( interestZoneExtent == QgsRectangle() )
|
||||||
{
|
{
|
||||||
mValid = false;
|
mValid = false;
|
||||||
@ -202,8 +277,12 @@ void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsPointXY interestZoneTopLeft = deviceMapToPixel.transform( QgsPointXY( interestZoneExtent.xMinimum(), interestZoneExtent.yMaximum() ) );
|
QgsPointXY interestZoneTopLeft;
|
||||||
QgsPointXY interestZoneBottomRight = deviceMapToPixel.transform( QgsPointXY( interestZoneExtent.xMaximum(), interestZoneExtent.yMinimum() ) );
|
QgsPointXY interestZoneBottomRight;
|
||||||
|
|
||||||
|
interestZoneTopLeft = deviceMapToPixel.transform( QgsPointXY( interestZoneExtent.xMinimum(), interestZoneExtent.yMaximum() ) );
|
||||||
|
interestZoneBottomRight = deviceMapToPixel.transform( QgsPointXY( interestZoneExtent.xMaximum(), interestZoneExtent.yMinimum() ) );
|
||||||
|
|
||||||
|
|
||||||
mFieldTopLeftInDeviceCoordinates = interestZoneTopLeft.toQPointF().toPoint();
|
mFieldTopLeftInDeviceCoordinates = interestZoneTopLeft.toQPointF().toPoint();
|
||||||
QPoint mFieldBottomRightInDeviceCoordinates = interestZoneBottomRight.toQPointF().toPoint();
|
QPoint mFieldBottomRightInDeviceCoordinates = interestZoneBottomRight.toQPointF().toPoint();
|
||||||
@ -245,11 +324,6 @@ void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
|
|||||||
fieldWidth,
|
fieldWidth,
|
||||||
fieldHeight, 0 );
|
fieldHeight, 0 );
|
||||||
|
|
||||||
mLayerPixelExtent.setTopLeft(
|
|
||||||
mMapToFieldPixel.transform( QgsPointXY( layerExtent.xMinimum(), layerExtent.yMaximum() ) ).toQPointF().toPoint() );
|
|
||||||
mLayerPixelExtent.setBottomRight(
|
|
||||||
mMapToFieldPixel.transform( QgsPointXY( layerExtent.xMaximum(), layerExtent.yMinimum() ) ).toQPointF().toPoint() );
|
|
||||||
|
|
||||||
initField();
|
initField();
|
||||||
mValid = true;
|
mValid = true;
|
||||||
}
|
}
|
||||||
@ -327,7 +401,6 @@ void QgsMeshStreamField::addTracesOnMesh( const QgsTriangularMesh &mesh, const Q
|
|||||||
void QgsMeshStreamField::addTrace( QPoint startPixel )
|
void QgsMeshStreamField::addTrace( QPoint startPixel )
|
||||||
{
|
{
|
||||||
//This is where each traces are constructed
|
//This is where each traces are constructed
|
||||||
|
|
||||||
if ( !mPainter )
|
if ( !mPainter )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -339,19 +412,18 @@ void QgsMeshStreamField::addTrace( QPoint startPixel )
|
|||||||
|
|
||||||
mPainter->setPen( mPen );
|
mPainter->setPen( mPen );
|
||||||
|
|
||||||
bool end = false;
|
|
||||||
//position in the pixelField
|
//position in the pixelField
|
||||||
double x1 = 0;
|
double x1 = 0;
|
||||||
double y1 = 0;
|
double y1 = 0;
|
||||||
|
|
||||||
std::list<QPair<QPoint, double>> chunkTrace;
|
std::list<QPair<QPoint, FieldData>> chunkTrace;
|
||||||
|
|
||||||
QPoint currentPixel = startPixel;
|
QPoint currentPixel = startPixel;
|
||||||
QgsVector vector;
|
QgsVector vector;
|
||||||
|
FieldData data;
|
||||||
|
data.time = 1;
|
||||||
|
|
||||||
float dt = 0;
|
while ( !mRenderContext.renderingStopped() )
|
||||||
|
|
||||||
while ( !end )
|
|
||||||
{
|
{
|
||||||
QgsPointXY mapPosition = positionToMapCoordinates( currentPixel, QgsPointXY( x1, y1 ) );
|
QgsPointXY mapPosition = positionToMapCoordinates( currentPixel, QgsPointXY( x1, y1 ) );
|
||||||
vector = mVectorValueInterpolator->vectorValue( mapPosition ) ;
|
vector = mVectorValueInterpolator->vectorValue( mapPosition ) ;
|
||||||
@ -359,26 +431,31 @@ void QgsMeshStreamField::addTrace( QPoint startPixel )
|
|||||||
if ( std::isnan( vector.x() ) || std::isnan( vector.y() ) )
|
if ( std::isnan( vector.x() ) || std::isnan( vector.y() ) )
|
||||||
{
|
{
|
||||||
mPixelFillingCount++;
|
mPixelFillingCount++;
|
||||||
|
setChunkTrace( chunkTrace );
|
||||||
|
drawChunkTrace( chunkTrace );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* adimensional value : Vu=2 when the particule need dt=1 to go through a pixel
|
/* nondimensional value : Vu=2 when the particle need dt=1 to go through a pixel with the mMagMax magnitude
|
||||||
* The size of the side of a pixel is 2
|
* The nondimensional size of the side of a pixel is 2
|
||||||
*/
|
*/
|
||||||
QgsVector vu = vector / mMagMax * 2;
|
QgsVector vu = vector / mMagMax * 2;
|
||||||
double mag = vector.length();
|
data.magnitude = vector.length();
|
||||||
double Vx = vu.x();
|
double Vx = vu.x();
|
||||||
double Vy = vu.y();
|
double Vy = vu.y();
|
||||||
double Vu = mag / mMagMax * 2; //nondimensional vector magnitude
|
double Vu = data.magnitude / mMagMax * 2; //nondimensional vector magnitude
|
||||||
|
|
||||||
if ( qgsDoubleNear( Vu, 0 ) )
|
if ( qgsDoubleNear( Vu, 0 ) )
|
||||||
{
|
{
|
||||||
// no trace anymore
|
// no trace anymore
|
||||||
mPixelFillingCount++;
|
addPixelToChunkTrace( currentPixel, data, chunkTrace );
|
||||||
|
simplifyChunkTrace( chunkTrace );
|
||||||
|
setChunkTrace( chunkTrace );
|
||||||
|
drawChunkTrace( chunkTrace );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//calculates where the particule will be after dt=1,
|
//calculates where the particle will be after dt=1,
|
||||||
QgsPointXY nextPosition = QgsPointXY( x1, y1 ) + vu;
|
QgsPointXY nextPosition = QgsPointXY( x1, y1 ) + vu;
|
||||||
int incX = 0;
|
int incX = 0;
|
||||||
int incY = 0;
|
int incY = 0;
|
||||||
@ -395,15 +472,21 @@ void QgsMeshStreamField::addTrace( QPoint startPixel )
|
|||||||
|
|
||||||
if ( incX != 0 || incY != 0 )
|
if ( incX != 0 || incY != 0 )
|
||||||
{
|
{
|
||||||
//the particule leave the current pixel --> store pixels, calculate where the particule is and change the current pixel
|
data.directionX = incX;
|
||||||
chunkTrace.emplace_back( currentPixel, mag );
|
data.directionY = -incY;
|
||||||
if ( chunkTrace.size() == 3 )
|
//the particule leave the current pixel --> store pixels, calculates where the particle is and change the current pixel
|
||||||
|
if ( chunkTrace.empty() )
|
||||||
{
|
{
|
||||||
simplifyChunkTrace( chunkTrace );
|
storeInField( QPair<QPoint, FieldData>( currentPixel, data ) );
|
||||||
|
}
|
||||||
|
if ( addPixelToChunkTrace( currentPixel, data, chunkTrace ) )
|
||||||
|
{
|
||||||
|
setChunkTrace( chunkTrace );
|
||||||
drawChunkTrace( chunkTrace );
|
drawChunkTrace( chunkTrace );
|
||||||
|
clearChunkTrace( chunkTrace );
|
||||||
}
|
}
|
||||||
|
|
||||||
dt = 1;
|
data.time = 1;
|
||||||
currentPixel += QPoint( incX, -incY );
|
currentPixel += QPoint( incX, -incY );
|
||||||
x1 = nextPosition.x() - 2 * incX;
|
x1 = nextPosition.x() - 2 * incX;
|
||||||
y1 = nextPosition.y() - 2 * incY;
|
y1 = nextPosition.y() - 2 * incY;
|
||||||
@ -471,11 +554,12 @@ void QgsMeshStreamField::addTrace( QPoint startPixel )
|
|||||||
double dy = y2 - y1;
|
double dy = y2 - y1;
|
||||||
double dl = sqrt( dx * dx + dy * dy );
|
double dl = sqrt( dx * dx + dy * dy );
|
||||||
|
|
||||||
dt += dl / Vu ; //adimensional time step : this the time needed to go to the border of the pixel
|
data.time += dl / Vu ; //adimensional time step : this the time needed to go to the border of the pixel
|
||||||
|
if ( data.time > 10000 ) //Guard to prevent that the particle never leave the pixel
|
||||||
if ( dt > 10000 ) //Guard to prevent that the particle never leave the pixel
|
|
||||||
{
|
{
|
||||||
mPixelFillingCount++;
|
addPixelToChunkTrace( currentPixel, data, chunkTrace );
|
||||||
|
setChunkTrace( chunkTrace );
|
||||||
|
drawChunkTrace( chunkTrace );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
x1 = x2;
|
x1 = x2;
|
||||||
@ -485,17 +569,16 @@ void QgsMeshStreamField::addTrace( QPoint startPixel )
|
|||||||
//test if the new current pixel is already defined, if yes no need to continue
|
//test if the new current pixel is already defined, if yes no need to continue
|
||||||
if ( isTraceExists( currentPixel ) )
|
if ( isTraceExists( currentPixel ) )
|
||||||
{
|
{
|
||||||
chunkTrace.emplace_back( currentPixel, mag );
|
//Set the pixel in the chunk before adding the current pixel because this pixel is already defined
|
||||||
if ( chunkTrace.size() == 3 )
|
setChunkTrace( chunkTrace );
|
||||||
simplifyChunkTrace( chunkTrace );
|
addPixelToChunkTrace( currentPixel, data, chunkTrace );
|
||||||
drawChunkTrace( chunkTrace );
|
drawChunkTrace( chunkTrace );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isTraceOutside( currentPixel ) )
|
if ( isTraceOutside( currentPixel ) )
|
||||||
{
|
{
|
||||||
if ( chunkTrace.size() == 3 )
|
setChunkTrace( chunkTrace );
|
||||||
simplifyChunkTrace( chunkTrace );
|
|
||||||
drawChunkTrace( chunkTrace );
|
drawChunkTrace( chunkTrace );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -519,7 +602,20 @@ QPointF QgsMeshStreamField::fieldToDevice( const QPoint &pixel ) const
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsMeshStreamField::initField()
|
bool QgsMeshStreamField::addPixelToChunkTrace( QPoint &pixel,
|
||||||
|
QgsMeshStreamField::FieldData &data,
|
||||||
|
std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
|
||||||
|
{
|
||||||
|
chunkTrace.emplace_back( pixel, data );
|
||||||
|
if ( chunkTrace.size() == 3 )
|
||||||
|
{
|
||||||
|
simplifyChunkTrace( chunkTrace );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshStreamlinesField::initField()
|
||||||
{
|
{
|
||||||
mField = QVector<bool>( mFieldSize.width() * mFieldSize.height(), false );
|
mField = QVector<bool>( mFieldSize.width() * mFieldSize.height(), false );
|
||||||
|
|
||||||
@ -534,53 +630,90 @@ void QgsMeshStreamField::initField()
|
|||||||
mPainter->setPen( mPen );
|
mPainter->setPen( mPen );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsMeshStreamField::storeInField( const QPoint &pixel )
|
QgsMeshStreamlinesField::QgsMeshStreamlinesField( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &datasetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues, const QgsRectangle &layerExtent, double magMax, bool dataIsOnVertices, QgsRenderContext &rendererContext ):
|
||||||
|
QgsMeshStreamField( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues, layerExtent, magMax, dataIsOnVertices, rendererContext )
|
||||||
|
{}
|
||||||
|
|
||||||
|
QgsMeshStreamlinesField::QgsMeshStreamlinesField( const QgsMeshStreamlinesField &other ):
|
||||||
|
QgsMeshStreamField( other ),
|
||||||
|
mField( other.mField )
|
||||||
|
{}
|
||||||
|
|
||||||
|
QgsMeshStreamlinesField &QgsMeshStreamlinesField::operator=( const QgsMeshStreamlinesField &other )
|
||||||
{
|
{
|
||||||
int i = pixel.x();
|
QgsMeshStreamField::operator=( other );
|
||||||
int j = pixel.y();
|
mField = other.mField;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshStreamlinesField::storeInField( const QPair<QPoint, FieldData> pixelData )
|
||||||
|
{
|
||||||
|
int i = pixelData.first.x();
|
||||||
|
int j = pixelData.first.y();
|
||||||
if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
|
if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
|
||||||
{
|
{
|
||||||
mField[j * mFieldSize.width() + i] = true;
|
mField[j * mFieldSize.width() + i] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsMeshStreamField::drawChunkTrace( std::list<QPair<QPoint, double> > &chunkTrace )
|
void QgsMeshStreamField::setChunkTrace( std::list<QPair<QPoint, FieldData> > &chunkTrace )
|
||||||
|
{
|
||||||
|
auto p = chunkTrace.begin();
|
||||||
|
while ( p != chunkTrace.end() )
|
||||||
|
{
|
||||||
|
storeInField( ( *p ) );
|
||||||
|
mPixelFillingCount++;
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshStreamlinesField::drawChunkTrace( const std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
|
||||||
{
|
{
|
||||||
auto p1 = chunkTrace.begin();
|
auto p1 = chunkTrace.begin();
|
||||||
auto p2 = p1;
|
auto p2 = p1;
|
||||||
p2++;
|
p2++;
|
||||||
while ( p2 != chunkTrace.end() )
|
while ( p2 != chunkTrace.end() )
|
||||||
{
|
{
|
||||||
storeInField( ( *p2 ).first );
|
if ( filterMag( ( *p1 ).second.magnitude ) && filterMag( ( *p2 ).second.magnitude ) )
|
||||||
if ( filterMag( ( *p1 ).second ) && filterMag( ( *p2 ).second ) )
|
|
||||||
mPainter->drawLine( fieldToDevice( ( *p1 ).first ), fieldToDevice( ( *p2 ).first ) );
|
mPainter->drawLine( fieldToDevice( ( *p1 ).first ), fieldToDevice( ( *p2 ).first ) );
|
||||||
mPixelFillingCount++;
|
|
||||||
auto p = p1;
|
|
||||||
p1++;
|
p1++;
|
||||||
p2++;
|
p2++;
|
||||||
chunkTrace.erase( p );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsMeshStreamField::simplifyChunkTrace( std::list<QPair<QPoint, double> > &chunkTrace )
|
void QgsMeshStreamField::clearChunkTrace( std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
|
||||||
{
|
{
|
||||||
Q_ASSERT( chunkTrace.size() == 3 );
|
auto one_before_end = std::prev( chunkTrace.end() );
|
||||||
|
chunkTrace.erase( chunkTrace.begin(), one_before_end );
|
||||||
QPoint p1 = chunkTrace.front().first;
|
|
||||||
auto ip2 = chunkTrace.begin();
|
|
||||||
ip2++;
|
|
||||||
QPoint p2 = ( *( ip2 ) ).first;
|
|
||||||
QPoint p3 = chunkTrace.back().first;
|
|
||||||
|
|
||||||
QPoint v1 = p1 - p2;
|
|
||||||
QPoint v2 = p2 - p3;
|
|
||||||
|
|
||||||
if ( v1.x()*v2.x() + v1.y()*v2.y() == 0 )
|
|
||||||
chunkTrace.erase( ip2 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QgsMeshStreamField::isTraceExists( const QPoint &pixel ) const
|
void QgsMeshStreamField::simplifyChunkTrace( std::list<QPair<QPoint, FieldData> > &shunkTrace )
|
||||||
|
{
|
||||||
|
if ( shunkTrace.size() != 3 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto ip3 = shunkTrace.begin();
|
||||||
|
auto ip1 = ip3++;
|
||||||
|
auto ip2 = ip3++;
|
||||||
|
|
||||||
|
while ( ip3 != shunkTrace.end() && ip2 != shunkTrace.end() )
|
||||||
|
{
|
||||||
|
QPoint v1 = ( *ip1 ).first - ( *ip2 ).first;
|
||||||
|
QPoint v2 = ( *ip2 ).first - ( *ip3 ).first;
|
||||||
|
if ( v1.x()*v2.x() + v1.y()*v2.y() == 0 )
|
||||||
|
{
|
||||||
|
( *ip1 ).second.time += ( ( *ip2 ).second.time ) / 2;
|
||||||
|
( *ip3 ).second.time += ( ( *ip2 ).second.time ) / 2;
|
||||||
|
( *ip1 ).second.directionX += ( *ip2 ).second.directionX;
|
||||||
|
( *ip1 ).second.directionY += ( *ip2 ).second.directionY;
|
||||||
|
shunkTrace.erase( ip2 );
|
||||||
|
}
|
||||||
|
ip1 = ip3++;
|
||||||
|
ip2 = ip3++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsMeshStreamlinesField::isTraceExists( const QPoint &pixel ) const
|
||||||
{
|
{
|
||||||
int i = pixel.x();
|
int i = pixel.x();
|
||||||
int j = pixel.y();
|
int j = pixel.y();
|
||||||
@ -590,7 +723,6 @@ bool QgsMeshStreamField::isTraceExists( const QPoint &pixel ) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QgsMeshStreamField::isTraceOutside( const QPoint &pixel ) const
|
bool QgsMeshStreamField::isTraceOutside( const QPoint &pixel ) const
|
||||||
@ -604,6 +736,53 @@ bool QgsMeshStreamField::isTraceOutside( const QPoint &pixel ) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QgsMeshStreamField::setMinimizeFieldSize( bool minimizeFieldSize )
|
||||||
|
{
|
||||||
|
mMinimizeFieldSize = minimizeFieldSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshStreamField &QgsMeshStreamField::operator=( const QgsMeshStreamField &other )
|
||||||
|
{
|
||||||
|
mFieldSize = other.mFieldSize ;
|
||||||
|
mFieldResolution = other.mFieldResolution;
|
||||||
|
mPen = other.mPen;
|
||||||
|
mTraceImage = other.mTraceImage ;
|
||||||
|
mMapToFieldPixel = other.mMapToFieldPixel ;
|
||||||
|
mPixelFillingCount = other.mPixelFillingCount ;
|
||||||
|
mMaxPixelFillingCount = other.mMaxPixelFillingCount ;
|
||||||
|
mLayerExtent = other.mLayerExtent ;
|
||||||
|
mMapExtent = other.mMapExtent;
|
||||||
|
mFieldTopLeftInDeviceCoordinates = other.mFieldTopLeftInDeviceCoordinates ;
|
||||||
|
mValid = other.mValid ;
|
||||||
|
mMagMax = other.mMagMax ;
|
||||||
|
mPixelFillingDensity = other.mPixelFillingDensity ;
|
||||||
|
mMinMagFilter = other.mMinMagFilter ;
|
||||||
|
mMaxMagFilter = other.mMaxMagFilter ;
|
||||||
|
mMinimizeFieldSize = other.mMinimizeFieldSize ;
|
||||||
|
mPainter = new QPainter( &mTraceImage );
|
||||||
|
mVectorValueInterpolator =
|
||||||
|
std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
|
||||||
|
|
||||||
|
if ( mPainter )
|
||||||
|
delete mPainter;
|
||||||
|
mPainter = new QPainter( &mTraceImage );
|
||||||
|
|
||||||
|
return ( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshStreamField::initImage()
|
||||||
|
{
|
||||||
|
if ( mPainter )
|
||||||
|
delete mPainter;
|
||||||
|
|
||||||
|
mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
|
||||||
|
mTraceImage.fill( 0X00000000 );
|
||||||
|
|
||||||
|
mPainter = new QPainter( &mTraceImage );
|
||||||
|
mPainter->setRenderHint( QPainter::Antialiasing, true );
|
||||||
|
mPainter->setPen( mPen );
|
||||||
|
}
|
||||||
|
|
||||||
bool QgsMeshStreamField::filterMag( double value ) const
|
bool QgsMeshStreamField::filterMag( double value ) const
|
||||||
{
|
{
|
||||||
return ( mMinMagFilter < 0 || value > mMinMagFilter ) && ( mMaxMagFilter < 0 || value < mMaxMagFilter );
|
return ( mMinMagFilter < 0 || value > mMinMagFilter ) && ( mMaxMagFilter < 0 || value < mMaxMagFilter );
|
||||||
@ -644,6 +823,21 @@ QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace(
|
|||||||
QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
|
QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace( const QgsMeshVectorValueInterpolatorFromFace &other ):
|
||||||
|
QgsMeshVectorValueInterpolator( other )
|
||||||
|
{}
|
||||||
|
|
||||||
|
QgsMeshVectorValueInterpolatorFromFace *QgsMeshVectorValueInterpolatorFromFace::clone()
|
||||||
|
{
|
||||||
|
return new QgsMeshVectorValueInterpolatorFromFace( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshVectorValueInterpolatorFromFace &QgsMeshVectorValueInterpolatorFromFace::operator=( const QgsMeshVectorValueInterpolatorFromFace &other )
|
||||||
|
{
|
||||||
|
QgsMeshVectorValueInterpolator::operator=( other );
|
||||||
|
return ( *this );
|
||||||
|
}
|
||||||
|
|
||||||
QgsVector QgsMeshVectorValueInterpolatorFromFace::interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const
|
QgsVector QgsMeshVectorValueInterpolatorFromFace::interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const
|
||||||
{
|
{
|
||||||
QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
|
QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
|
||||||
@ -663,10 +857,10 @@ QgsVector QgsMeshVectorValueInterpolatorFromFace::interpolatedValuePrivate( int
|
|||||||
point );
|
point );
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsMeshVectorStreamLineRenderer::QgsMeshVectorStreamLineRenderer( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &dataSetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues, bool dataIsOnVertices, const QgsMeshRendererVectorSettings &settings, QgsRenderContext &rendererContext, const QgsRectangle &layerExtent, double magMax ):
|
QgsMeshVectorStreamlineRenderer::QgsMeshVectorStreamlineRenderer( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &dataSetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues, bool dataIsOnVertices, const QgsMeshRendererVectorSettings &settings, QgsRenderContext &rendererContext, const QgsRectangle &layerExtent, double magMax ):
|
||||||
mRendererContext( rendererContext )
|
mRendererContext( rendererContext )
|
||||||
{
|
{
|
||||||
mStreamLineField.reset( new QgsMeshStreamField( triangularMesh,
|
mStreamLineField.reset( new QgsMeshStreamlinesField( triangularMesh,
|
||||||
dataSetVectorValues,
|
dataSetVectorValues,
|
||||||
scalarActiveFaceFlagValues,
|
scalarActiveFaceFlagValues,
|
||||||
layerExtent,
|
layerExtent,
|
||||||
@ -695,11 +889,470 @@ QgsMeshVectorStreamLineRenderer::QgsMeshVectorStreamLineRenderer( const QgsTrian
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsMeshVectorStreamLineRenderer::draw()
|
void QgsMeshVectorStreamlineRenderer::draw()
|
||||||
{
|
{
|
||||||
if ( mRendererContext.renderingStopped() )
|
if ( mRendererContext.renderingStopped() )
|
||||||
return;
|
return;
|
||||||
mRendererContext.painter()->drawImage( mStreamLineField->topLeft(), mStreamLineField->image() );
|
mRendererContext.painter()->drawImage( mStreamLineField->topLeft(), mStreamLineField->image() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsMeshParticleTracesField::QgsMeshParticleTracesField( const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &datasetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues, const QgsRectangle &layerExtent, double magMax, bool dataIsOnVertices, const QgsRenderContext &rendererContext ):
|
||||||
|
QgsMeshStreamField( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues, layerExtent, magMax, dataIsOnVertices, rendererContext )
|
||||||
|
{
|
||||||
|
std::srand( uint( std::time( nullptr ) ) );
|
||||||
|
mPen.setCapStyle( Qt::RoundCap );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshParticleTracesField::QgsMeshParticleTracesField( const QgsMeshParticleTracesField &other ):
|
||||||
|
QgsMeshStreamField( other ),
|
||||||
|
mTimeField( other.mTimeField ),
|
||||||
|
mDirectionField( other.mDirectionField ),
|
||||||
|
mParticles( other.mParticles ),
|
||||||
|
mStumpImage( other.mStumpImage ),
|
||||||
|
mTimeStep( other.mTimeStep ),
|
||||||
|
mParticlesLifeTime( other.mParticlesLifeTime ),
|
||||||
|
mParticlesCount( other.mParticlesCount ),
|
||||||
|
mTailFactor( other.mTailFactor ),
|
||||||
|
mParticleColor( other.mParticleColor ),
|
||||||
|
mParticleSize( other.mParticleSize ),
|
||||||
|
mStumpFactor( other.mStumpFactor )
|
||||||
|
{}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::addParticle( const QPoint &startPoint, double lifeTime )
|
||||||
|
{
|
||||||
|
addTrace( startPoint );
|
||||||
|
if ( time( startPoint ) > 0 )
|
||||||
|
{
|
||||||
|
QgsMeshTraceParticle p;
|
||||||
|
p.lifeTime = lifeTime;
|
||||||
|
p.position = startPoint;
|
||||||
|
mParticles.append( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::addParticleXY( const QgsPointXY &startPoint, double lifeTime )
|
||||||
|
{
|
||||||
|
addParticle( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint(), lifeTime );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::moveParticles()
|
||||||
|
{
|
||||||
|
stump();
|
||||||
|
for ( auto &p : mParticles )
|
||||||
|
{
|
||||||
|
double spentTime = p.remainingTime; //adjust with the past remaining time
|
||||||
|
size_t countAdded = 0;
|
||||||
|
while ( spentTime < mTimeStep && p.lifeTime > 0 )
|
||||||
|
{
|
||||||
|
double timeToSpend = double( time( p.position ) );
|
||||||
|
if ( timeToSpend > 0 )
|
||||||
|
{
|
||||||
|
p.lifeTime -= timeToSpend;
|
||||||
|
spentTime += timeToSpend;
|
||||||
|
QPoint dir = direction( p.position );
|
||||||
|
if ( p.lifeTime > 0 )
|
||||||
|
{
|
||||||
|
p.position += dir;
|
||||||
|
p.tail.emplace_back( p.position );
|
||||||
|
countAdded++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p.lifeTime = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( p.lifeTime <= 0 )
|
||||||
|
{
|
||||||
|
// the particle is not alive anymore
|
||||||
|
p.lifeTime = 0;
|
||||||
|
p.tail.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p.remainingTime = spentTime - mTimeStep;
|
||||||
|
while ( int( p.tail.size() ) > mMinTailLength && p.tail.size() > countAdded * mTailFactor )
|
||||||
|
p.tail.erase( p.tail.begin() );
|
||||||
|
drawParticleTrace( p );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove empty (dead particles)
|
||||||
|
int i = 0;
|
||||||
|
while ( i < mParticles.count() )
|
||||||
|
{
|
||||||
|
if ( mParticles.at( i ).tail.size() == 0 )
|
||||||
|
mParticles.removeAt( i );
|
||||||
|
else
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
//add new particles if needed
|
||||||
|
if ( mParticles.count() < mParticlesCount )
|
||||||
|
addRandomParticles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::addRandomParticles()
|
||||||
|
{
|
||||||
|
if ( !isValid() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int count = mParticlesCount - mParticles.count();
|
||||||
|
|
||||||
|
for ( int i = 0; i < count; ++i )
|
||||||
|
{
|
||||||
|
int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
|
||||||
|
int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
|
||||||
|
double lifeTime = ( std::rand() / ( ( RAND_MAX + 1u ) / mParticlesLifeTime ) );
|
||||||
|
addParticle( QPoint( xRandom, yRandom ), lifeTime );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::storeInField( const QPair<QPoint, QgsMeshStreamField::FieldData> pixelData )
|
||||||
|
{
|
||||||
|
int i = pixelData.first.x();
|
||||||
|
int j = pixelData.first.y();
|
||||||
|
if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
|
||||||
|
{
|
||||||
|
mTimeField[j * mFieldSize.width() + i] = pixelData.second.time;
|
||||||
|
int d = pixelData.second.directionX + 2 + ( pixelData.second.directionY + 1 ) * 3;
|
||||||
|
mDirectionField[j * mFieldSize.width() + i] = static_cast<char>( d );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::initField()
|
||||||
|
{
|
||||||
|
mTimeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), -1 );
|
||||||
|
mDirectionField = QVector<char>( mFieldSize.width() * mFieldSize.height(), static_cast<char>( int( 0 ) ) );
|
||||||
|
initImage();
|
||||||
|
mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
|
||||||
|
mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) ); //alpha=0 -> no persitence, alpha=255 -> total persistence
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsMeshParticleTracesField::isTraceExists( const QPoint &pixel ) const
|
||||||
|
{
|
||||||
|
int i = pixel.x();
|
||||||
|
int j = pixel.y();
|
||||||
|
if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
|
||||||
|
{
|
||||||
|
return mTimeField[j * mFieldSize.width() + i] >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::setMinTailLength( int minTailLength )
|
||||||
|
{
|
||||||
|
mMinTailLength = minTailLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshParticleTracesField &QgsMeshParticleTracesField::operator=( const QgsMeshParticleTracesField &other )
|
||||||
|
{
|
||||||
|
QgsMeshStreamField::operator=( other );
|
||||||
|
mTimeField = other.mTimeField;
|
||||||
|
mDirectionField = other.mDirectionField;
|
||||||
|
mParticles = other.mParticles;
|
||||||
|
mStumpImage = other.mStumpImage;
|
||||||
|
mTimeStep = other.mTimeStep;
|
||||||
|
mParticlesLifeTime = other.mParticlesLifeTime;
|
||||||
|
mParticlesCount = other.mParticlesCount;
|
||||||
|
mTailFactor = other.mTailFactor;
|
||||||
|
mParticleColor = other.mParticleColor;
|
||||||
|
mParticleSize = other.mParticleSize;
|
||||||
|
mStumpFactor = other.mStumpFactor;
|
||||||
|
|
||||||
|
return ( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::setTailFactor( double tailFactor )
|
||||||
|
{
|
||||||
|
mTailFactor = tailFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::setParticleSize( double particleSize )
|
||||||
|
{
|
||||||
|
mParticleSize = particleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::setParticleColor( const QColor &particleColor )
|
||||||
|
{
|
||||||
|
mParticleColor = particleColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::setTimeStep( double timeStep )
|
||||||
|
{
|
||||||
|
mTimeStep = timeStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::setParticlesLifeTime( double particlesLifeTime )
|
||||||
|
{
|
||||||
|
mParticlesLifeTime = particlesLifeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage QgsMeshParticleTracesField::imageRendered() const
|
||||||
|
{
|
||||||
|
return mTraceImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::stump()
|
||||||
|
{
|
||||||
|
mPainter->save();
|
||||||
|
mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
|
||||||
|
mPainter->drawImage( QPoint( 0, 0 ), mStumpImage );
|
||||||
|
mPainter->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::setStumpFactor( int sf )
|
||||||
|
{
|
||||||
|
mStumpFactor = sf;
|
||||||
|
mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
|
||||||
|
mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint QgsMeshParticleTracesField::direction( QPoint position ) const
|
||||||
|
{
|
||||||
|
int i = position.x();
|
||||||
|
int j = position.y();
|
||||||
|
if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
|
||||||
|
{
|
||||||
|
int dir = static_cast<int>( mDirectionField[j * mFieldSize.width() + i] );
|
||||||
|
if ( dir != 0 && dir < 10 )
|
||||||
|
return QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
|
||||||
|
}
|
||||||
|
return QPoint( 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
float QgsMeshParticleTracesField::time( QPoint position ) const
|
||||||
|
{
|
||||||
|
int i = position.x();
|
||||||
|
int j = position.y();
|
||||||
|
if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
|
||||||
|
{
|
||||||
|
return mTimeField[j * mFieldSize.width() + i];
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::drawParticleTrace( const QgsMeshTraceParticle &particle )
|
||||||
|
{
|
||||||
|
const std::list<QPoint> &tail = particle.tail;
|
||||||
|
if ( tail.size() == 0 )
|
||||||
|
return;
|
||||||
|
double iniWidth = mParticleSize;
|
||||||
|
double finWidth = 0;
|
||||||
|
|
||||||
|
size_t pixelCount = tail.size();
|
||||||
|
QColor traceColor = mParticleColor;
|
||||||
|
double transparency = sin( M_PI * particle.lifeTime / mParticlesLifeTime );
|
||||||
|
traceColor.setAlphaF( transparency );
|
||||||
|
mPen.setColor( traceColor );
|
||||||
|
double dw;
|
||||||
|
if ( pixelCount > 1 )
|
||||||
|
dw = ( iniWidth - finWidth ) / ( pixelCount );
|
||||||
|
else
|
||||||
|
dw = 0;
|
||||||
|
|
||||||
|
auto ip1 = std::prev( tail.end() );
|
||||||
|
auto ip2 = std::prev( ip1 );
|
||||||
|
int i = 0;
|
||||||
|
while ( ip1 != tail.begin() )
|
||||||
|
{
|
||||||
|
QPointF p1 = fieldToDevice( ( *ip1 ) );
|
||||||
|
QPointF p2 = fieldToDevice( ( *ip2 ) );
|
||||||
|
mPen.setWidthF( iniWidth - i * dw );
|
||||||
|
mPainter->setPen( mPen );
|
||||||
|
mPainter->drawLine( p1, p2 );
|
||||||
|
ip1--;
|
||||||
|
ip2--;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshParticleTracesField::setParticlesCount( int particlesCount )
|
||||||
|
{
|
||||||
|
mParticlesCount = particlesCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshVectorTraceRenderer::QgsMeshVectorTraceRenderer(
|
||||||
|
const QgsTriangularMesh &triangularMesh,
|
||||||
|
const QgsMeshDataBlock &dataSetVectorValues,
|
||||||
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues,
|
||||||
|
bool dataIsOnVertices,
|
||||||
|
const QgsRenderContext &rendererContext,
|
||||||
|
const QgsRectangle &layerExtent, double magMax ):
|
||||||
|
mRendererContext( rendererContext )
|
||||||
|
{
|
||||||
|
mParticleField = std::unique_ptr<QgsMeshParticleTracesField>( new QgsMeshParticleTracesField( triangularMesh,
|
||||||
|
dataSetVectorValues,
|
||||||
|
scalarActiveFaceFlagValues,
|
||||||
|
layerExtent,
|
||||||
|
magMax,
|
||||||
|
dataIsOnVertices,
|
||||||
|
rendererContext ) ) ;
|
||||||
|
mParticleField->updateSize( rendererContext ) ;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshVectorTraceRenderer::QgsMeshVectorTraceRenderer( QgsMeshLayer *layer, const QgsRenderContext &rendererContext ):
|
||||||
|
mRendererContext( rendererContext )
|
||||||
|
{
|
||||||
|
if ( !layer->triangularMesh() )
|
||||||
|
layer->reload();
|
||||||
|
|
||||||
|
QgsMeshDataBlock vectorDatasetValues;
|
||||||
|
QgsMeshDataBlock scalarActiveFaceFlagValues;
|
||||||
|
bool vectorDataOnVertices;
|
||||||
|
double magMax;
|
||||||
|
|
||||||
|
// Find out if we can use cache up to date. If yes, use it and return
|
||||||
|
const int datasetGroupCount = layer->dataProvider()->datasetGroupCount();
|
||||||
|
QgsMeshDatasetIndex datasetIndex = layer->rendererSettings().activeVectorDataset();
|
||||||
|
QgsMeshLayerRendererCache *cache = layer->rendererCache();
|
||||||
|
|
||||||
|
if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
|
||||||
|
( cache->mActiveVectorDatasetIndex == datasetIndex ) )
|
||||||
|
{
|
||||||
|
vectorDatasetValues = cache->mVectorDatasetValues;
|
||||||
|
scalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
|
||||||
|
magMax = cache->mVectorDatasetMagMaximum;
|
||||||
|
vectorDataOnVertices = cache->mVectorDataOnVertices;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const QgsMeshDatasetGroupMetadata metadata =
|
||||||
|
layer->dataProvider()->datasetGroupMetadata( layer->rendererSettings().activeVectorDataset() );
|
||||||
|
magMax = metadata.maximum();
|
||||||
|
vectorDataOnVertices = metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
|
||||||
|
|
||||||
|
int count;
|
||||||
|
if ( vectorDataOnVertices )
|
||||||
|
count = layer->nativeMesh()->vertices.count();
|
||||||
|
else
|
||||||
|
count = layer->nativeMesh()->faces.count();
|
||||||
|
|
||||||
|
vectorDatasetValues = layer->dataProvider()->datasetValues(
|
||||||
|
datasetIndex,
|
||||||
|
0,
|
||||||
|
count );
|
||||||
|
|
||||||
|
scalarActiveFaceFlagValues = layer->dataProvider()->areFacesActive(
|
||||||
|
datasetIndex,
|
||||||
|
0,
|
||||||
|
layer->nativeMesh()->faces.count() );
|
||||||
|
}
|
||||||
|
|
||||||
|
mParticleField = std::unique_ptr<QgsMeshParticleTracesField>( new QgsMeshParticleTracesField( ( *layer->triangularMesh() ),
|
||||||
|
vectorDatasetValues,
|
||||||
|
scalarActiveFaceFlagValues,
|
||||||
|
layer->extent(),
|
||||||
|
magMax,
|
||||||
|
vectorDataOnVertices,
|
||||||
|
rendererContext ) ) ;
|
||||||
|
|
||||||
|
mParticleField->setMinimizeFieldSize( false );
|
||||||
|
mParticleField->updateSize( mRendererContext );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshVectorTraceRenderer::QgsMeshVectorTraceRenderer( const QgsMeshVectorTraceRenderer &other ):
|
||||||
|
mRendererContext( other.mRendererContext ),
|
||||||
|
mFPS( other.mFPS ),
|
||||||
|
mVpixMax( other.mVpixMax ),
|
||||||
|
mParticleLifeTime( other.mParticleLifeTime )
|
||||||
|
{
|
||||||
|
mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
|
||||||
|
new QgsMeshParticleTracesField( *other.mParticleField ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshVectorTraceRenderer::~QgsMeshVectorTraceRenderer() = default;
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::seedRandomParticles( int count )
|
||||||
|
{
|
||||||
|
mParticleField->setParticlesCount( count );
|
||||||
|
mParticleField->addRandomParticles();
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage QgsMeshVectorTraceRenderer::imageRendered()
|
||||||
|
{
|
||||||
|
mParticleField->moveParticles();
|
||||||
|
return mParticleField->image();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::setFPS( int FPS )
|
||||||
|
{
|
||||||
|
if ( FPS > 0 )
|
||||||
|
mFPS = FPS;
|
||||||
|
else
|
||||||
|
mFPS = 1;
|
||||||
|
|
||||||
|
updateFieldParameter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::setMaxSpeedPixel( int max )
|
||||||
|
{
|
||||||
|
mVpixMax = max;
|
||||||
|
updateFieldParameter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::setParticlesLifeTime( double particleLifeTime )
|
||||||
|
{
|
||||||
|
mParticleLifeTime = particleLifeTime;
|
||||||
|
updateFieldParameter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::setParticlesColor( const QColor &c )
|
||||||
|
{
|
||||||
|
mParticleField->setParticleColor( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::setParticlesSize( double width )
|
||||||
|
{
|
||||||
|
mParticleField->setParticleSize( width );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::setTailFactor( double fct )
|
||||||
|
{
|
||||||
|
mParticleField->setTailFactor( fct );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::setMinimumTailLength( int l )
|
||||||
|
{
|
||||||
|
mParticleField->setMinTailLength( l );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::setTailPersitence( double p )
|
||||||
|
{
|
||||||
|
if ( p < 0 )
|
||||||
|
p = 0;
|
||||||
|
if ( p > 1 )
|
||||||
|
p = 1;
|
||||||
|
mParticleField->setStumpFactor( int( 255 * p ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMeshVectorTraceRenderer &QgsMeshVectorTraceRenderer::operator=( const QgsMeshVectorTraceRenderer &other )
|
||||||
|
{
|
||||||
|
mParticleField.reset( new QgsMeshParticleTracesField( *mParticleField ) );
|
||||||
|
const_cast<QgsRenderContext &>( mRendererContext ) = other.mRendererContext;
|
||||||
|
mFPS = other.mFPS;
|
||||||
|
mVpixMax = other.mVpixMax;
|
||||||
|
mParticleLifeTime = other.mParticleLifeTime;
|
||||||
|
|
||||||
|
return ( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMeshVectorTraceRenderer::updateFieldParameter()
|
||||||
|
{
|
||||||
|
double fieldTimeStep = mVpixMax / mFPS;
|
||||||
|
double fieldLifeTime = mParticleLifeTime * mFPS * fieldTimeStep;
|
||||||
|
mParticleField->setTimeStep( fieldTimeStep );
|
||||||
|
mParticleField->setParticlesLifeTime( fieldLifeTime );
|
||||||
|
}
|
||||||
|
|
||||||
///@endcond
|
///@endcond
|
||||||
|
|||||||
@ -18,7 +18,6 @@
|
|||||||
#ifndef QGSMESHTRACERENDERER_H
|
#ifndef QGSMESHTRACERENDERER_H
|
||||||
#define QGSMESHTRACERENDERER_H
|
#define QGSMESHTRACERENDERER_H
|
||||||
|
|
||||||
#define SIP_NO_FILE
|
|
||||||
|
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
@ -33,6 +32,8 @@
|
|||||||
|
|
||||||
///@cond PRIVATE
|
///@cond PRIVATE
|
||||||
|
|
||||||
|
#ifndef SIP_RUN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \ingroup core
|
* \ingroup core
|
||||||
*
|
*
|
||||||
@ -52,13 +53,22 @@ class QgsMeshVectorValueInterpolator
|
|||||||
QgsMeshVectorValueInterpolator( const QgsTriangularMesh &triangularMesh,
|
QgsMeshVectorValueInterpolator( const QgsTriangularMesh &triangularMesh,
|
||||||
const QgsMeshDataBlock &datasetVectorValues,
|
const QgsMeshDataBlock &datasetVectorValues,
|
||||||
const QgsMeshDataBlock &scalarActiveFaceFlagValues );
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues );
|
||||||
|
|
||||||
|
//! Copy constructor
|
||||||
|
QgsMeshVectorValueInterpolator( const QgsMeshVectorValueInterpolator &other );
|
||||||
|
|
||||||
|
//! Clone
|
||||||
|
virtual QgsMeshVectorValueInterpolator *clone() = 0;
|
||||||
|
|
||||||
//! Destructor
|
//! Destructor
|
||||||
virtual ~QgsMeshVectorValueInterpolator() = default;
|
virtual ~QgsMeshVectorValueInterpolator() = default;
|
||||||
|
|
||||||
|
|
||||||
//! Returns the interpolated vector
|
//! Returns the interpolated vector
|
||||||
virtual QgsVector vectorValue( const QgsPointXY &point ) const;
|
virtual QgsVector vectorValue( const QgsPointXY &point ) const;
|
||||||
|
|
||||||
|
//! Assignment operator
|
||||||
|
QgsMeshVectorValueInterpolator &operator=( const QgsMeshVectorValueInterpolator &other );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateCacheFaceIndex( const QgsPointXY &point ) const;
|
void updateCacheFaceIndex( const QgsPointXY &point ) const;
|
||||||
|
|
||||||
@ -97,6 +107,15 @@ class QgsMeshVectorValueInterpolatorFromVertex: public QgsMeshVectorValueInterpo
|
|||||||
const QgsMeshDataBlock &datasetVectorValues,
|
const QgsMeshDataBlock &datasetVectorValues,
|
||||||
const QgsMeshDataBlock &scalarActiveFaceFlagValues );
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues );
|
||||||
|
|
||||||
|
//! Copy constructor
|
||||||
|
QgsMeshVectorValueInterpolatorFromVertex( const QgsMeshVectorValueInterpolatorFromVertex &other );
|
||||||
|
|
||||||
|
//! Clone the instance
|
||||||
|
virtual QgsMeshVectorValueInterpolatorFromVertex *clone() override;
|
||||||
|
|
||||||
|
//! Assignment operator
|
||||||
|
QgsMeshVectorValueInterpolatorFromVertex &operator=( const QgsMeshVectorValueInterpolatorFromVertex &other );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QgsVector interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const override;
|
QgsVector interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const override;
|
||||||
};
|
};
|
||||||
@ -121,6 +140,15 @@ class QgsMeshVectorValueInterpolatorFromFace: public QgsMeshVectorValueInterpola
|
|||||||
const QgsMeshDataBlock &datasetVectorValues,
|
const QgsMeshDataBlock &datasetVectorValues,
|
||||||
const QgsMeshDataBlock &scalarActiveFaceFlagValues );
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues );
|
||||||
|
|
||||||
|
//! Copy constructor
|
||||||
|
QgsMeshVectorValueInterpolatorFromFace( const QgsMeshVectorValueInterpolatorFromFace &other );
|
||||||
|
|
||||||
|
//! Clone the instance
|
||||||
|
virtual QgsMeshVectorValueInterpolatorFromFace *clone() override;
|
||||||
|
|
||||||
|
//! Assignment operator
|
||||||
|
QgsMeshVectorValueInterpolatorFromFace &operator=( const QgsMeshVectorValueInterpolatorFromFace &other );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QgsVector interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const override;
|
QgsVector interpolatedValuePrivate( int faceIndex, const QgsPointXY point ) const override;
|
||||||
};
|
};
|
||||||
@ -128,7 +156,7 @@ class QgsMeshVectorValueInterpolatorFromFace: public QgsMeshVectorValueInterpola
|
|||||||
/**
|
/**
|
||||||
* \ingroup core
|
* \ingroup core
|
||||||
*
|
*
|
||||||
* Abstract class used to handle information about stream field;
|
* Abstract class used to handle information about stream field
|
||||||
*
|
*
|
||||||
* \note not available in Python bindings
|
* \note not available in Python bindings
|
||||||
* \since QGIS 3.12
|
* \since QGIS 3.12
|
||||||
@ -136,22 +164,29 @@ class QgsMeshVectorValueInterpolatorFromFace: public QgsMeshVectorValueInterpola
|
|||||||
class QgsMeshStreamField
|
class QgsMeshStreamField
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct FieldData
|
||||||
|
{
|
||||||
|
double magnitude;
|
||||||
|
float time;
|
||||||
|
int directionX;
|
||||||
|
int directionY;
|
||||||
|
};
|
||||||
|
|
||||||
//! Constructor
|
//! Constructor
|
||||||
QgsMeshStreamField( const QgsTriangularMesh &triangularMesh,
|
QgsMeshStreamField( const QgsTriangularMesh &triangularMesh,
|
||||||
const QgsMeshDataBlock &dataSetVectorValues,
|
const QgsMeshDataBlock &dataSetVectorValues,
|
||||||
const QgsMeshDataBlock &scalarActiveFaceFlagValues,
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues,
|
||||||
const QgsRectangle &layerExtent,
|
const QgsRectangle &layerExtent,
|
||||||
double magMax, bool dataIsOnVertices, QgsRenderContext &rendererContext,
|
double magnitudeMaximum, bool dataIsOnVertices, const QgsRenderContext &rendererContext,
|
||||||
int resolution = 1 );
|
int resolution = 1 );
|
||||||
|
|
||||||
//! Destructor
|
//! Copy constructor
|
||||||
virtual ~QgsMeshStreamField()
|
QgsMeshStreamField( const QgsMeshStreamField &other );
|
||||||
{
|
|
||||||
if ( mPainter )
|
|
||||||
delete mPainter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
//! Destructor
|
||||||
|
virtual ~QgsMeshStreamField();
|
||||||
|
|
||||||
|
/**
|
||||||
* Updates the size of the field and the QgsMapToPixel instance to retrieve map point
|
* Updates the size of the field and the QgsMapToPixel instance to retrieve map point
|
||||||
* from pixel in the field depending on the resolution of the device
|
* from pixel in the field depending on the resolution of the device
|
||||||
* If the extent of renderer context and the resolution are not changed, do nothing
|
* If the extent of renderer context and the resolution are not changed, do nothing
|
||||||
@ -159,7 +194,7 @@ class QgsMeshStreamField
|
|||||||
*/
|
*/
|
||||||
void updateSize( const QgsRenderContext &renderContext );
|
void updateSize( const QgsRenderContext &renderContext );
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Updates the size of the field and the QgsMapToPixel instance to retrieve map point
|
* Updates the size of the field and the QgsMapToPixel instance to retrieve map point
|
||||||
* from pixel in the field depending on the resolution of the device
|
* from pixel in the field depending on the resolution of the device
|
||||||
*/
|
*/
|
||||||
@ -216,49 +251,243 @@ class QgsMeshStreamField
|
|||||||
//! Sets min/max filter
|
//! Sets min/max filter
|
||||||
void setFilter( double min, double max );
|
void setFilter( double min, double max );
|
||||||
|
|
||||||
private:
|
//! Sets if the size of the field has to be minimized of all the mesh is in the device
|
||||||
|
void setMinimizeFieldSize( bool minimizeFieldSize );
|
||||||
|
|
||||||
QgsPointXY positionToMapCoordinates( const QPoint &pixelPosition, const QgsPointXY &positionInPixel );
|
//! Assignment operator
|
||||||
|
QgsMeshStreamField &operator=( const QgsMeshStreamField &other );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void initImage();
|
||||||
QPointF fieldToDevice( const QPoint &pixel ) const;
|
QPointF fieldToDevice( const QPoint &pixel ) const;
|
||||||
void initField();
|
|
||||||
void storeInField( const QPoint &pixel );
|
|
||||||
void drawChunkTrace( std::list<QPair<QPoint, double>> &chunkTrace );
|
|
||||||
|
|
||||||
void simplifyChunkTrace( std::list<QPair<QPoint, double>> &chunkTrace );
|
|
||||||
|
|
||||||
bool isTraceExists( const QPoint &pixel ) const;
|
|
||||||
|
|
||||||
bool isTraceOutside( const QPoint &pixel ) const;
|
|
||||||
|
|
||||||
bool filterMag( double value ) const;
|
bool filterMag( double value ) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QgsPointXY positionToMapCoordinates( const QPoint &pixelPosition, const QgsPointXY &positionInPixel );
|
||||||
|
bool addPixelToChunkTrace( QPoint &pixel,
|
||||||
|
QgsMeshStreamField::FieldData &data,
|
||||||
|
std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace );
|
||||||
|
void setChunkTrace( std::list<QPair<QPoint, FieldData>> &chunkTrace );
|
||||||
|
virtual void drawChunkTrace( const std::list<QPair<QPoint, FieldData>> &chunkTrace ) = 0;
|
||||||
|
void clearChunkTrace( std::list<QPair<QPoint, FieldData>> &chunkTrace );
|
||||||
|
virtual void storeInField( const QPair<QPoint, FieldData> pixelData ) = 0;
|
||||||
|
virtual void initField() = 0;
|
||||||
|
void simplifyChunkTrace( std::list<QPair<QPoint, FieldData>> &shunkTrace );
|
||||||
|
|
||||||
|
virtual bool isTraceExists( const QPoint &pixel ) const = 0;
|
||||||
|
bool isTraceOutside( const QPoint &pixel ) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
QSize mFieldSize;
|
QSize mFieldSize;
|
||||||
QRect mLayerPixelExtent;
|
QPainter *mPainter = nullptr;
|
||||||
int mFieldResolution = 1;
|
int mFieldResolution = 1;
|
||||||
|
QPen mPen;
|
||||||
|
QImage mTraceImage;
|
||||||
|
|
||||||
|
QgsMapToPixel mMapToFieldPixel;
|
||||||
|
|
||||||
|
private:
|
||||||
int mPixelFillingCount = 0;
|
int mPixelFillingCount = 0;
|
||||||
int mMaxPixelFillingCount = 0;
|
int mMaxPixelFillingCount = 0;
|
||||||
std::unique_ptr<QgsMeshVectorValueInterpolator> mVectorValueInterpolator;
|
std::unique_ptr<QgsMeshVectorValueInterpolator> mVectorValueInterpolator;
|
||||||
QImage mTraceImage;
|
|
||||||
QVector<bool> mField;
|
|
||||||
QgsRectangle mLayerExtent;
|
QgsRectangle mLayerExtent;
|
||||||
QgsRectangle mMapExtent;
|
QgsRectangle mMapExtent;
|
||||||
QgsMapToPixel mMapToFieldPixel;
|
|
||||||
QPen mPen;
|
|
||||||
QPainter *mPainter = nullptr;
|
|
||||||
QPoint mFieldTopLeftInDeviceCoordinates;
|
QPoint mFieldTopLeftInDeviceCoordinates;
|
||||||
bool mValid = false;
|
bool mValid = false;
|
||||||
double mMagMax = 0;
|
double mMagMax = 0;
|
||||||
double mPixelFillingDensity;
|
double mPixelFillingDensity;
|
||||||
double mMinMagFilter = -1;
|
double mMinMagFilter = -1;
|
||||||
double mMaxMagFilter = -1;
|
double mMaxMagFilter = -1;
|
||||||
QgsRenderContext &mRenderContext; //keep the renderer context only to know if the renderer is stopped
|
const QgsRenderContext &mRenderContext; //keep the renderer context only to know if the renderer is stopped
|
||||||
|
bool mMinimizeFieldSize = true; //
|
||||||
};
|
};
|
||||||
|
|
||||||
class QgsMeshVectorStreamLineRenderer: public QgsMeshVectorRenderer
|
/**
|
||||||
|
* \ingroup core
|
||||||
|
*
|
||||||
|
* Class used to draw streamlines from vector field
|
||||||
|
*
|
||||||
|
* \note not available in Python bindings
|
||||||
|
* \since QGIS 3.12
|
||||||
|
*/
|
||||||
|
class QgsMeshStreamlinesField: public QgsMeshStreamField
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Constructor
|
||||||
|
QgsMeshStreamlinesField( const QgsTriangularMesh &triangularMesh,
|
||||||
|
const QgsMeshDataBlock &datasetVectorValues,
|
||||||
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues,
|
||||||
|
const QgsRectangle &layerExtent,
|
||||||
|
double magMax, bool dataIsOnVertices, QgsRenderContext &rendererContext );
|
||||||
|
|
||||||
|
//! Copy constructor
|
||||||
|
QgsMeshStreamlinesField( const QgsMeshStreamlinesField &other );
|
||||||
|
|
||||||
|
//! Assignment operator
|
||||||
|
QgsMeshStreamlinesField &operator=( const QgsMeshStreamlinesField &other );
|
||||||
|
|
||||||
|
private:
|
||||||
|
void storeInField( const QPair<QPoint, FieldData> pixelData ) override;
|
||||||
|
void initField() override;
|
||||||
|
bool isTraceExists( const QPoint &pixel ) const override;
|
||||||
|
void drawChunkTrace( const std::list<QPair<QPoint, FieldData> > &chunkTrace ) override;
|
||||||
|
|
||||||
|
QVector<bool> mField;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class QgsMeshParticleTracesField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup core
|
||||||
|
*
|
||||||
|
* Used to simulation moving particle
|
||||||
|
*
|
||||||
|
* \note not available in Python bindings
|
||||||
|
* \since QGIS 3.12
|
||||||
|
*/
|
||||||
|
struct QgsMeshTraceParticle
|
||||||
|
{
|
||||||
|
double lifeTime = 0;
|
||||||
|
QPoint position;
|
||||||
|
std::list<QPoint> tail;
|
||||||
|
double remainingTime = 0; //time remaining to spend in the current pixel at the end of the time step
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup core
|
||||||
|
*
|
||||||
|
* Class used to draw streamlines from vector field
|
||||||
|
*
|
||||||
|
* \note not available in Python bindings
|
||||||
|
* \since QGIS 3.12
|
||||||
|
*/
|
||||||
|
class QgsMeshParticleTracesField: public QgsMeshStreamField
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Constructor
|
||||||
|
QgsMeshParticleTracesField( const QgsTriangularMesh &triangularMesh,
|
||||||
|
const QgsMeshDataBlock &datasetVectorValues,
|
||||||
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues,
|
||||||
|
const QgsRectangle &layerExtent,
|
||||||
|
double magMax,
|
||||||
|
bool dataIsOnVertices,
|
||||||
|
const QgsRenderContext &rendererContext );
|
||||||
|
|
||||||
|
//! Copy constructor
|
||||||
|
QgsMeshParticleTracesField( const QgsMeshParticleTracesField &other );
|
||||||
|
|
||||||
|
//! Adds a particle in the vector field from a start point (pixel) with a specified life time
|
||||||
|
void addParticle( const QPoint &startPoint, double lifeTime );
|
||||||
|
|
||||||
|
//! Adds a particle in the vector field from a start point (map point) with a specified life time
|
||||||
|
void addParticleXY( const QgsPointXY &startPoint, double lifeTime );
|
||||||
|
|
||||||
|
//! Adds particle randomly (position and life time
|
||||||
|
void addRandomParticles();
|
||||||
|
|
||||||
|
//! Moves all the particles with a displacement corresponding to a nondimensional time
|
||||||
|
void moveParticles();
|
||||||
|
|
||||||
|
//! Returns the current image of the particles
|
||||||
|
QImage imageRendered() const;
|
||||||
|
|
||||||
|
//! Sets the total number of particles generated randomly
|
||||||
|
void setParticlesCount( int particlesCount );
|
||||||
|
|
||||||
|
//! Sets the maximum life time (nondimensional) of particle generated
|
||||||
|
void setParticlesLifeTime( double particlesLifeTime );
|
||||||
|
|
||||||
|
//! Stumps particles image and leave a persistent effect
|
||||||
|
void stump();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets stump factor from 0 to 255 :
|
||||||
|
* 0, stump completely, no persistence
|
||||||
|
* 255, no stump, total persistence
|
||||||
|
*/
|
||||||
|
void setStumpFactor( int sf );
|
||||||
|
|
||||||
|
//! Sets the time step
|
||||||
|
void setTimeStep( double timeStep );
|
||||||
|
|
||||||
|
//! Sets tihe color of the particles
|
||||||
|
void setParticleColor( const QColor &particleColor );
|
||||||
|
|
||||||
|
//! Sets particles size
|
||||||
|
void setParticleSize( double particleSize );
|
||||||
|
|
||||||
|
//! Sets the tail factor
|
||||||
|
void setTailFactor( double tailFactor );
|
||||||
|
|
||||||
|
//! Sets the minimum tail length
|
||||||
|
void setMinTailLength( int minTailLength );
|
||||||
|
|
||||||
|
//! Assignment operator
|
||||||
|
QgsMeshParticleTracesField &operator=( const QgsMeshParticleTracesField &other );
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPoint direction( QPoint position ) const;
|
||||||
|
|
||||||
|
float time( QPoint position ) const;
|
||||||
|
|
||||||
|
void drawParticleTrace( const QgsMeshTraceParticle &particle );
|
||||||
|
|
||||||
|
void storeInField( const QPair<QPoint, FieldData> pixelData ) override;
|
||||||
|
void initField() override;
|
||||||
|
bool isTraceExists( const QPoint &pixel ) const override;
|
||||||
|
void drawChunkTrace( const std::list<QPair<QPoint, FieldData>> &chunkTrace ) override {Q_UNUSED( chunkTrace );}
|
||||||
|
|
||||||
|
/* Nondimensional time
|
||||||
|
* This field store the time spent by the particle in the pixel
|
||||||
|
*
|
||||||
|
* This time is nondimensional and value 1 is equivalent to the time spent by the particle in a pixel
|
||||||
|
* for Vmax, the maximum magnitude of the vector field.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QVector<float> mTimeField;
|
||||||
|
|
||||||
|
/*the direction for a pixel is defined with a char value
|
||||||
|
*
|
||||||
|
* 1 2 3
|
||||||
|
* 4 5 6
|
||||||
|
* 7 8 9
|
||||||
|
*
|
||||||
|
* convenient to retrieve the indexes of the next pixel from the direction d:
|
||||||
|
* Xnext= (d-1)%3-1
|
||||||
|
* Ynext = (d-1)/3-1
|
||||||
|
*
|
||||||
|
* and the direction is defined by :
|
||||||
|
* d=incX + 2 + (incY+1)*3
|
||||||
|
*/
|
||||||
|
QVector<char> mDirectionField;
|
||||||
|
QList<QgsMeshTraceParticle> mParticles;
|
||||||
|
QImage mStumpImage;
|
||||||
|
|
||||||
|
double mTimeStep = 200;
|
||||||
|
double mParticlesLifeTime = 5000;
|
||||||
|
int mParticlesCount = 1000;
|
||||||
|
double mTailFactor = 5;
|
||||||
|
int mMinTailLength = 3;
|
||||||
|
QColor mParticleColor = Qt::white;
|
||||||
|
double mParticleSize = 2.5;
|
||||||
|
int mStumpFactor = 50;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup core
|
||||||
|
*
|
||||||
|
* A class derived from QgsMeshVectorRenderer used to render the particles traces
|
||||||
|
*
|
||||||
|
* \note not available in Python bindings
|
||||||
|
* \since QGIS 3.12
|
||||||
|
*/
|
||||||
|
class QgsMeshVectorStreamlineRenderer: public QgsMeshVectorRenderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//!Constructor
|
//!Constructor
|
||||||
QgsMeshVectorStreamLineRenderer( const QgsTriangularMesh &triangularMesh,
|
QgsMeshVectorStreamlineRenderer( const QgsTriangularMesh &triangularMesh,
|
||||||
const QgsMeshDataBlock &dataSetVectorValues,
|
const QgsMeshDataBlock &dataSetVectorValues,
|
||||||
const QgsMeshDataBlock &scalarActiveFaceFlagValues,
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues,
|
||||||
bool dataIsOnVertices,
|
bool dataIsOnVertices,
|
||||||
@ -267,7 +496,6 @@ class QgsMeshVectorStreamLineRenderer: public QgsMeshVectorRenderer
|
|||||||
const QgsRectangle &layerExtent,
|
const QgsRectangle &layerExtent,
|
||||||
double magMax );
|
double magMax );
|
||||||
|
|
||||||
|
|
||||||
void draw() override;
|
void draw() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -275,6 +503,78 @@ class QgsMeshVectorStreamLineRenderer: public QgsMeshVectorRenderer
|
|||||||
QgsRenderContext &mRendererContext;
|
QgsRenderContext &mRendererContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif //SIP_RUN
|
||||||
|
|
||||||
///@endcond
|
///@endcond
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup core
|
||||||
|
*
|
||||||
|
* A wrapper for QgsMeshParticuleTracesField used to render the particles. Available for Python binding
|
||||||
|
*
|
||||||
|
* \since QGIS 3.12
|
||||||
|
*/
|
||||||
|
class CORE_EXPORT QgsMeshVectorTraceRenderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//!Constructor to use from QgsMeshVectorRenderer
|
||||||
|
QgsMeshVectorTraceRenderer( const QgsTriangularMesh &triangularMesh,
|
||||||
|
const QgsMeshDataBlock &dataSetVectorValues,
|
||||||
|
const QgsMeshDataBlock &scalarActiveFaceFlagValues,
|
||||||
|
bool dataIsOnVertices,
|
||||||
|
const QgsRenderContext &rendererContext,
|
||||||
|
const QgsRectangle &layerExtent,
|
||||||
|
double magMax ) SIP_SKIP;
|
||||||
|
|
||||||
|
//!Constructor to use with Python binding
|
||||||
|
QgsMeshVectorTraceRenderer( QgsMeshLayer *layer, const QgsRenderContext &rendererContext );
|
||||||
|
|
||||||
|
//! Copy constructor
|
||||||
|
QgsMeshVectorTraceRenderer( const QgsMeshVectorTraceRenderer &other );
|
||||||
|
|
||||||
|
//! Destructor
|
||||||
|
~QgsMeshVectorTraceRenderer();
|
||||||
|
|
||||||
|
//! seeds particles in the vector fields
|
||||||
|
void seedRandomParticles( int count );
|
||||||
|
|
||||||
|
//! Moves all the particles using frame per second (fps) to calculate the displacement
|
||||||
|
QImage imageRendered();
|
||||||
|
|
||||||
|
//! Sets the number of frames per seconds that will be rendered
|
||||||
|
void setFPS( int FPS );
|
||||||
|
|
||||||
|
//! Sets the max number of pixels that can be go through by the particles in 1 second
|
||||||
|
void setMaxSpeedPixel( int max );
|
||||||
|
|
||||||
|
//! Sets maximum life time of particles in seconds
|
||||||
|
void setParticlesLifeTime( double particleLifeTime );
|
||||||
|
|
||||||
|
//! Sets colors of particle
|
||||||
|
void setParticlesColor( const QColor &c );
|
||||||
|
|
||||||
|
//! Sets particle size
|
||||||
|
void setParticlesSize( double width );
|
||||||
|
|
||||||
|
//! Sets the tail factor, used to adjust the length of the tail. 0 : minimum length, >1 increase the tail
|
||||||
|
void setTailFactor( double fct );
|
||||||
|
|
||||||
|
//! Sets the minimum tail length
|
||||||
|
void setMinimumTailLength( int l );
|
||||||
|
|
||||||
|
//! Sets the visual persistence of the tail
|
||||||
|
void setTailPersitence( double p );
|
||||||
|
|
||||||
|
//! Assignment operator
|
||||||
|
QgsMeshVectorTraceRenderer &operator=( const QgsMeshVectorTraceRenderer &other );
|
||||||
|
private:
|
||||||
|
std::unique_ptr<QgsMeshParticleTracesField> mParticleField;
|
||||||
|
const QgsRenderContext &mRendererContext;
|
||||||
|
int mFPS = 15; //frame per second of the output, used to calculate orher parameters of the field
|
||||||
|
int mVpixMax = 2000; //is the number of pixels that are going through for 1 s
|
||||||
|
double mParticleLifeTime = 5;
|
||||||
|
|
||||||
|
void updateFieldParameter();
|
||||||
|
};
|
||||||
|
|
||||||
#endif // QGSMESHTRACERENDERER_H
|
#endif // QGSMESHTRACERENDERER_H
|
||||||
|
|||||||
@ -470,7 +470,7 @@ QgsMeshVectorRenderer *QgsMeshVectorRenderer::makeVectorRenderer( const QgsTrian
|
|||||||
context, size );
|
context, size );
|
||||||
break;
|
break;
|
||||||
case QgsMeshRendererVectorSettings::Streamlines:
|
case QgsMeshRendererVectorSettings::Streamlines:
|
||||||
renderer = new QgsMeshVectorStreamLineRenderer(
|
renderer = new QgsMeshVectorStreamlineRenderer(
|
||||||
m,
|
m,
|
||||||
datasetVectorValues,
|
datasetVectorValues,
|
||||||
scalarActiveFaceFlagValues,
|
scalarActiveFaceFlagValues,
|
||||||
|
|||||||
@ -153,8 +153,7 @@ namespace QgsMeshUtils
|
|||||||
* Tests if point p is on the face defined with vertices
|
* Tests if point p is on the face defined with vertices
|
||||||
* \since QGIS 3.12
|
* \since QGIS 3.12
|
||||||
*/
|
*/
|
||||||
bool isInTriangleFace( const QgsPointXY point, const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
|
bool isInTriangleFace( const QgsPointXY point, const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices );
|
||||||
;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
Loading…
x
Reference in New Issue
Block a user