QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
qgsrubberband3d.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrubberband3d.cpp
3 --------------------------------------
4 Date : June 2021
5 Copyright : (C) 2021 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsrubberband3d.h"
17
18#include "qgs3dmapsettings.h"
19#include "qgs3dutils.h"
20#include "qgsabstract3dengine.h"
22#include "qgsgeotransform.h"
23#include "qgslinematerial_p.h"
24#include "qgslinestring.h"
25#include "qgslinevertexdata_p.h"
26#include "qgsmarkersymbol.h"
27#include "qgsmessagelog.h"
29#include "qgspolygon.h"
30#include "qgssymbollayer.h"
31#include "qgssymbollayerutils.h"
33#include "qgstessellator.h"
34#include "qgsvertexid.h"
35
36#include <QColor>
37#include <QString>
38#include <Qt3DCore/QAttribute>
39#include <Qt3DCore/QBuffer>
40#include <Qt3DCore/QEntity>
41#include <Qt3DCore/QGeometry>
42#include <Qt3DRender/QGeometryRenderer>
43
44using namespace Qt::StringLiterals;
45
47
48
49QgsRubberBand3D::QgsRubberBand3D( Qgs3DMapSettings &map, QgsAbstract3DEngine *engine, Qt3DCore::QEntity *parentEntity, const Qgis::GeometryType geometryType )
50 : mMapSettings( &map )
51 , mEngine( engine )
52 , mGeometryType( geometryType )
53{
54 switch ( mGeometryType )
55 {
57 setupMarker( parentEntity );
58 break;
60 setupLine( parentEntity );
61 setupMarker( parentEntity );
62 break;
64 setupMarker( parentEntity );
65 setupLine( parentEntity );
66 setupPolygon( parentEntity );
67 break;
70 QgsDebugError( "Unknown GeometryType used in QgsRubberband3D" );
71 break;
72 }
73}
74
75void QgsRubberBand3D::setupMarker( Qt3DCore::QEntity *parentEntity )
76{
77 mMarkerEntity.reset( new Qt3DCore::QEntity( parentEntity ) );
78 mMarkerGeometry = new QgsBillboardGeometry();
79 mMarkerGeometryRenderer = new Qt3DRender::QGeometryRenderer;
80 mMarkerGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
81 mMarkerGeometryRenderer->setGeometry( mMarkerGeometry );
82 mMarkerGeometryRenderer->setVertexCount( mMarkerGeometry->count() );
83
84 setMarkerType( mMarkerType );
85 mMarkerEntity->addComponent( mMarkerGeometryRenderer );
86
87 mMarkerTransform = new QgsGeoTransform;
88 mMarkerTransform->setOrigin( mMapSettings->origin() );
89 mMarkerEntity->addComponent( mMarkerTransform );
90}
91
92void QgsRubberBand3D::setupLine( Qt3DCore::QEntity *parentEntity )
93{
94 mLineEntity.reset( new Qt3DCore::QEntity( parentEntity ) );
95
96 QgsLineVertexData dummyLineData;
97 mLineGeometry = dummyLineData.createGeometry( mLineEntity );
98
99 Q_ASSERT( mLineGeometry->attributes().count() == 2 );
100 mPositionAttribute = mLineGeometry->attributes().at( 0 );
101 mIndexAttribute = mLineGeometry->attributes().at( 1 );
102
103 mLineGeometryRenderer = new Qt3DRender::QGeometryRenderer;
104 mLineGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
105 mLineGeometryRenderer->setGeometry( mLineGeometry );
106 mLineGeometryRenderer->setPrimitiveRestartEnabled( true );
107 mLineGeometryRenderer->setRestartIndexValue( 0 );
108
109 mLineEntity->addComponent( mLineGeometryRenderer );
110
111 mLineMaterial = new QgsLineMaterial;
112 mLineMaterial->setLineWidth( mWidth );
113 mLineMaterial->setLineColor( mColor );
114
115 QObject::connect( mEngine, &QgsAbstract3DEngine::sizeChanged, mLineMaterial, [this] { mLineMaterial->setViewportSize( mEngine->size() ); } );
116 mLineMaterial->setViewportSize( mEngine->size() );
117
118 mLineEntity->addComponent( mLineMaterial );
119
120 mLineTransform = new QgsGeoTransform;
121 mLineTransform->setOrigin( mMapSettings->origin() );
122 mLineEntity->addComponent( mLineTransform );
123}
124
125void QgsRubberBand3D::setupPolygon( Qt3DCore::QEntity *parentEntity )
126{
127 mPolygonEntity.reset( new Qt3DCore::QEntity( parentEntity ) );
128
129 mPolygonGeometry = new QgsTessellatedPolygonGeometry();
130
131 Qt3DRender::QGeometryRenderer *polygonGeometryRenderer = new Qt3DRender::QGeometryRenderer;
132 polygonGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
133 polygonGeometryRenderer->setGeometry( mPolygonGeometry );
134 mPolygonEntity->addComponent( polygonGeometryRenderer );
135
136 QgsPhongMaterialSettings polygonMaterialSettings = QgsPhongMaterialSettings();
137 polygonMaterialSettings.setAmbient( mColor );
138 polygonMaterialSettings.setDiffuse( mColor );
139 polygonMaterialSettings.setOpacity( DEFAULT_POLYGON_OPACITY );
140 mPolygonMaterial = polygonMaterialSettings.toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, QgsMaterialContext() );
141 mPolygonEntity->addComponent( mPolygonMaterial );
142
143 mPolygonTransform = new QgsGeoTransform;
144 mPolygonTransform->setOrigin( mMapSettings->origin() );
145 mPolygonEntity->addComponent( mPolygonTransform );
146}
147
148void QgsRubberBand3D::removePoint( int index )
149{
150 if ( QgsPolygon *polygon = qgsgeometry_cast<QgsPolygon *>( mGeometry.get() ) )
151 {
152 QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( polygon->exteriorRing() );
153 const int vertexIndex = index < 0 ? lineString->numPoints() - 1 + index : index;
154 lineString->deleteVertex( QgsVertexId( 0, 0, vertexIndex ) );
155
156 if ( lineString->numPoints() < 3 )
157 {
158 mGeometry.set( new QgsLineString( *lineString ) );
159 }
160 }
161 else if ( QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( mGeometry.get() ) )
162 {
163 const int vertexIndex = index < 0 ? lineString->numPoints() + index : index;
164 lineString->deleteVertex( QgsVertexId( 0, 0, vertexIndex ) );
165 }
166 else
167 {
168 return;
169 }
170
171 updateGeometry();
172}
173
174QgsRubberBand3D::~QgsRubberBand3D()
175{
176 if ( mPolygonEntity )
177 {
178 mPolygonEntity.reset();
179 }
180 if ( mLineEntity )
181 {
182 mLineEntity.reset();
183 }
184 if ( mMarkerEntity )
185 {
186 mMarkerEntity.reset();
187 }
188}
189
190
191float QgsRubberBand3D::width() const
192{
193 return mWidth;
194}
195
196void QgsRubberBand3D::setWidth( float width )
197{
198 const bool isLineOrPolygon = mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon;
199 mWidth = width;
200
201 if ( isLineOrPolygon && mEdgesEnabled )
202 {
203 // when highlighting lines, the vertex markers should be wider
204 mLineMaterial->setLineWidth( width );
205 width *= 3;
206 }
207
208 mMarkerSymbol->setSize( width );
209 updateMarkerMaterial();
210}
211
212QColor QgsRubberBand3D::color() const
213{
214 return mColor;
215}
216
217void QgsRubberBand3D::setColor( const QColor color )
218{
219 const bool isLineOrPolygon = mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon;
220 mColor = color;
221
222 if ( mEdgesEnabled && isLineOrPolygon )
223 {
224 mLineMaterial->setLineColor( color );
225 }
226
227 if ( isLineOrPolygon )
228 {
229 mMarkerSymbol->setColor( color.lighter( 130 ) );
230 }
231 else
232 {
233 mMarkerSymbol->setColor( color );
234 }
235
236 if ( mMarkerSymbol->symbolLayerCount() > 0 && mMarkerSymbol->symbolLayer( 0 )->layerType() == "SimpleMarker"_L1 && !mOutlineColor.value() )
237 {
238 mMarkerSymbol->symbolLayer( 0 )->setStrokeColor( color );
239 }
240 updateMarkerMaterial();
241
242 if ( mGeometryType == Qgis::GeometryType::Polygon )
243 {
244 if ( mPolygonMaterial )
245 mPolygonEntity->removeComponent( mPolygonMaterial );
246
247 if ( mPolygonFillEnabled )
248 {
249 QgsPhongMaterialSettings polygonMaterialSettings;
250 polygonMaterialSettings.setAmbient( mColor );
251 polygonMaterialSettings.setDiffuse( mColor );
252 polygonMaterialSettings.setOpacity( DEFAULT_POLYGON_OPACITY );
253 mPolygonMaterial = polygonMaterialSettings.toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, QgsMaterialContext() );
254 mPolygonEntity->addComponent( mPolygonMaterial );
255 }
256 }
257}
258
259QColor QgsRubberBand3D::outlineColor() const
260{
261 return mOutlineColor;
262}
263
264void QgsRubberBand3D::setOutlineColor( const QColor color )
265{
266 mOutlineColor = color;
267
268 if ( mMarkerSymbol->symbolLayerCount() > 0 && mMarkerSymbol->symbolLayer( 0 )->layerType() == "SimpleMarker"_L1 )
269 {
270 mMarkerSymbol->symbolLayer( 0 )->setStrokeColor( color );
271 }
272 updateMarkerMaterial();
273}
274
275void QgsRubberBand3D::setMarkerType( const MarkerType marker )
276{
277 mMarkerType = marker;
278
279 const bool lineOrPolygon = mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon;
280
281 const QVariantMap props {
282 { u"color"_s, lineOrPolygon ? mColor.lighter( 130 ).name() : mColor.name() },
283 { u"size_unit"_s, u"pixel"_s },
284 { u"size"_s, QString::number( lineOrPolygon ? mWidth * 3.f : mWidth ) },
285 { u"outline_color"_s, mOutlineColor.value() ? mOutlineColor.name() : mColor.name() },
286 { u"outline_style"_s, QgsSymbolLayerUtils::encodePenStyle( mMarkerOutlineStyle ) },
287 { u"outline_width"_s, QString::number( lineOrPolygon ? 0.5 : 1 ) },
288 { u"name"_s, mMarkerType == Square ? u"square"_s : u"circle"_s }
289 };
290
291 mMarkerSymbol = QgsMarkerSymbol::createSimple( props );
292 updateMarkerMaterial();
293}
294
295QgsRubberBand3D::MarkerType QgsRubberBand3D::markerType() const
296{
297 return mMarkerType;
298}
299
300void QgsRubberBand3D::setMarkerOutlineStyle( const Qt::PenStyle style )
301{
302 mMarkerOutlineStyle = style;
303 setMarkerType( markerType() );
304}
305
306Qt::PenStyle QgsRubberBand3D::markerOutlineStyle() const
307{
308 return mMarkerOutlineStyle;
309}
310
311void QgsRubberBand3D::setMarkersEnabled( const bool enable )
312{
313 mMarkerEnabled = enable;
314 updateMarkerMaterial();
315}
316
317bool QgsRubberBand3D::hasMarkersEnabled() const
318{
319 return mMarkerEnabled;
320}
321
322void QgsRubberBand3D::setEdgesEnabled( const bool enable )
323{
324 mEdgesEnabled = enable;
325 setColor( mColor );
326}
327
328bool QgsRubberBand3D::hasEdgesEnabled() const
329{
330 return mEdgesEnabled;
331}
332
333void QgsRubberBand3D::setFillEnabled( const bool enable )
334{
335 mPolygonFillEnabled = enable;
336 setColor( mColor );
337}
338
339bool QgsRubberBand3D::hasFillEnabled() const
340{
341 return mPolygonFillEnabled;
342}
343
344void QgsRubberBand3D::reset()
345{
346 mGeometry.set( nullptr );
347 updateGeometry();
348}
349
350void QgsRubberBand3D::addPoint( const QgsPoint &pt )
351{
352 if ( QgsPolygon *polygon = qgsgeometry_cast<QgsPolygon *>( mGeometry.get() ) )
353 {
354 QgsLineString *exteriorRing = qgsgeometry_cast<QgsLineString *>( polygon->exteriorRing() );
355 const int lastVertexIndex = exteriorRing->numPoints() - 1;
356 exteriorRing->insertVertex( QgsVertexId( 0, 0, lastVertexIndex ), pt );
357 }
358 else if ( QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( mGeometry.get() ) )
359 {
360 lineString->addVertex( pt );
361 // transform linestring to polygon if we have enough vertices
362 if ( mGeometryType == Qgis::GeometryType::Polygon && lineString->numPoints() >= 3 )
363 {
364 mGeometry.set( new QgsPolygon( lineString->clone() ) );
365 }
366 }
367 else if ( !mGeometry.constGet() )
368 {
369 mGeometry.set( new QgsLineString( QVector<QgsPoint> { pt } ) );
370 }
371
372 updateGeometry();
373}
374
375void QgsRubberBand3D::setGeometry( const QgsGeometry &geometry )
376{
377 mGeometry = geometry;
378 mGeometryType = geometry.type();
379
380 updateGeometry();
381}
382
383void QgsRubberBand3D::removeLastPoint()
384{
385 removePoint( -1 );
386}
387
388void QgsRubberBand3D::removePenultimatePoint()
389{
390 removePoint( -2 );
391}
392
393void QgsRubberBand3D::moveLastPoint( const QgsPoint &pt )
394{
395 if ( QgsPolygon *polygon = qgsgeometry_cast<QgsPolygon *>( mGeometry.get() ) )
396 {
397 QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( polygon->exteriorRing() );
398 const int lastVertexIndex = lineString->numPoints() - 2;
399 lineString->moveVertex( QgsVertexId( 0, 0, lastVertexIndex ), pt );
400 }
401 else if ( QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( mGeometry.get() ) )
402 {
403 const int lastVertexIndex = lineString->numPoints() - 1;
404 lineString->moveVertex( QgsVertexId( 0, 0, lastVertexIndex ), pt );
405 }
406 else
407 {
408 return;
409 }
410
411 updateGeometry();
412}
413
414void QgsRubberBand3D::updateGeometry()
415{
416 // figure out a reasonable origin for the coordinates to keep them as small as possible
417 const QgsBox3D box = mGeometry.constGet() ? mGeometry.constGet()->boundingBox3D() : QgsBox3D();
418 const QgsVector3D dataOrigin = box.isNull() ? mMapSettings->origin() : box.center();
419
420 QgsLineVertexData lineData;
421 lineData.withAdjacency = true;
422 lineData.geocentricCoordinates = mMapSettings->sceneMode() == Qgis::SceneMode::Globe;
424 if ( const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( mGeometry.constGet() ) )
425 {
426 std::unique_ptr< QgsLineString > lineString( qgsgeometry_cast<QgsLineString *>( polygon->exteriorRing()->clone() ) );
427 const int lastVertexIndex = lineString->numPoints() - 1;
428 lineString->deleteVertex( QgsVertexId( 0, 0, lastVertexIndex ) );
429 lineData.addLineString( *lineString, 0, true );
430 }
431 else if ( const QgsLineString *lineString = qgsgeometry_cast<const QgsLineString *>( mGeometry.constGet() ) )
432 {
433 lineData.addLineString( *lineString, 0, false );
434 }
435
436
437 if ( mEdgesEnabled && ( mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon ) )
438 {
439 mPositionAttribute->buffer()->setData( lineData.createVertexBuffer() );
440 mIndexAttribute->buffer()->setData( lineData.createIndexBuffer() );
441 mLineGeometryRenderer->setVertexCount( static_cast<int>( lineData.indexes.count() ) );
442 mLineTransform->setGeoTranslation( dataOrigin );
443 }
444
445 // first entry is empty for primitive restart
446 lineData.vertices.pop_front();
447
448 // we may not want a marker on the last point as it's tracked by the mouse cursor
449 if ( mHideLastMarker && !lineData.vertices.isEmpty() )
450 lineData.vertices.pop_back();
451
452 mMarkerGeometry->setPositions( lineData.vertices );
453 mMarkerGeometryRenderer->setVertexCount( static_cast<int>( lineData.vertices.count() ) );
454 mMarkerTransform->setGeoTranslation( dataOrigin );
455
456 if ( mGeometryType == Qgis::GeometryType::Polygon )
457 {
458 if ( const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( mGeometry.constGet() ) )
459 {
460 QgsTessellator tessellator;
461 tessellator.setOrigin( mMapSettings->origin() );
462 tessellator.setAddNormals( true );
463 tessellator.setOutputZUp( true );
464 tessellator.addPolygon( *polygon, 0 );
465 if ( !tessellator.error().isEmpty() )
466 {
467 QgsMessageLog::logMessage( tessellator.error(), QObject::tr( "3D" ) );
468 }
469 // extract vertex buffer data from tessellator
470 const QByteArray vertexBuffer = tessellator.vertexBuffer();
471 const QByteArray indexBuffer = tessellator.indexBuffer();
472 const size_t vertexCount = tessellator.uniqueVertexCount();
473 const size_t indexCount = tessellator.dataVerticesCount();
474
475 mPolygonGeometry->setVertexBufferData( vertexBuffer, vertexCount, QVector<QgsFeatureId>(), QVector<uint>() );
476 mPolygonGeometry->setIndexBufferData( indexBuffer, indexCount );
477 mPolygonTransform->setGeoTranslation( mMapSettings->origin() );
478 }
479 else
480 {
481 mPolygonGeometry->setVertexBufferData( QByteArray(), 0, QVector<QgsFeatureId>(), QVector<uint>() );
482 mPolygonGeometry->setIndexBufferData( QByteArray(), 0 );
483 }
484 }
485}
486
487void QgsRubberBand3D::updateMarkerMaterial()
488{
489 if ( mMarkerEnabled )
490 {
491 if ( !mMarkerMaterial )
492 {
493 mMarkerMaterial = new QgsPoint3DBillboardMaterial();
494 mMarkerEntity->addComponent( mMarkerMaterial );
495 //TODO: QgsAbstract3DEngine::sizeChanged should have const QSize &size param
496 QObject::connect( mEngine, &QgsAbstract3DEngine::sizeChanged, mMarkerMaterial, [this] { mMarkerMaterial->setViewportSize( mEngine->size() ); } );
497 }
498
499 mMarkerMaterial->setTexture2DFromSymbol( mMarkerSymbol.get(), Qgs3DRenderContext::fromMapSettings( mMapSettings ) );
500 mMarkerMaterial->setViewportSize( mEngine->size() );
501 }
502 else if ( !mMarkerEnabled && mMarkerMaterial )
503 {
504 mMarkerEntity->removeComponent( mMarkerMaterial );
505 mMarkerMaterial->setParent( static_cast<Qt3DCore::QEntity *>( nullptr ) );
506 mMarkerMaterial->deleteLater();
507 mMarkerMaterial = nullptr;
508 }
509}
@ Absolute
Elevation is taken directly from feature and is independent of terrain height (final elevation = feat...
Definition qgis.h:4100
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
@ Vertex
Clamp every vertex of feature.
Definition qgis.h:4113
@ Globe
Scene is represented as a globe using a geocentric CRS.
Definition qgis.h:4299
Definition of the world.
static Qgs3DRenderContext fromMapSettings(const Qgs3DMapSettings *mapSettings)
Creates an initialized Qgs3DRenderContext instance from given Qgs3DMapSettings.
Base class for 3D engine implementation.
void sizeChanged()
Emitted after a call to setSize().
Geometry of the billboard rendering for points in 3D map view.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
QgsVector3D center() const
Returns the center of the box as a vector.
Definition qgsbox3d.cpp:124
bool isNull() const
Test if the box is null (holding no spatial information).
Definition qgsbox3d.cpp:311
A geometry is the spatial representation of a feature.
Qgis::GeometryType type
Line string geometry type, with support for z-dimension and m-values.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
int numPoints() const override
Returns the number of points in the curve.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
static std::unique_ptr< QgsMarkerSymbol > createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
Context settings for a material.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Basic shading material used for rendering based on the Phong shading model with three color component...
void setOpacity(double opacity)
Sets opacity of the surface.
QgsMaterial * toMaterial(QgsMaterialSettingsRenderingTechnique technique, const QgsMaterialContext &context) const override
Creates a new QgsMaterial object representing the material settings.
void setDiffuse(const QColor &diffuse)
Sets diffuse color component.
void setAmbient(const QColor &ambient)
Sets ambient color component.
Material of the billboard rendering for points in 3D map view.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
Polygon geometry type.
Definition qgspolygon.h:37
static QString encodePenStyle(Qt::PenStyle style)
Qt3DRender::QGeometry subclass that represents polygons tessellated into 3D geometry.
Tessellates polygons into triangles.
void setOrigin(const QgsVector3D &origin)
Sets the origin point of the map.
QByteArray vertexBuffer() const
Returns vertex buffer for the generated points.
void addPolygon(const QgsPolygon &polygon, float extrusionHeight)
Tessellates a triangle and adds its vertex entries to the output data array.
int uniqueVertexCount() const
Returns unique vertex count.
QByteArray indexBuffer() const
Returns index buffer for the generated points.
QString error() const
Returns a descriptive error string if the tessellation failed.
void setOutputZUp(bool zUp)
Sets whether the "up" direction should be the Z axis on output (true), otherwise the "up" direction w...
int dataVerticesCount() const
Returns the number of vertices stored in the output data array.
void setAddNormals(bool addNormals)
Sets whether normals should be added to the output data (true) or not (false).
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
@ Triangles
Triangle based rendering (default).
T qgsgeometry_cast(QgsAbstractGeometry *geom)
#define QgsDebugError(str)
Definition qgslogger.h:59
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:34