Skip to content

Commit 6ee9a47

Browse files
committed
[mesh] [feature] allow render vectors/arrows on the user-defined grid
1 parent 9af405c commit 6ee9a47

16 files changed

+483
-116
lines changed

python/core/auto_generated/mesh/qgsmeshrenderersettings.sip.in

+25
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,31 @@ Returns ratio of the head length of the arrow (range 0-1)
282282
void setArrowHeadLengthRatio( double arrowHeadLengthRatio );
283283
%Docstring
284284
Sets ratio of the head length of the arrow (range 0-1)
285+
%End
286+
287+
bool isOnUserDefinedGrid() const;
288+
%Docstring
289+
Returns if vectors are drawn on user-defined grid
290+
%End
291+
void setOnUserDefinedGrid( bool enabled );
292+
%Docstring
293+
Toggle drawing of vectors on user defined grid
294+
%End
295+
int userGridCellWidth() const;
296+
%Docstring
297+
Returns width in pixels of user grid cell
298+
%End
299+
void setUserGridCellWidth( int width );
300+
%Docstring
301+
Sets width of user grid cell (in pixels)
302+
%End
303+
int userGridCellHeight() const;
304+
%Docstring
305+
Returns height in pixels of user grid cell
306+
%End
307+
void setUserGridCellHeight( int height );
308+
%Docstring
309+
Sets height of user grid cell (in pixels)
285310
%End
286311

287312
QDomElement writeXml( QDomDocument &doc ) const;

src/app/mesh/qgsmeshrenderervectorsettingswidget.cpp

+30-1
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
#include "qgsmeshlayer.h"
2020
#include "qgsmessagelog.h"
2121

22+
#include <QIntValidator>
2223

2324
QgsMeshRendererVectorSettingsWidget::QgsMeshRendererVectorSettingsWidget( QWidget *parent )
2425
: QWidget( parent )
2526

2627
{
2728
setupUi( this );
29+
addValidators( );
2830

2931
mShaftLengthComboBox->setCurrentIndex( -1 );
3032

@@ -37,18 +39,35 @@ QgsMeshRendererVectorSettingsWidget::QgsMeshRendererVectorSettingsWidget( QWidge
3739

3840
connect( mShaftLengthComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ),
3941
mShaftOptionsStackedWidget, &QStackedWidget::setCurrentIndex );
42+
43+
connect( mDisplayVectorsOnGridGroupBox, &QGroupBox::toggled, this, &QgsMeshRendererVectorSettingsWidget::widgetChanged );
44+
4045
QVector<QLineEdit *> widgets;
4146
widgets << mMinMagLineEdit << mMaxMagLineEdit
4247
<< mHeadWidthLineEdit << mHeadLengthLineEdit
4348
<< mMinimumShaftLineEdit << mMaximumShaftLineEdit
44-
<< mScaleShaftByFactorOfLineEdit << mShaftLengthLineEdit;
49+
<< mScaleShaftByFactorOfLineEdit << mShaftLengthLineEdit
50+
<< mXSpacingLineEdit << mYSpacingLineEdit;
4551

4652
for ( auto widget : widgets )
4753
{
4854
connect( widget, &QLineEdit::textChanged, this, &QgsMeshRendererVectorSettingsWidget::widgetChanged );
4955
}
5056
}
5157

