QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 "qgs3dmapsettings.h"
23 //#include "qgsterraingenerator.h"
24 #include "qgs3dutils.h"
25 
26 #include "qgsvectorlayer.h"
27 #include "qgsmultilinestring.h"
28 #include "qgsmultipolygon.h"
29 #include "qgsgeos.h"
30 
31 #include <Qt3DExtras/QPhongMaterial>
32 #include <Qt3DRender/QAttribute>
33 #include <Qt3DRender/QBuffer>
34 #include <Qt3DRender/QGeometryRenderer>
35 
37 
38 
39 static Qt3DExtras::QPhongMaterial *_material( const QgsLine3DSymbol &symbol )
40 {
41  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
42 
43  material->setAmbient( symbol.material().ambient() );
44  material->setDiffuse( symbol.material().diffuse() );
45  material->setSpecular( symbol.material().specular() );
46  material->setShininess( symbol.material().shininess() );
47 
48  return material;
49 }
50 
51 
52 // -----------
53 
54 
55 class QgsBufferedLine3DSymbolHandler : public QgsFeature3DHandler
56 {
57  public:
58  QgsBufferedLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
59  : mSymbol( symbol ), mSelectedIds( selectedIds ) {}
60 
61  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
62  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
63  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
64 
65  private:
66 
68  struct LineData
69  {
70  QList<QgsPolygon *> polygons;
71  QList<QgsFeatureId> fids;
72  };
73 
74  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected );
75 
76  // input specific for this class
77  const QgsLine3DSymbol &mSymbol;
78  // inputs - generic
79  QgsFeatureIds mSelectedIds;
80 
81  // outputs
82  LineData outNormal;
83  LineData outSelected;
84 };
85 
86 
87 
88 bool QgsBufferedLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
89 {
90  Q_UNUSED( context )
91  Q_UNUSED( attributeNames )
92  return true;
93 }
94 
95 void QgsBufferedLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
96 {
97  if ( f.geometry().isNull() )
98  return;
99 
100  LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
101 
102  QgsGeometry geom = f.geometry();
103 
104  // segmentize curved geometries if necessary
105  if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
106  geom = QgsGeometry( geom.constGet()->segmentize() );
107 
108  const QgsAbstractGeometry *g = geom.constGet();
109 
110  // TODO: configurable
111  const int nSegments = 4;
114  const double mitreLimit = 0;
115 
116  QgsGeos engine( g );
117  QgsAbstractGeometry *buffered = engine.buffer( mSymbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory
118 
119  if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon )
120  {
121  QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered );
122  Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() );
123  out.polygons.append( polyBuffered );
124  out.fids.append( f.id() );
125  }
126  else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon )
127  {
128  QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered );
129  for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i )
130  {
131  QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i );
132  Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon );
133  QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts
134  Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() );
135  out.polygons.append( polyBuffered );
136  out.fids.append( f.id() );
137  }
138  delete buffered;
139  }
140 }
141 
142 
143 void QgsBufferedLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
144 {
145  // create entity for selected and not selected
146  makeEntity( parent, context, outNormal, false );
147  makeEntity( parent, context, outSelected, true );
148 }
149 
150 
151 void QgsBufferedLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected )
152 {
153  if ( out.polygons.isEmpty() )
154  return; // nothing to show - no need to create the entity
155 
156  Qt3DExtras::QPhongMaterial *mat = _material( mSymbol );
157  if ( selected )
158  {
159  // update the material with selection colors
160  mat->setDiffuse( context.map().selectionColor() );
161  mat->setAmbient( context.map().selectionColor().darker() );
162  }
163 
164  QgsPointXY origin( context.map().origin().x(), context.map().origin().y() );
166  geometry->setPolygons( out.polygons, out.fids, origin, mSymbol.extrusionHeight() );
167 
168  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
169  renderer->setGeometry( geometry );
170 
171  // make entity
172  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
173  entity->addComponent( renderer );
174  entity->addComponent( mat );
175  entity->setParent( parent );
176 
177  if ( !selected )
178  entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( QStringLiteral( "main" ) ); // temporary measure to distinguish between "selected" and "main"
179 }
180 
181 
182 // --------------
183 
184 
185 class QgsSimpleLine3DSymbolHandler : public QgsFeature3DHandler
186 {
187  public:
188  QgsSimpleLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
189  : mSymbol( symbol ), mSelectedIds( selectedIds )
190  {
191  }
192 
193  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
194  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
195  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
196 
197  private:
198 
199  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
200  Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
201 
202  // input specific for this class
203  const QgsLine3DSymbol &mSymbol;
204  // inputs - generic
205  QgsFeatureIds mSelectedIds;
206 
207  // outputs
208  QgsLineVertexData outNormal;
209  QgsLineVertexData outSelected;
210 };
211 
212 
213 
214 bool QgsSimpleLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
215 {
216  Q_UNUSED( attributeNames )
217 
218  outNormal.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
219  outSelected.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
220 
221  return true;
222 }
223 
224 void QgsSimpleLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
225 {
226  Q_UNUSED( context )
227  if ( f.geometry().isNull() )
228  return;
229 
230  QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
231 
232  QgsGeometry geom = f.geometry();
233  const QgsAbstractGeometry *g = geom.constGet();
234  if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
235  {
236  out.addLineString( *ls );
237  }
238  else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
239  {
240  for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
241  {
242  const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
243  out.addLineString( *ls );
244  }
245  }
246 }
247 
248 void QgsSimpleLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
249 {
250  // create entity for selected and not selected
251  makeEntity( parent, context, outNormal, false );
252  makeEntity( parent, context, outSelected, true );
253 }
254 
255 
256 void QgsSimpleLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
257 {
258  if ( out.indexes.isEmpty() )
259  return;
260 
261  // material (only ambient color is used for the color)
262 
263  Qt3DExtras::QPhongMaterial *mat = _material( mSymbol );
264  if ( selected )
265  {
266  // update the material with selection colors
267  mat->setAmbient( context.map().selectionColor() );
268  }
269 
270  // geometry renderer
271 
272  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
273 
274  Qt3DRender::QGeometry *geom = out.createGeometry( entity );
275 
276  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
277  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStrip );
278  renderer->setGeometry( geom );
279  renderer->setVertexCount( out.indexes.count() );
280  renderer->setPrimitiveRestartEnabled( true );
281  renderer->setRestartIndexValue( 0 );
282 
283  // make entity
284  entity->addComponent( renderer );
285  entity->addComponent( mat );
286  entity->setParent( parent );
287 }
288 
289 
290 
291 // --------------
292 
293 
294 class QgsThickLine3DSymbolHandler : public QgsFeature3DHandler
295 {
296  public:
297  QgsThickLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
298  : mSymbol( symbol ), mSelectedIds( selectedIds )
299  {
300  }
301 
302  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
303  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
304  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
305 
306  private:
307 
308 
309  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
310  Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
311 
312  // input specific for this class
313  const QgsLine3DSymbol &mSymbol;
314  // inputs - generic
315  QgsFeatureIds mSelectedIds;
316 
317  // outputs
318  QgsLineVertexData outNormal;
319  QgsLineVertexData outSelected;
320 };
321 
322 
323 
324 bool QgsThickLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
325 {
326  Q_UNUSED( attributeNames )
327 
328  outNormal.withAdjacency = true;
329  outSelected.withAdjacency = true;
330  outNormal.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
331  outSelected.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
332 
333  return true;
334 }
335 
336 void QgsThickLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
337 {
338  Q_UNUSED( context )
339  if ( f.geometry().isNull() )
340  return;
341 
342  QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
343 
344  QgsGeometry geom = f.geometry();
345  const QgsAbstractGeometry *g = geom.constGet();
346  if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
347  {
348  out.addLineString( *ls );
349  }
350  else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
351  {
352  for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
353  {
354  const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
355  out.addLineString( *ls );
356  }
357  }
358 }
359 
360 void QgsThickLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
361 {
362  // create entity for selected and not selected
363  makeEntity( parent, context, outNormal, false );
364  makeEntity( parent, context, outSelected, true );
365 }
366 
367 
368 void QgsThickLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
369 {
370  if ( out.indexes.isEmpty() )
371  return;
372 
373  // material (only ambient color is used for the color)
374 
375  QgsLineMaterial *mat = new QgsLineMaterial;
376  mat->setLineColor( mSymbol.material().ambient() );
377  mat->setLineWidth( mSymbol.width() );
378  if ( selected )
379  {
380  // update the material with selection colors
381  mat->setLineColor( context.map().selectionColor() );
382  }
383 
384  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
385 
386  // geometry renderer
387  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
388  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
389  renderer->setGeometry( out.createGeometry( entity ) );
390  renderer->setVertexCount( out.indexes.count() );
391  renderer->setPrimitiveRestartEnabled( true );
392  renderer->setRestartIndexValue( 0 );
393 
394  // make entity
395  entity->addComponent( renderer );
396  entity->addComponent( mat );
397  entity->setParent( parent );
398 }
399 
400 
401 // --------------
402 
403 
404 namespace Qgs3DSymbolImpl
405 {
406 
407  QgsFeature3DHandler *handlerForLine3DSymbol( QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
408  {
409  if ( symbol.renderAsSimpleLines() )
410  return new QgsThickLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
411  //return new QgsSimpleLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
412  else
413  return new QgsBufferedLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
414  }
415 
416  Qt3DCore::QEntity *entityForLine3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
417  {
418  QgsFeature3DHandler *handler = handlerForLine3DSymbol( layer, symbol );
419  Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
420  delete handler;
421  return e;
422  }
423 }
424 
QgsFeatureId id
Definition: qgsfeature.h:64
float shininess() const
Returns shininess of the surface.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QColor specular() const
Returns specular color component.
A class to represent a 2D point.
Definition: qgspointxy.h:43
Multi line string geometry collection.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
void setPolygons(const QList< QgsPolygon *> &polygons, const QList< QgsFeatureId > &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList< float > &extrusionHeightPerPolygon=QList< float >())
Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
EndCapStyle
End cap styles for buffers.
Definition: qgsgeometry.h:1051
3 Definition of the world
const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
3 Class derived from Qt3DRender::QGeometry that represents polygons tessellated into 3D geometry...
QgsPhongMaterialSettings material() const
Returns material used for shading of the symbol.
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
3 3D symbol that draws linestring geometries as planar polygons (created from lines using a buffer wi...
Abstract base class for all geometries.
Does vector analysis using the geos library and handles import, export, exception handling*...
Definition: qgsgeos.h:103
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
Use rounded joins.
Definition: qgsgeometry.h:1062
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool renderAsSimpleLines() const
Returns whether the renderer will render data with simple lines (otherwise it uses buffer) ...
Multi polygon geometry collection.
static bool isCurvedType(Type type)
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:609
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
QColor ambient() const
Returns ambient color component.
static void clampAltitudes(QgsLineString *lineString, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map)
Clamps altitude of vertices of a linestring according to the settings.
Definition: qgs3dutils.cpp:267
QColor diffuse() const
Returns diffuse color component.
QgsGeometry geometry
Definition: qgsfeature.h:67
Polygon geometry type.
Definition: qgspolygon.h:31
JoinStyle
Join styles for buffers.
Definition: qgsgeometry.h:1060
Represents a vector layer which manages a vector based data sets.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:430
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.