QGIS API Documentation 3.43.0-Master (3ee7834ace6)
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
20#include "qgsmarkersymbol.h"
21#include "qgswindow3dengine.h"
22#include "qgslinevertexdata_p.h"
23#include "qgslinematerial_p.h"
24#include "qgsvertexid.h"
25#include "qgssymbollayer.h"
26#include "qgs3dmapsettings.h"
27#include "qgs3dutils.h"
28#include "qgslinestring.h"
29#include "qgsmessagelog.h"
30#include "qgspolygon.h"
31#include "qgssymbollayerutils.h"
33#include "qgstessellator.h"
34
35#include <Qt3DCore/QEntity>
36
37#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
38#include <Qt3DRender/QAttribute>
39#include <Qt3DRender/QBuffer>
40#include <Qt3DRender/QGeometry>
41#else
42#include <Qt3DCore/QAttribute>
43#include <Qt3DCore/QBuffer>
44#include <Qt3DCore/QGeometry>
45#endif
46
47#include <Qt3DRender/QGeometryRenderer>
48#include <QColor>
49
50
52
53
54QgsRubberBand3D::QgsRubberBand3D( Qgs3DMapSettings &map, QgsAbstract3DEngine *engine, Qt3DCore::QEntity *parentEntity, const Qgis::GeometryType geometryType )
55 : mMapSettings( &map )
56 , mEngine( engine )
57 , mGeometryType( geometryType )
58{
59 switch ( mGeometryType )
60 {
62 setupMarker( parentEntity );
63 break;
65 setupLine( parentEntity, engine );
66 setupMarker( parentEntity );
67 break;
69 setupMarker( parentEntity );
70 setupLine( parentEntity, engine );
71 setupPolygon( parentEntity );
72 break;
75 QgsDebugError( "Unknown GeometryType used in QgsRubberband3D" );
76 break;
77 }
78}
79
80void QgsRubberBand3D::setupMarker( Qt3DCore::QEntity *parentEntity )
81{
82 mMarkerEntity = new Qt3DCore::QEntity( parentEntity );
83 mMarkerGeometry = new QgsBillboardGeometry();
84 mMarkerGeometryRenderer = new Qt3DRender::QGeometryRenderer;
85 mMarkerGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
86 mMarkerGeometryRenderer->setGeometry( mMarkerGeometry );
87 mMarkerGeometryRenderer->setVertexCount( mMarkerGeometry->count() );
88
89 setMarkerType( mMarkerType );
90 mMarkerEntity->addComponent( mMarkerGeometryRenderer );
91}
92
93void QgsRubberBand3D::setupLine( Qt3DCore::QEntity *parentEntity, QgsAbstract3DEngine *engine )
94{
95 mLineEntity = new Qt3DCore::QEntity( parentEntity );
96
97 QgsLineVertexData dummyLineData;
98 mLineGeometry = dummyLineData.createGeometry( mLineEntity );
99
100 Q_ASSERT( mLineGeometry->attributes().count() == 2 );
101 mPositionAttribute = mLineGeometry->attributes().at( 0 );
102 mIndexAttribute = mLineGeometry->attributes().at( 1 );
103
104 mLineGeometryRenderer = new Qt3DRender::QGeometryRenderer;
105 mLineGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
106 mLineGeometryRenderer->setGeometry( mLineGeometry );
107 mLineGeometryRenderer->setPrimitiveRestartEnabled( true );
108 mLineGeometryRenderer->setRestartIndexValue( 0 );
109
110 mLineEntity->addComponent( mLineGeometryRenderer );
111
112 mLineMaterial = new QgsLineMaterial;
113 mLineMaterial->setLineWidth( mWidth );
114 mLineMaterial->setLineColor( mColor );
115
116 QObject::connect( engine, &QgsAbstract3DEngine::sizeChanged, mLineMaterial, [this, engine] {
117 mLineMaterial->setViewportSize( engine->size() );
118 } );
119 mLineMaterial->setViewportSize( engine->size() );
120
121 mLineEntity->addComponent( mLineMaterial );
122}
123
124void QgsRubberBand3D::setupPolygon( Qt3DCore::QEntity *parentEntity )
125{
126 mPolygonEntity = new Qt3DCore::QEntity( parentEntity );
127
128 mPolygonGeometry = new QgsTessellatedPolygonGeometry();
129
130 Qt3DRender::QGeometryRenderer *polygonGeometryRenderer = new Qt3DRender::QGeometryRenderer;
131 polygonGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
132 polygonGeometryRenderer->setGeometry( mPolygonGeometry );
133 mPolygonEntity->addComponent( polygonGeometryRenderer );
134
135 QgsPhongMaterialSettings polygonMaterialSettings = QgsPhongMaterialSettings();
136 polygonMaterialSettings.setAmbient( mColor );
137 polygonMaterialSettings.setDiffuse( mColor );
138 polygonMaterialSettings.setOpacity( DEFAULT_POLYGON_OPACITY );
139 mPolygonMaterial = polygonMaterialSettings.toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, QgsMaterialContext() );
140 mPolygonEntity->addComponent( mPolygonMaterial );
141}
142
143void QgsRubberBand3D::removePoint( int index )
144{
145 if ( QgsPolygon *polygon = qgsgeometry_cast<QgsPolygon *>( mGeometry.get() ) )
146 {
147 QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( polygon->exteriorRing() );
148 const int vertexIndex = index < 0 ? lineString->numPoints() - 1 + index : index;
149 lineString->deleteVertex( QgsVertexId( 0, 0, vertexIndex ) );
150
151 if ( lineString->numPoints() < 3 )
152 {
153 mGeometry.set( new QgsLineString( *lineString ) );
154 }
155 }
156 else if ( QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( mGeometry.get() ) )
157 {
158 const int vertexIndex = index < 0 ? lineString->numPoints() + index : index;
159 lineString->deleteVertex( QgsVertexId( 0, 0, vertexIndex ) );
160 }
161 else
162 {
163 return;
164 }
165
166 updateGeometry();
167}
168
169QgsRubberBand3D::~QgsRubberBand3D()
170{
171 if ( mPolygonEntity )
172 delete mPolygonEntity;
173 if ( mLineEntity )
174 delete mLineEntity;
175 if ( mMarkerEntity )
176 delete mMarkerEntity;
177}
178
179
180float QgsRubberBand3D::width() const
181{
182 return mWidth;
183}
184
185void QgsRubberBand3D::setWidth( float width )
186{
187 const bool isLineOrPolygon = mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon;
188 mWidth = width;
189
190 if ( isLineOrPolygon && mEdgesEnabled )
191 {
192 // when highlighting lines, the vertex markers should be wider
193 mLineMaterial->setLineWidth( width );
194 width *= 3;
195 }
196
197 mMarkerSymbol->setSize( width );
198 updateMarkerMaterial();
199}
200
201QColor QgsRubberBand3D::color() const
202{
203 return mColor;
204}
205
206void QgsRubberBand3D::setColor( const QColor color )
207{
208 const bool isLineOrPolygon = mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon;
209 mColor = color;
210
211 if ( mEdgesEnabled && isLineOrPolygon )
212 {
213 mLineMaterial->setLineColor( color );
214 }
215
216 if ( isLineOrPolygon )
217 {
218 mMarkerSymbol->setColor( color.lighter( 130 ) );
219 }
220 else
221 {
222 mMarkerSymbol->setColor( color );
223 }
224
225 if ( mMarkerSymbol->symbolLayerCount() > 0 && mMarkerSymbol->symbolLayer( 0 )->layerType() == QLatin1String( "SimpleMarker" ) && !mOutlineColor.value() )
226 {
227 mMarkerSymbol->symbolLayer( 0 )->setStrokeColor( color );
228 }
229 updateMarkerMaterial();
230
231 if ( mGeometryType == Qgis::GeometryType::Polygon )
232 {
233 if ( mPolygonMaterial )
234 mPolygonEntity->removeComponent( mPolygonMaterial );
235
236 if ( mPolygonFillEnabled )
237 {
238 QgsPhongMaterialSettings polygonMaterialSettings;
239 polygonMaterialSettings.setAmbient( mColor );
240 polygonMaterialSettings.setDiffuse( mColor );
241 polygonMaterialSettings.setOpacity( DEFAULT_POLYGON_OPACITY );
242 mPolygonMaterial = polygonMaterialSettings.toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, QgsMaterialContext() );
243 mPolygonEntity->addComponent( mPolygonMaterial );
244 }
245 }
246}
247
248QColor QgsRubberBand3D::outlineColor() const
249{
250 return mOutlineColor;
251}
252
253void QgsRubberBand3D::setOutlineColor( const QColor color )
254{
255 mOutlineColor = color;
256
257 if ( mMarkerSymbol->symbolLayerCount() > 0 && mMarkerSymbol->symbolLayer( 0 )->layerType() == QLatin1String( "SimpleMarker" ) )
258 {
259 mMarkerSymbol->symbolLayer( 0 )->setStrokeColor( color );
260 }
261 updateMarkerMaterial();
262}
263
264void QgsRubberBand3D::setMarkerType( const MarkerType marker )
265{
266 mMarkerType = marker;
267
268 const bool lineOrPolygon = mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon;
269
270 const QVariantMap props {
271 { QStringLiteral( "color" ), lineOrPolygon ? mColor.lighter( 130 ).name() : mColor.name() },
272 { QStringLiteral( "size_unit" ), QStringLiteral( "pixel" ) },
273 { QStringLiteral( "size" ), QString::number( lineOrPolygon ? mWidth * 3.f : mWidth ) },
274 { QStringLiteral( "outline_color" ), mOutlineColor.value() ? mOutlineColor.name() : mColor.name() },
275 { QStringLiteral( "outline_style" ), QgsSymbolLayerUtils::encodePenStyle( mMarkerOutlineStyle ) },
276 { QStringLiteral( "outline_width" ), QString::number( lineOrPolygon ? 0.5 : 1 ) },
277 { QStringLiteral( "name" ), mMarkerType == Square ? QStringLiteral( "square" ) : QStringLiteral( "circle" ) }
278 };
279
280 mMarkerSymbol.reset( QgsMarkerSymbol::createSimple( props ) );
281 updateMarkerMaterial();
282}
283
284QgsRubberBand3D::MarkerType QgsRubberBand3D::markerType() const
285{
286 return mMarkerType;
287}
288
289void QgsRubberBand3D::setMarkerOutlineStyle( const Qt::PenStyle style )
290{
291 mMarkerOutlineStyle = style;
292 setMarkerType( markerType() );
293}
294
295Qt::PenStyle QgsRubberBand3D::markerOutlineStyle() const
296{
297 return mMarkerOutlineStyle;
298}
299
300void QgsRubberBand3D::setMarkersEnabled( const bool enable )
301{
302 mMarkerEnabled = enable;
303 updateMarkerMaterial();
304}
305
306bool QgsRubberBand3D::hasMarkersEnabled() const
307{
308 return mMarkerEnabled;
309}
310
311void QgsRubberBand3D::setEdgesEnabled( const bool enable )
312{
313 mEdgesEnabled = enable;
314 setColor( mColor );
315}
316
317bool QgsRubberBand3D::hasEdgesEnabled() const
318{
319 return mEdgesEnabled;
320}
321
322void QgsRubberBand3D::setFillEnabled( const bool enable )
323{
324 mPolygonFillEnabled = enable;
325 setColor( mColor );
326}
327
328bool QgsRubberBand3D::hasFillEnabled() const
329{
330 return mPolygonFillEnabled;
331}
332
333void QgsRubberBand3D::reset()
334{
335 mGeometry.set( nullptr );
336 updateGeometry();
337}
338
339void QgsRubberBand3D::addPoint( const QgsPoint &pt )
340{
341 if ( QgsPolygon *polygon = qgsgeometry_cast<QgsPolygon *>( mGeometry.get() ) )
342 {
343 QgsLineString *exteriorRing = qgsgeometry_cast<QgsLineString *>( polygon->exteriorRing() );
344 const int lastVertexIndex = exteriorRing->numPoints() - 1;
345 exteriorRing->insertVertex( QgsVertexId( 0, 0, lastVertexIndex ), pt );
346 }
347 else if ( QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( mGeometry.get() ) )
348 {
349 lineString->addVertex( pt );
350 // transform linestring to polygon if we have enough vertices
351 if ( mGeometryType == Qgis::GeometryType::Polygon && lineString->numPoints() >= 3 )
352 {
353 mGeometry.set( new QgsPolygon( lineString->clone() ) );
354 }
355 }
356 else if ( !mGeometry.constGet() )
357 {
358 mGeometry.set( new QgsLineString( QVector<QgsPoint> { pt } ) );
359 }
360
361 updateGeometry();
362}
363
364void QgsRubberBand3D::setGeometry( const QgsGeometry &geometry )
365{
366 mGeometry = geometry;
367 mGeometryType = geometry.type();
368
369 updateGeometry();
370}
371
372void QgsRubberBand3D::removeLastPoint()
373{
374 removePoint( -1 );
375}
376
377void QgsRubberBand3D::removePenultimatePoint()
378{
379 removePoint( -2 );
380}
381
382void QgsRubberBand3D::moveLastPoint( const QgsPoint &pt )
383{
384 if ( QgsPolygon *polygon = qgsgeometry_cast<QgsPolygon *>( mGeometry.get() ) )
385 {
386 QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( polygon->exteriorRing() );
387 const int lastVertexIndex = lineString->numPoints() - 2;
388 lineString->moveVertex( QgsVertexId( 0, 0, lastVertexIndex ), pt );
389 }
390 else if ( QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( mGeometry.get() ) )
391 {
392 const int lastVertexIndex = lineString->numPoints() - 1;
393 lineString->moveVertex( QgsVertexId( 0, 0, lastVertexIndex ), pt );
394 }
395 else
396 {
397 return;
398 }
399
400 updateGeometry();
401}
402
403void QgsRubberBand3D::updateGeometry()
404{
405 QgsLineVertexData lineData;
406 lineData.withAdjacency = true;
408 if ( const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( mGeometry.constGet() ) )
409 {
410 std::unique_ptr< QgsLineString > lineString( qgsgeometry_cast<QgsLineString *>( polygon->exteriorRing()->clone() ) );
411 const int lastVertexIndex = lineString->numPoints() - 1;
412 lineString->deleteVertex( QgsVertexId( 0, 0, lastVertexIndex ) );
413 lineData.addLineString( *lineString, 0, true );
414 }
415 else if ( const QgsLineString *lineString = qgsgeometry_cast<const QgsLineString *>( mGeometry.constGet() ) )
416 {
417 lineData.addLineString( *lineString, 0, false );
418 }
419
420
421 if ( mEdgesEnabled && ( mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon ) )
422 {
423 mPositionAttribute->buffer()->setData( lineData.createVertexBuffer() );
424 mIndexAttribute->buffer()->setData( lineData.createIndexBuffer() );
425 mLineGeometryRenderer->setVertexCount( lineData.indexes.count() );
426 }
427
428 // first entry is empty for primitive restart
429 lineData.vertices.pop_front();
430
431 // we may not want a marker on the last point as it's tracked by the mouse cursor
432 if ( mHideLastMarker && !lineData.vertices.isEmpty() )
433 lineData.vertices.pop_back();
434
435 mMarkerGeometry->setPoints( lineData.vertices );
436 mMarkerGeometryRenderer->setVertexCount( lineData.vertices.count() );
437
438
439 if ( mGeometryType == Qgis::GeometryType::Polygon )
440 {
441 if ( const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( mGeometry.constGet() ) )
442 {
443 QgsTessellator tessellator( mMapSettings->origin().x(), mMapSettings->origin().y(), true );
444 tessellator.setOutputZUp( true );
445 tessellator.addPolygon( *polygon, 0 );
446 if ( !tessellator.error().isEmpty() )
447 {
448 QgsMessageLog::logMessage( tessellator.error(), QObject::tr( "3D" ) );
449 }
450 // extract vertex buffer data from tessellator
451 const QByteArray data( reinterpret_cast<const char *>( tessellator.data().constData() ), static_cast<int>( tessellator.data().count() * sizeof( float ) ) );
452 const int vertexCount = data.count() / tessellator.stride();
453 mPolygonGeometry->setData( data, vertexCount, QVector<QgsFeatureId>(), QVector<uint>() );
454 }
455 else
456 {
457 mPolygonGeometry->setData( QByteArray(), 0, QVector<QgsFeatureId>(), QVector<uint>() );
458 }
459 }
460}
461
462void QgsRubberBand3D::updateMarkerMaterial()
463{
464 if ( mMarkerEnabled )
465 {
466 mMarkerMaterial = new QgsPoint3DBillboardMaterial();
467 mMarkerMaterial->setTexture2DFromSymbol( mMarkerSymbol.get(), Qgs3DRenderContext::fromMapSettings( mMapSettings ) );
468 mMarkerEntity->addComponent( mMarkerMaterial );
469
470 //TODO: QgsAbstract3DEngine::sizeChanged should have const QSize &size param
471 QObject::connect( mEngine, &QgsAbstract3DEngine::sizeChanged, mMarkerMaterial, [this] {
472 mMarkerMaterial->setViewportSize( mEngine->size() );
473 } );
474 mMarkerMaterial->setViewportSize( mEngine->size() );
475 }
476 else
477 {
478 mMarkerEntity->removeComponent( mMarkerMaterial );
479 QObject::disconnect( mEngine, nullptr, mMarkerMaterial, nullptr );
480 }
481}
@ Absolute
Elevation is taken directly from feature and is independent of terrain height (final elevation = feat...
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:337
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
@ Vertex
Clamp every vertex of feature.
static Qgs3DRenderContext fromMapSettings(const Qgs3DMapSettings *mapSettings)
Creates an initialized Qgs3DRenderContext instance from given Qgs3DMapSettings.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
void sizeChanged()
Emitted after a call to setSize()
virtual QSize size() const =0
Returns size of the engine's rendering area in pixels.
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 QgsMarkerSymbol * createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
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())
Adds a message to the log instance (and creates it if necessary).
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.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Polygon geometry type.
Definition qgspolygon.h:33
static QString encodePenStyle(Qt::PenStyle style)
Class that takes care of tessellation of polygons into triangles.
@ Triangles
Triangle based rendering (default)
#define QgsDebugError(str)
Definition qgslogger.h:40
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30