58+
void QgsMeshRendererVectorSettingsWidget::addValidators()
59+
{
60+
QIntValidator *validatorX = new QIntValidator();
61+
validatorX->setBottom( 0 );
62+
validatorX->setParent( mXSpacingLineEdit );
63+
mXSpacingLineEdit->setValidator( validatorX );
64+
65+
QIntValidator *validatorY = new QIntValidator();
66+
validatorY->setBottom( 0 );
67+
validatorY->setParent( mYSpacingLineEdit );
68+
mYSpacingLineEdit->setValidator( validatorY );
69+
}
70+
5271
void QgsMeshRendererVectorSettingsWidget::setLayer( QgsMeshLayer *layer )
5372
{
5473
mMeshLayer = layer;
@@ -76,6 +95,16 @@ QgsMeshRendererVectorSettings QgsMeshRendererVectorSettingsWidget::settings() co
7695
val = filterValue( mHeadLengthLineEdit->text(), settings.arrowHeadLengthRatio() * 100.0 );
7796
settings.setArrowHeadLengthRatio( val / 100.0 );
7897

98+
// user grid
99+
bool enabled = mDisplayVectorsOnGridGroupBox->isChecked();
100+
settings.setOnUserDefinedGrid( enabled );
101+
102+
val = filterValue( mXSpacingLineEdit->text(), settings.userGridCellWidth() );
103+
settings.setUserGridCellWidth( static_cast<int>( val ) );
104+
105+
val = filterValue( mYSpacingLineEdit->text(), settings.userGridCellHeight() );
106+
settings.setUserGridCellHeight( static_cast<int>( val ) );
107+
79108
// shaft length
80109
auto method = static_cast<QgsMeshRendererVectorSettings::ArrowScalingMethod>( mShaftLengthComboBox->currentIndex() );
81110
settings.setShaftLengthMethod( method );

src/app/mesh/qgsmeshrenderervectorsettingswidget.h

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ class APP_EXPORT QgsMeshRendererVectorSettingsWidget : public QWidget, private U
6161

6262
private:
6363

64+
//! Add validators to line edits
65+
void addValidators();
66+
6467
/**
6568
* convert text to double, return err_val if
6669
* text is not possible to convert or the value is negative

src/core/mesh/qgsmeshlayer.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
#include "qgsmaplayerlegend.h"
2626
#include "qgsmeshdataprovider.h"
2727
#include "qgsmeshlayer.h"
28-
#include "qgsmeshlayerinterpolator.h"
2928
#include "qgsmeshlayerrenderer.h"
3029
#include "qgsmeshlayerutils.h"
3130
#include "qgsproviderregistry.h"
@@ -144,11 +143,11 @@ QgsMeshDatasetValue QgsMeshLayer::datasetValue( const QgsMeshDatasetIndex &index
144143
const QgsMeshDatasetValue val1 = dataProvider()->datasetValue( index, v1 );
145144
const QgsMeshDatasetValue val2 = dataProvider()->datasetValue( index, v2 );
146145
const QgsMeshDatasetValue val3 = dataProvider()->datasetValue( index, v3 );
147-
const double x = QgsMeshLayerInterpolator::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
146+
const double x = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
148147
double y = std::numeric_limits<double>::quiet_NaN();
149148
bool isVector = dataProvider()->datasetGroupMetadata( index ).isVector();
150149
if ( isVector )
151-
y = QgsMeshLayerInterpolator::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
150+
y = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
152151

153152
value = QgsMeshDatasetValue( x, y );
154153
}

src/core/mesh/qgsmeshlayerinterpolator.cpp

+5-87
Original file line numberDiff line numberDiff line change
@@ -26,86 +26,7 @@
2626
#include "qgsvector.h"
2727
#include "qgspoint.h"
2828
#include "qgspointxy.h"
29-
30-
static void bbox2rect(
31-
const QgsMapToPixel &mtp,
32-
const QSize &outputSize,
33-
const QgsRectangle &bbox,
34-
int &leftLim, int &rightLim, int &topLim, int &bottomLim )
35-
{
36-
QgsPointXY ll = mtp.transform( bbox.xMinimum(), bbox.yMinimum() );
37-
QgsPointXY ur = mtp.transform( bbox.xMaximum(), bbox.yMaximum() );
38-
topLim = std::max( int( ur.y() ), 0 );
39-
bottomLim = std::min( int( ll.y() ), outputSize.height() - 1 );
40-
leftLim = std::max( int( ll.x() ), 0 );
41-
rightLim = std::min( int( ur.x() ), outputSize.width() - 1 );
42-
}
43-
44-
static void lamTol( double &lam )
45-
{
46-
const static double eps = 1e-6;
47-
if ( ( lam < 0.0 ) && ( lam > -eps ) )
48-
{
49-
lam = 0.0;
50-
}
51-
}
52-
53-
static bool E3T_physicalToBarycentric( const QgsPointXY &pA, const QgsPointXY &pB, const QgsPointXY &pC, const QgsPointXY &pP,
54-
double &lam1, double &lam2, double &lam3 )
55-
{
56-
if ( pA == pB || pA == pC || pB == pC )
57-
return false; // this is not a valid triangle!
58-
59-
// Compute vectors
60-
QgsVector v0( pC - pA );
61-
QgsVector v1( pB - pA );
62-
QgsVector v2( pP - pA );
63-
64-
// Compute dot products
65-
double dot00 = v0 * v0;
66-
double dot01 = v0 * v1;
67-
double dot02 = v0 * v2;
68-
double dot11 = v1 * v1;
69-
double dot12 = v1 * v2;
70-
71-
// Compute barycentric coordinates
72-
double invDenom = 1.0 / ( dot00 * dot11 - dot01 * dot01 );
73-
lam1 = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
74-
lam2 = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
75-
lam3 = 1.0 - lam1 - lam2;
76-
77-
// Apply some tolerance to lam so we can detect correctly border points
78-
lamTol( lam1 );
79-
lamTol( lam2 );
80-
lamTol( lam3 );
81-
82-
// Return if POI is outside triangle
83-
if ( ( lam1 < 0 ) || ( lam2 < 0 ) || ( lam3 < 0 ) )
84-
{
85-
return false;
86-
}
87-
88-
return true;
89-
}
90-
91-
double QgsMeshLayerInterpolator::interpolateFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
92-
double val1, double val2, double val3, const QgsPointXY &pt )
93-
{
94-
double lam1, lam2, lam3;
95-
if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
96-
return std::numeric_limits<double>::quiet_NaN();
97-
98-
return lam1 * val3 + lam2 * val2 + lam3 * val1;
99-
}
100-
101-
double interpolateFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, double val, const QgsPointXY &pt )
102-
{
103-
double lam1, lam2, lam3;
104-
if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
105-
return std::numeric_limits<double>::quiet_NaN();
106-
107-
return val;
108-
}
29+
#include "qgsmeshlayerutils.h"
10930

11031
QgsMeshLayerInterpolator::QgsMeshLayerInterpolator( const QgsTriangularMesh &m,
11132
const QVector<double> &datasetValues, const QVector<bool> &activeFaceFlagValues,
@@ -167,16 +88,13 @@ QgsRasterBlock *QgsMeshLayerInterpolator::block( int, const QgsRectangle &extent
16788
if ( !isActive )
16889
continue;
16990

170-
QgsRectangle bbox;
171-
bbox.combineExtentWith( p1.x(), p1.y() );
172-
bbox.combineExtentWith( p2.x(), p2.y() );
173-
bbox.combineExtentWith( p3.x(), p3.y() );
91+
QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
17492
if ( !extent.intersects( bbox ) )
17593
continue;
17694

17795
// Get the BBox of the element in pixels
17896
int topLim, bottomLim, leftLim, rightLim;
179-
bbox2rect( mContext.mapToPixel(), mOutputSize, bbox, leftLim, rightLim, topLim, bottomLim );
97+
QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, leftLim, rightLim, topLim, bottomLim );
18098

18199
// interpolate in the bounding box of the face
182100
for ( int j = topLim; j <= bottomLim; j++ )
@@ -187,7 +105,7 @@ QgsRasterBlock *QgsMeshLayerInterpolator::block( int, const QgsRectangle &extent
187105
double val;
188106
const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( k, j );
189107
if ( mDataOnVertices )
190-
val = interpolateFromVerticesData(
108+
val = QgsMeshLayerUtils::interpolateFromVerticesData(
191109
p1,
192110
p2,
193111
p3,
@@ -198,7 +116,7 @@ QgsRasterBlock *QgsMeshLayerInterpolator::block( int, const QgsRectangle &extent
198116
else
199117
{
200118
int face = mTriangularMesh.trianglesToNativeFaces()[i];
201-
val = interpolateFromFacesData(
119+
val = QgsMeshLayerUtils::interpolateFromFacesData(
202120
p1,
203121
p2,
204122
p3,

src/core/mesh/qgsmeshlayerinterpolator.h

-16
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,6 @@ class QgsMeshLayerInterpolator : public QgsRasterInterface
6060
int bandCount() const override;
6161
QgsRasterBlock *block( int, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback = nullptr ) override;
6262

63-
/*
64-
* Interpolates value based on known values on the vertices of a triangle
65-
* \param p1 first vertex of the triangle
66-
* \param p2 second vertex of the triangle
67-
* \param p3 third vertex of the triangle
68-
* \param val1 value on p1 of the triangle
69-
* \param val2 value on p2 of the triangle
70-
* \param val3 value on p3 of the triangle
71-
* \param pt point where to calculate value
72-
* \returns value on the point pt or NaN in case the point is outside the triangle
73-
*/
74-
static double interpolateFromVerticesData(
75-
const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
76-
double val1, double val2, double val3, const QgsPointXY &pt
77-
);
78-
7963
private:
8064
const QgsTriangularMesh &mTriangularMesh;
8165
const QVector<double> &mDatasetValues;

src/core/mesh/qgsmeshlayerutils.cpp

+92
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,96 @@ void QgsMeshLayerUtils::calculateMinMaxForDataset( double &min, double &max, Qgs
116116

117117
}
118118

119+
void QgsMeshLayerUtils::boundingBoxToScreenRectangle( const QgsMapToPixel &mtp,
120+
const QSize &outputSize,
121+
const QgsRectangle &bbox,
122+
int &leftLim,
123+
int &rightLim,
124+
int &topLim,
125+
int &bottomLim )
126+
{
127+
QgsPointXY ll = mtp.transform( bbox.xMinimum(), bbox.yMinimum() );
128+
QgsPointXY ur = mtp.transform( bbox.xMaximum(), bbox.yMaximum() );
129+
topLim = std::max( int( ur.y() ), 0 );
130+
bottomLim = std::min( int( ll.y() ), outputSize.height() - 1 );
131+
leftLim = std::max( int( ll.x() ), 0 );
132+
rightLim = std::min( int( ur.x() ), outputSize.width() - 1 );
133+
}
134+
135+
static void lamTol( double &lam )
136+
{
137+
const static double eps = 1e-6;
138+
if ( ( lam < 0.0 ) && ( lam > -eps ) )
139+
{
140+
lam = 0.0;
141+
}
142+
}
143+
144+
static bool E3T_physicalToBarycentric( const QgsPointXY &pA, const QgsPointXY &pB, const QgsPointXY &pC, const QgsPointXY &pP,
145+
double &lam1, double &lam2, double &lam3 )
146+
{
147+
if ( pA == pB || pA == pC || pB == pC )
148+
return false; // this is not a valid triangle!
149+
150+
// Compute vectors
151+
QgsVector v0( pC - pA );
152+
QgsVector v1( pB - pA );
153+
QgsVector v2( pP - pA );
154+
155+
// Compute dot products
156+
double dot00 = v0 * v0;
157+
double dot01 = v0 * v1;
158+
double dot02 = v0 * v2;
159+
double dot11 = v1 * v1;
160+
double dot12 = v1 * v2;
161+
162+
// Compute barycentric coordinates
163+
double invDenom = 1.0 / ( dot00 * dot11 - dot01 * dot01 );
164+
lam1 = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
165+
lam2 = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
166+
lam3 = 1.0 - lam1 - lam2;
167+
168+
// Apply some tolerance to lam so we can detect correctly border points
169+
lamTol( lam1 );
170+
lamTol( lam2 );
171+
lamTol( lam3 );
172+
173+
// Return if POI is outside triangle
174+
if ( ( lam1 < 0 ) || ( lam2 < 0 ) || ( lam3 < 0 ) )
175+
{
176+
return false;
177+
}
178+
179+
return true;
180+
}
181+
182+
double QgsMeshLayerUtils::interpolateFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
183+
double val1, double val2, double val3, const QgsPointXY &pt )
184+
{
185+
double lam1, lam2, lam3;
186+
if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
187+
return std::numeric_limits<double>::quiet_NaN();
188+
189+
return lam1 * val3 + lam2 * val2 + lam3 * val1;
190+
}
191+
192+
double QgsMeshLayerUtils::interpolateFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
193+
double val, const QgsPointXY &pt )
194+
{
195+
double lam1, lam2, lam3;
196+
if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
197+
return std::numeric_limits<double>::quiet_NaN();
198+
199+
return val;
200+
}
201+
202+
QgsRectangle QgsMeshLayerUtils::triangleBoundingBox( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3 )
203+
{
204+
QgsRectangle bbox;
205+
bbox.combineExtentWith( p1.x(), p1.y() );
206+
bbox.combineExtentWith( p2.x(), p2.y() );
207+
bbox.combineExtentWith( p3.x(), p3.y() );
208+
return bbox;
209+
}
210+
119211
///@endcond

0 commit comments

Comments
 (0)