QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgsline3dsymbol_p.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsline3dsymbol_p.cpp
3 --------------------------------------
4 Date : July 2017
5 Copyright : (C) 2017 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 "qgsline3dsymbol_p.h"
17
18#include "qgsline3dsymbol.h"
19#include "qgslinematerial_p.h"
20#include "qgslinevertexdata_p.h"
22#include "qgstessellator.h"
23#include "qgs3dmapsettings.h"
24//#include "qgsterraingenerator.h"
25#include "qgs3dutils.h"
26
27#include "qgsvectorlayer.h"
28#include "qgsmultilinestring.h"
29#include "qgsmultipolygon.h"
30#include "qgsgeos.h"
32#include "qgspolygon.h"
34
35#include <Qt3DExtras/QPhongMaterial>
36#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
37#include <Qt3DRender/QAttribute>
38#include <Qt3DRender/QBuffer>
39
40typedef Qt3DRender::QAttribute Qt3DQAttribute;
41typedef Qt3DRender::QBuffer Qt3DQBuffer;
42typedef Qt3DRender::QGeometry Qt3DQGeometry;
43#else
44#include <Qt3DCore/QAttribute>
45#include <Qt3DCore/QBuffer>
46
47typedef Qt3DCore::QAttribute Qt3DQAttribute;
48typedef Qt3DCore::QBuffer Qt3DQBuffer;
49typedef Qt3DCore::QGeometry Qt3DQGeometry;
50#endif
51#include <Qt3DRender/QGeometryRenderer>
52
54
55// -----------
56
57
58class QgsBufferedLine3DSymbolHandler : public QgsFeature3DHandler
59{
60 public:
61 QgsBufferedLine3DSymbolHandler( const QgsLine3DSymbol *symbol, const QgsFeatureIds &selectedIds )
62 : mSymbol( static_cast< QgsLine3DSymbol *>( symbol->clone() ) )
63 , mSelectedIds( selectedIds ) {}
64
65 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
66 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
67 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
68
69 private:
70
72 struct LineData
73 {
74 std::unique_ptr<QgsTessellator> tessellator;
75 QVector<QgsFeatureId> triangleIndexFids;
76 QVector<uint> triangleIndexStartingIndices;
77 };
78
79 void processPolygon( QgsPolygon *polyBuffered, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, LineData &out );
80
81 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected );
82
83 // input specific for this class
84 std::unique_ptr< QgsLine3DSymbol > mSymbol;
85 // inputs - generic
86 QgsFeatureIds mSelectedIds;
87
88 // outputs
89 LineData outNormal;
90 LineData outSelected;
91};
92
93
94
95bool QgsBufferedLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
96{
97 Q_UNUSED( attributeNames )
98
99 const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() );
100
101 outNormal.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true,
102 false, false, false, texturedMaterialSettings ? texturedMaterialSettings->requiresTextureCoordinates() : false,
103 3,
104 texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
105 outSelected.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true,
106 false, false, false, texturedMaterialSettings ? texturedMaterialSettings->requiresTextureCoordinates() : false,
107 3,
108 texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
109
110 return true;
111}
112
113void QgsBufferedLine3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3DRenderContext &context )
114{
115 if ( f.geometry().isNull() )
116 return;
117
118 LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
119
120 QgsGeometry geom = f.geometry();
122
123 // segmentize curved geometries if necessary
125 {
126 geom = QgsGeometry( g->segmentize() );
127 g = geom.constGet()->simplifiedTypeRef();
128 }
129
130 // TODO: configurable
131 const int nSegments = 4;
132 const Qgis::EndCapStyle endCapStyle = Qgis::EndCapStyle::Round;
133 const Qgis::JoinStyle joinStyle = Qgis::JoinStyle::Round;
134 const double mitreLimit = 0;
135
136 const QgsGeos engine( g );
137
138 double width = mSymbol->width();
139 if ( qgsDoubleNear( width, 0 ) )
140 {
141 // a zero-width buffered line should be treated like a "wall" or "fence" -- we fake this by bumping the width to a very tiny amount,
142 // so that we get a very narrow polygon shape to work with...
143 width = 0.001;
144 }
145
146 QgsAbstractGeometry *buffered = engine.buffer( width / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory
147 if ( !buffered )
148 return;
149
151 {
152 QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered );
153 processPolygon( polyBuffered, f.id(), mSymbol->height(), mSymbol->extrusionHeight(), context, out );
154 }
155 else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == Qgis::WkbType::MultiPolygon )
156 {
157 QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered );
158 for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i )
159 {
160 QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( mpolyBuffered->polygonN( i ) )->clone(); // need to clone individual geometry parts
161 processPolygon( polyBuffered, f.id(), mSymbol->height(), mSymbol->extrusionHeight(), context, out );
162 }
163 delete buffered;
164 }
165 mFeatureCount++;
166}
167
168void QgsBufferedLine3DSymbolHandler::processPolygon( QgsPolygon *polyBuffered, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, LineData &out )
169{
170 Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), height, context.map() );
171
172 Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
173 const uint startingTriangleIndex = static_cast<uint>( out.tessellator->dataVerticesCount() / 3 );
174 out.triangleIndexStartingIndices.append( startingTriangleIndex );
175 out.triangleIndexFids.append( fid );
176 out.tessellator->addPolygon( *polyBuffered, extrusionHeight );
177 delete polyBuffered;
178}
179
180void QgsBufferedLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
181{
182 // create entity for selected and not selected
183 makeEntity( parent, context, outNormal, false );
184 makeEntity( parent, context, outSelected, true );
185
186 mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
187 mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
188}
189
190
191void QgsBufferedLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected )
192{
193 if ( out.tessellator->dataVerticesCount() == 0 )
194 return; // nothing to show - no need to create the entity
195
196 QgsMaterialContext materialContext;
197 materialContext.setIsSelected( selected );
198 materialContext.setSelectionColor( context.map().selectionColor() );
199 Qt3DRender::QMaterial *mat = mSymbol->materialSettings()->toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, materialContext );
200
201 // extract vertex buffer data from tessellator
202 const QByteArray data( ( const char * )out.tessellator->data().constData(), out.tessellator->data().count() * sizeof( float ) );
203 const int nVerts = data.count() / out.tessellator->stride();
204
205 const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() );
206
207 QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry( true, false, false,
208 texturedMaterialSettings ? texturedMaterialSettings->requiresTextureCoordinates() : false );
209 geometry->setData( data, nVerts, out.triangleIndexFids, out.triangleIndexStartingIndices );
210
211 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
212 renderer->setGeometry( geometry );
213
214 // make entity
215 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
216 entity->addComponent( renderer );
217 entity->addComponent( mat );
218 entity->setParent( parent );
219
220 if ( !selected )
221 entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( QStringLiteral( "main" ) ); // temporary measure to distinguish between "selected" and "main"
222
223 // cppcheck wrongly believes entity will leak
224 // cppcheck-suppress memleak
225}
226
227
228// --------------
229
230
231class QgsSimpleLine3DSymbolHandler : public QgsFeature3DHandler
232{
233 public:
234 QgsSimpleLine3DSymbolHandler( const QgsLine3DSymbol *symbol, const QgsFeatureIds &selectedIds )
235 : mSymbol( static_cast< QgsLine3DSymbol *>( symbol->clone() ) )
236 , mSelectedIds( selectedIds )
237 {
238 }
239
240 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
241 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
242 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
243
244 private:
245
246 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
247 Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
248
249 // input specific for this class
250 std::unique_ptr< QgsLine3DSymbol > mSymbol;
251 // inputs - generic
252 QgsFeatureIds mSelectedIds;
253
254 // outputs
255 QgsLineVertexData outNormal;
256 QgsLineVertexData outSelected;
257};
258
259
260
261bool QgsSimpleLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
262{
263 Q_UNUSED( attributeNames )
264
265 outNormal.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->height(), &context.map() );
266 outSelected.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->height(), &context.map() );
267
268 return true;
269}
270
271void QgsSimpleLine3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3DRenderContext &context )
272{
273 Q_UNUSED( context )
274 if ( f.geometry().isNull() )
275 return;
276
277 QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
278
279 const QgsGeometry geom = f.geometry();
280 const QgsAbstractGeometry *g = geom.constGet();
281 if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
282 {
283 out.addLineString( *ls );
284 }
285 else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
286 {
287 for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
288 {
289 const QgsLineString *ls = mls->lineStringN( nGeom );
290 out.addLineString( *ls );
291 }
292 }
293 mFeatureCount++;
294}
295
296void QgsSimpleLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
297{
298 // create entity for selected and not selected
299 makeEntity( parent, context, outNormal, false );
300 makeEntity( parent, context, outSelected, true );
301
302 updateZRangeFromPositions( outNormal.vertices );
303 updateZRangeFromPositions( outSelected.vertices );
304}
305
306
307void QgsSimpleLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
308{
309 if ( out.indexes.isEmpty() )
310 return;
311
312 // material (only ambient color is used for the color)
313
314 QgsMaterialContext materialContext;
315 materialContext.setIsSelected( selected );
316 materialContext.setSelectionColor( context.map().selectionColor() );
317 Qt3DRender::QMaterial *mat = mSymbol->materialSettings()->toMaterial( QgsMaterialSettingsRenderingTechnique::Lines, materialContext );
318
319 // geometry renderer
320
321 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
322
323 Qt3DQGeometry *geom = out.createGeometry( entity );
324
325 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
326 renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStrip );
327 renderer->setGeometry( geom );
328 renderer->setVertexCount( out.indexes.count() );
329 renderer->setPrimitiveRestartEnabled( true );
330 renderer->setRestartIndexValue( 0 );
331
332 // make entity
333 entity->addComponent( renderer );
334 entity->addComponent( mat );
335 entity->setParent( parent );
336}
337
338
339
340// --------------
341
342
343class QgsThickLine3DSymbolHandler : public QgsFeature3DHandler
344{
345 public:
346 QgsThickLine3DSymbolHandler( const QgsLine3DSymbol *symbol, const QgsFeatureIds &selectedIds )
347 : mSymbol( static_cast< QgsLine3DSymbol * >( symbol->clone() ) )
348 , mSelectedIds( selectedIds )
349 {
350 }
351
352 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
353 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
354 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
355
356 private:
357
358
359 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
360 Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
361
362 // input specific for this class
363 std::unique_ptr< QgsLine3DSymbol > mSymbol;
364 // inputs - generic
365 QgsFeatureIds mSelectedIds;
366
367 // outputs
368 QgsLineVertexData outNormal;
369 QgsLineVertexData outSelected;
370};
371
372
373
374bool QgsThickLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
375{
376 Q_UNUSED( attributeNames )
377
378 outNormal.withAdjacency = true;
379 outSelected.withAdjacency = true;
380 outNormal.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->height(), &context.map() );
381 outSelected.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->height(), &context.map() );
382
383 return true;
384}
385
386void QgsThickLine3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3DRenderContext &context )
387{
388 Q_UNUSED( context )
389 if ( f.geometry().isNull() )
390 return;
391
392 QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
393
394 QgsGeometry geom = f.geometry();
396
397 // segmentize curved geometries if necessary
399 {
400 geom = QgsGeometry( g->segmentize() );
401 g = geom.constGet()->simplifiedTypeRef();
402 }
403
404 if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
405 {
406 out.addLineString( *ls );
407 }
408 else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
409 {
410 for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
411 {
412 const QgsLineString *ls = mls->lineStringN( nGeom );
413 out.addLineString( *ls );
414 }
415 }
416 mFeatureCount++;
417}
418
419void QgsThickLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
420{
421 // create entity for selected and not selected
422 makeEntity( parent, context, outNormal, false );
423 makeEntity( parent, context, outSelected, true );
424
425 updateZRangeFromPositions( outNormal.vertices );
426 updateZRangeFromPositions( outSelected.vertices );
427}
428
429
430void QgsThickLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
431{
432 if ( out.indexes.isEmpty() )
433 return;
434
435 // material (only ambient color is used for the color)
436 QgsMaterialContext materialContext;
437 materialContext.setIsSelected( selected );
438 materialContext.setSelectionColor( context.map().selectionColor() );
439 Qt3DRender::QMaterial *mat = mSymbol->materialSettings()->toMaterial( QgsMaterialSettingsRenderingTechnique::Lines, materialContext );
440 if ( !mat )
441 {
442 const QgsSimpleLineMaterialSettings defaultMaterial;
443 mat = defaultMaterial.toMaterial( QgsMaterialSettingsRenderingTechnique::Lines, materialContext );
444 }
445
446 if ( QgsLineMaterial *lineMaterial = dynamic_cast< QgsLineMaterial * >( mat ) )
447 lineMaterial->setLineWidth( mSymbol->width() );
448
449 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
450
451 // geometry renderer
452 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
453 renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
454 renderer->setGeometry( out.createGeometry( entity ) );
455 renderer->setVertexCount( out.indexes.count() );
456 renderer->setPrimitiveRestartEnabled( true );
457 renderer->setRestartIndexValue( 0 );
458
459 // make entity
460 entity->addComponent( renderer );
461 entity->addComponent( mat );
462 entity->setParent( parent );
463}
464
465
466// --------------
467
468
469namespace Qgs3DSymbolImpl
470{
471
472 QgsFeature3DHandler *handlerForLine3DSymbol( QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
473 {
474 const QgsLine3DSymbol *lineSymbol = dynamic_cast< const QgsLine3DSymbol * >( symbol );
475 if ( !lineSymbol )
476 return nullptr;
477
478 if ( lineSymbol->renderAsSimpleLines() )
479 return new QgsThickLine3DSymbolHandler( lineSymbol, layer->selectedFeatureIds() );
480 //return new QgsSimpleLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
481 else
482 return new QgsBufferedLine3DSymbolHandler( lineSymbol, layer->selectedFeatureIds() );
483 }
484
485 Qt3DCore::QEntity *entityForLine3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
486 {
487 QgsFeature3DHandler *handler = handlerForLine3DSymbol( layer, &symbol );
488 Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
489 delete handler;
490 return e;
491 }
492}
493
JoinStyle
Join styles for buffers.
Definition: qgis.h:1344
EndCapStyle
End cap styles for buffers.
Definition: qgis.h:1331
@ Polygon
Polygon.
@ MultiPolygon
MultiPolygon.
static void clampAltitudes(QgsLineString *lineString, Qgis::AltitudeClamping altClamp, Qgis::AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map)
Clamps altitude of vertices of a linestring according to the settings.
Definition: qgs3dutils.cpp:354
Abstract base class for all geometries.
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
Qgis::WkbType wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:99
bool renderAsSimpleLines() const
Returns whether the renderer will render data with simple lines (otherwise it uses buffer)
QgsAbstract3DSymbol * clone() const override SIP_FACTORY
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
void setIsSelected(bool isSelected)
Sets whether the material should represent a selected state.
void setSelectionColor(const QColor &color)
Sets the color for representing materials in a selected state.
Multi line string geometry collection.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
bool requiresTextureCoordinates() const
Returns true if the material requires texture coordinates to be generated during triangulation....
float textureRotation() const
Returns the texture rotation, in degrees.
Polygon geometry type.
Definition: qgspolygon.h:34
Qt3DRender::QMaterial * toMaterial(QgsMaterialSettingsRenderingTechnique technique, const QgsMaterialContext &context) const override
Creates a new QMaterial object representing the material settings.
void setData(const QByteArray &vertexBufferData, int vertexCount, const QVector< QgsFeatureId > &triangleIndexFids, const QVector< uint > &triangleIndexStartingIndices)
Initializes vertex buffer (and other members) from data that were already tessellated.
Class that takes care of tessellation of polygons into triangles.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
static bool isCurvedType(Qgis::WkbType type) SIP_HOLDGIL
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:808
static Qgis::WkbType flatType(Qgis::WkbType type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:629
@ Triangles
Triangle based rendering (default)
@ Lines
Line based rendering, requires line data.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
Qt3DCore::QGeometry Qt3DQGeometry
Definition: qgs3daxis.cpp:29
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry