QGIS API Documentation 4.3.0-Master (52aa6503bed)
Loading...
Searching...
No Matches
qgs3dsymbolutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgs3dsymbolutils.cpp
3 --------------------------------------
4 Date : January 2026
5 Copyright : (C) 2026 by Jean Felder
6 Email : jean dot felder at oslandia 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 "qgs3dsymbolutils.h"
17
18#include "qgs3dsymbolregistry.h"
19#include "qgsabstract3dsymbol.h"
21#include "qgsapplication.h"
22#include "qgsfillsymbol.h"
23#include "qgsfillsymbollayer.h"
24#include "qgsline3dsymbol.h"
25#include "qgslinesymbol.h"
26#include "qgslinesymbollayer.h"
27#include "qgslogger.h"
28#include "qgsmarkersymbol.h"
32#include "qgspainting.h"
35#include "qgspoint3dsymbol.h"
36#include "qgspolygon3dsymbol.h"
37#include "qgsproject.h"
38#include "qgsrendercontext.h"
39#include "qgsunittypes.h"
40#include "qgsvectorlayer.h"
41
42#include <QColor>
43#include <QPainter>
44#include <QPainterPath>
45#include <QString>
46
47using namespace Qt::StringLiterals;
48
50{
51 if ( size <= 0 )
52 {
53 return size;
54 }
55
56 constexpr double MM_PER_INCH = 25.4;
57 constexpr double POINTS_PER_INCH = 72.0;
58 constexpr double DEFAULT_DPI = 96.0;
59 // Fixed conversion factor from millimeters to 3D scene units.
60 constexpr double MM_TO_3D_UNITS = 10.0;
61
62 double result = 0;
63 switch ( unit )
64 {
66 result = size * QgsUnitTypes::fromUnitToUnitFactor( units2D, units3D );
67 break;
70 break;
72 result = size * MM_TO_3D_UNITS;
73 break;
75 result = size * MM_PER_INCH / POINTS_PER_INCH * MM_TO_3D_UNITS;
76 break;
78 result = size * MM_PER_INCH * MM_TO_3D_UNITS;
79 break;
81 result = size * 25.4 / DEFAULT_DPI * MM_TO_3D_UNITS;
82 break;
85 result = size;
86 }
87
88 return result;
89}
90
92{
93 QColor color = QColor();
94
95 if ( !symbol )
96 {
97 return color;
98 }
99
100 QgsAbstractMaterialSettings *materialSettings = symbol->materialSettings();
101 if ( materialSettings )
102 {
103 color = materialSettings->averageColor();
104 }
105 else
106 {
107 QgsDebugError( u"Qgs3DSymbolUtils::vectorMaterialAverageColor: unable to retrieve material from symbol"_s );
108 }
109
110 return color;
111}
112
113QIcon Qgs3DSymbolUtils::vectorSymbolPreviewIcon( const QgsAbstract3DSymbol *symbol, const QSize &size, const QgsScreenProperties &screen, int padding )
114{
115 if ( !symbol || ( symbol->type() != "line"_L1 && symbol->type() == "point"_L1 && symbol->type() == "polygon"_L1 ) )
116 {
117 QgsDebugError( u"A vector symbol is expected by the Qgs3DSymbolUtils::vectorSymbolPreviewIcon function"_s );
118 return QIcon();
119 }
120
121 const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1;
122 QPixmap pixmap( size * devicePixelRatio );
123 pixmap.setDevicePixelRatio( devicePixelRatio );
124 pixmap.fill( Qt::transparent );
125
126 QPainter painter( &pixmap );
127 painter.setRenderHint( QPainter::Antialiasing );
128 painter.setPen( Qt::NoPen );
129
130 const QColor baseColor = Qgs3DSymbolUtils::vectorSymbolAverageColor( symbol );
131 painter.setBrush( QBrush( baseColor ) );
132
133 if ( symbol->type() == "line"_L1 )
134 {
135 // For lines, the icon size depends on the width of the line
136 // draw a line
137 const QgsLine3DSymbol *lineSymbol = dynamic_cast<const QgsLine3DSymbol *>( symbol );
138 const int lineHeight = std::min( static_cast<int>( lineSymbol->width() ), size.height() - 2 * padding );
139 const int y = ( size.height() - lineHeight ) / 2;
140 painter.drawRect( padding, y, size.width() - 2 * padding, lineHeight );
141 }
142 else if ( symbol->type() == "point"_L1 )
143 {
144 // For points, the icon size depends on the shape of the symbol
145 const QgsPoint3DSymbol *pointSymbol = dynamic_cast<const QgsPoint3DSymbol *>( symbol );
146 switch ( pointSymbol->shape() )
147 {
149 {
150 // draw a projected cone
151 float bottomRadius = pointSymbol->shapeProperty( u"bottomRadius"_s ).toFloat();
152 float topRadius = 2 * pointSymbol->shapeProperty( u"topRadius"_s ).toFloat();
153 float length = pointSymbol->shapeProperty( u"length"_s ).toFloat();
154
155 const float maxRadius = static_cast<float>( size.width() ) / 2.0f - static_cast<float>( padding );
156 const float maxLength = static_cast<float>( size.height() ) - 2.0f * static_cast<float>( padding );
157
158 bottomRadius = std::min( bottomRadius, maxRadius );
159 topRadius = std::min( topRadius, maxRadius );
160 length = std::min( length, maxLength );
161
162 const float centerX = static_cast<float>( size.width() ) / 2.0f;
163 const float availableHeight = static_cast<float>( size.height() - 2 * padding );
164 const float topY = static_cast<float>( padding ) + ( availableHeight - length ) / 2.0f;
165 const float bottomY = topY + length;
166
167 QPainterPath path;
168 const QPointF topLeft( centerX - topRadius, topY );
169 const QPointF topRight( centerX + topRadius, topY );
170 const QPointF bottomLeft( centerX - bottomRadius, bottomY );
171 const QPointF bottomRight( centerX + bottomRadius, bottomY );
172
173 path.moveTo( topLeft );
174 path.lineTo( bottomLeft );
175 path.lineTo( bottomRight );
176 path.lineTo( topRight );
177 path.closeSubpath();
178 painter.fillPath( path, painter.brush() );
179 break;
180 }
183 {
184 // draw a rectangle
185 const int pointSize = static_cast<int>( pointSymbol->shapeProperty( u"size"_s ).toFloat() );
186 const int rectSize = std::min( std::min( pointSize, size.height() ), size.width() ) - 2 * padding;
187 const int rectX = ( size.width() - rectSize ) / 2;
188 const int rectY = ( size.height() - rectSize ) / 2;
189 painter.drawRect( rectX, rectY, rectSize, rectSize );
190 break;
191 }
193 {
194 // draw a projected cylinder
195 float radius = pointSymbol->shapeProperty( u"radius"_s ).toFloat();
196 float length = pointSymbol->shapeProperty( u"length"_s ).toFloat();
197 float centerX = static_cast<float>( size.width() ) / 2.0f;
198
199 float maxRadius = static_cast<float>( size.width() ) / 2.0f - static_cast<float>( padding );
200 float maxLength = static_cast<float>( size.height() - 2 * padding );
201 radius = std::min( radius, maxRadius );
202 length = std::min( length, maxLength );
203
204 float availableHeight = static_cast<float>( size.height() - 2 * padding );
205 float topY = static_cast<float>( padding ) + ( availableHeight - length ) / 2.0f;
206 float bottomY = topY + length;
207
208 QPainterPath path;
209 QPointF topLeft( centerX - radius, topY );
210 QPointF topRight( centerX + radius, topY );
211 QPointF bottomLeft( centerX - radius, bottomY );
212 QPointF bottomRight( centerX + radius, bottomY );
213
214 path.moveTo( topLeft );
215 path.lineTo( bottomLeft );
216 path.lineTo( bottomRight );
217 path.lineTo( topRight );
218 path.closeSubpath();
219
220 painter.fillPath( path, painter.brush() );
221 painter.drawPath( path );
222 break;
223 }
225 {
226 // draw a circle
227 const float pointSize = 2 * pointSymbol->shapeProperty( u"radius"_s ).toFloat();
228 const int diameter = std::min( std::min( static_cast<int>( pointSize ), size.width() ), size.height() ) - 2 * padding;
229 const int x = ( size.width() - diameter ) / 2;
230 const int y = ( size.height() - diameter ) / 2;
231 painter.drawEllipse( x, y, diameter, diameter );
232 break;
233 }
235 {
236 // draw a projected torus
237 float radius = pointSymbol->shapeProperty( u"radius"_s ).toFloat();
238 float minorRadius = pointSymbol->shapeProperty( u"minorRadius"_s ).toFloat();
239
240 float maxRadius = std::min( static_cast<float>( size.width() ), static_cast<float>( size.height() ) ) / 2.0f - static_cast<float>( padding );
241 radius = std::min( radius, maxRadius );
242 minorRadius = std::min( minorRadius, radius );
243
244 // Exterior ellipse
245 const float centerX = static_cast<float>( size.width() ) / 2.0f;
246 const float centerY = static_cast<float>( size.height() ) / 2.0f;
247 painter.drawEllipse( QPointF( centerX, centerY ), radius, radius );
248
249 // Interior ellipse
250 QgsScopedQPainterState painterState( &painter );
251 painter.setCompositionMode( QPainter::CompositionMode_Clear );
252 painter.drawEllipse( QPointF( centerX, centerY ), minorRadius, minorRadius );
253 break;
254 }
258 {
259 // fallback - draw a rectangle
260 const int shapeSize = 10;
261 const int rectSize = std::min( std::min( shapeSize, size.height() ), size.width() ) - 2 * padding;
262 const int rectX = ( size.width() - rectSize ) / 2;
263 const int rectY = ( size.height() - rectSize ) / 2;
264 painter.drawRect( rectX, rectY, rectSize, rectSize );
265 break;
266 }
267 }
268 }
269 else if ( symbol->type() == "polygon"_L1 )
270 {
271 // draw a rectangle
272 const QgsPolygon3DSymbol *polygonSymbol = dynamic_cast<const QgsPolygon3DSymbol *>( symbol );
273 if ( polygonSymbol->edgesEnabled() )
274 {
275 painter.setPen( QPen( polygonSymbol->edgeColor(), polygonSymbol->edgeWidth() ) );
276 }
277 painter.drawRect( padding, padding, size.width() - 2 * padding, size.height() - 2 * padding );
278 }
279
280 painter.end();
281 return QIcon( pixmap );
282}
283
285{
286 if ( !symbol )
287 {
288 return false;
289 }
290
291 bool colorSet = false;
292 QgsAbstractMaterialSettings *materialSettings = symbol->materialSettings();
293 if ( materialSettings )
294 {
295 materialSettings->setColorsFromBase( baseColor );
296 colorSet = true;
297 }
298 else
299 {
300 QgsDebugError( u"Qgs3DSymbolUtils::setVectorSymbolBaseColor: unable to retrieve material from symbol"_s );
301 }
302
303 return colorSet;
304}
305
307{
308 bool copied = false;
309
310 if ( fromSymbol && toSymbol )
311 {
312 toSymbol->setMaterialSettings( fromSymbol->materialSettings()->clone() );
313 copied = true;
314 }
315 else
316 {
317 QgsDebugError( u"Qgs3DSymbolUtils::copyVectorSymbolMaterial: unable to retrieve material from symbols"_s );
318 }
319
320 return copied;
321}
322
323std::unique_ptr<QgsAbstract3DSymbol> Qgs3DSymbolUtils::create3DSymbolFrom2D( const QgsVectorLayer *vLayer, const QgsSymbol *symbol2D )
324{
325 if ( !symbol2D || !vLayer )
326 {
327 return nullptr;
328 }
329
330 const QgsProject *project = vLayer->project();
331 const QgsCoordinateReferenceSystem crs2D = project ? project->crs() : vLayer->crs();
332 const QgsCoordinateReferenceSystem crs3D = project ? project->crs3D() : vLayer->crs();
333 const Qgis::DistanceUnit units3D = crs3D.isGeographic() ? Qgis::DistanceUnit::Meters : crs3D.mapUnits();
334
335 std::unique_ptr<QgsAbstract3DSymbol> symbol3D = QgsApplication::symbol3DRegistry()->defaultSymbolForGeometryType( vLayer->geometryType() );
336 symbol3D->setDefaultPropertiesFromLayer( vLayer );
337
338 // set the main color
339 Qgs3DSymbolUtils::setVectorSymbolBaseColor( symbol3D.get(), symbol2D->color() );
340 if ( symbol3D->type() == "line"_L1 )
341 {
342 QgsLine3DSymbol *lineSymbol3D = qgis::down_cast<QgsLine3DSymbol *>( symbol3D.get() );
343 // lines geometry type - retrieve its width
344 if ( const QgsLineSymbol *lineSymbol = qgis::down_cast<const QgsLineSymbol *>( symbol2D ) )
345 {
346 if ( lineSymbol->symbolLayerCount() > 0 )
347 {
348 const QgsSymbolLayer *symbolLayer = lineSymbol->symbolLayer( 0 );
349 if ( const QgsSimpleLineSymbolLayer *simpleLineLayer = qgis::down_cast<const QgsSimpleLineSymbolLayer *>( symbolLayer ) )
350 {
351 const double lineWidthPixels = convert2DSizeTo3DSceneUnits( simpleLineLayer->width(), simpleLineLayer->widthUnit(), crs2D.mapUnits(), units3D );
352 lineSymbol3D->setWidth( static_cast<float>( lineWidthPixels ) );
353 }
354 }
355 }
356 }
357 else if ( symbol3D->type() == "point"_L1 )
358 {
359 QgsPoint3DSymbol *pointSymbol3D = qgis::down_cast<QgsPoint3DSymbol *>( symbol3D.get() );
360 if ( const QgsMarkerSymbol *markerSymbol = qgis::down_cast<const QgsMarkerSymbol *>( symbol2D ) )
361 {
362 if ( markerSymbol->symbolLayerCount() > 0 )
363 {
364 const QgsSymbolLayer *symbolLayer = markerSymbol->symbolLayer( 0 );
365 if ( const QgsSimpleMarkerSymbolLayer *simpleMarkerLayer = qgis::down_cast<const QgsSimpleMarkerSymbolLayer *>( symbolLayer ) )
366 {
367 const double sizeMapUnits = convert2DSizeTo3DSceneUnits( markerSymbol->size(), markerSymbol->sizeUnit(), crs2D.mapUnits(), units3D );
368
369 switch ( simpleMarkerLayer->shape() )
370 {
372 {
373 pointSymbol3D->setShape( Qgis::Point3DShape::Sphere );
374 QVariantMap vmSphere;
375 vmSphere[u"radius"_s] = sizeMapUnits / 2.;
376 pointSymbol3D->setShapeProperties( vmSphere );
377 break;
378 }
380 {
381 pointSymbol3D->setShape( Qgis::Point3DShape::Cube );
382 QVariantMap vmCube;
383 vmCube[u"size"_s] = sizeMapUnits;
384 pointSymbol3D->setShapeProperties( vmCube );
385 break;
386 }
387
390 {
391 QVariantMap vmCone;
392 vmCone[u"length"_s] = sizeMapUnits;
393 vmCone[u"topRadius"_s] = sizeMapUnits / 10.;
394 vmCone[u"bottomRadius"_s] = sizeMapUnits / 2.;
395 pointSymbol3D->setShapeProperties( vmCone );
396 pointSymbol3D->setShape( Qgis::Point3DShape::Cone );
397 break;
398 }
399
400 default:
401 break;
402 }
403 }
404 }
405 }
406 }
407
408 // handle opacity
409 QgsAbstractMaterialSettings *materialSettings = symbol3D->materialSettings();
410 if ( materialSettings->type() == "phong"_L1 )
411 {
412 QgsPhongMaterialSettings *phongSettings = qgis::down_cast<QgsPhongMaterialSettings *>( materialSettings );
413 phongSettings->setOpacity( symbol2D->opacity() );
414 }
415 else if ( materialSettings->type() == "phongtextured"_L1 )
416 {
417 QgsPhongTexturedMaterialSettings *phongTexturedSettings = qgis::down_cast<QgsPhongTexturedMaterialSettings *>( materialSettings );
418 phongTexturedSettings->setOpacity( symbol2D->opacity() );
419 }
420 else if ( materialSettings->type() == "metalroughtextured"_L1 )
421 {
422 QgsMetalRoughTexturedMaterialSettings *metalTexturedSettings = qgis::down_cast<QgsMetalRoughTexturedMaterialSettings *>( materialSettings );
423 metalTexturedSettings->setOpacity( symbol2D->opacity() );
424 }
425 else if ( materialSettings->type() == "metalrough"_L1 )
426 {
427 QgsMetalRoughMaterialSettings *metalSettings = qgis::down_cast<QgsMetalRoughMaterialSettings *>( materialSettings );
428 metalSettings->setOpacity( symbol2D->opacity() );
429 }
430
431 return symbol3D;
432}
@ Plane
Flat plane.
Definition qgis.h:4437
@ Cylinder
Cylinder.
Definition qgis.h:4432
@ Torus
Torus.
Definition qgis.h:4436
@ ExtrudedText
Extruded text.
Definition qgis.h:4438
@ Model
Model.
Definition qgis.h:4439
@ Sphere
Sphere.
Definition qgis.h:4433
@ Billboard
Billboard.
Definition qgis.h:4440
DistanceUnit
Units of distance.
Definition qgis.h:5568
@ Meters
Meters.
Definition qgis.h:5569
@ EquilateralTriangle
Equilateral triangle.
Definition qgis.h:3324
@ Circle
Circle.
Definition qgis.h:3327
@ Triangle
Triangle.
Definition qgis.h:3323
@ Square
Square.
Definition qgis.h:3319
RenderUnit
Rendering size units.
Definition qgis.h:5758
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5762
@ Millimeters
Millimeters.
Definition qgis.h:5759
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5763
@ Unknown
Mixed or unknown units.
Definition qgis.h:5765
@ MapUnits
Map units.
Definition qgis.h:5760
@ Pixels
Pixels.
Definition qgis.h:5761
@ Inches
Inches.
Definition qgis.h:5764
@ MetersInMapUnits
Meters value as Map units.
Definition qgis.h:5766
std::unique_ptr< QgsAbstract3DSymbol > defaultSymbolForGeometryType(Qgis::GeometryType type)
Creates a new instance of the default 3D symbol for the specified geometry type.
static std::unique_ptr< QgsAbstract3DSymbol > create3DSymbolFrom2D(const QgsVectorLayer *vLayer, const QgsSymbol *symbol2D)
Creates a 3D symbol from a 2D symbol.
static QColor vectorSymbolAverageColor(const QgsAbstract3DSymbol *symbol)
Computes an approximate color from a 3D vector symbol.
static bool copyVectorSymbolMaterial(const QgsAbstract3DSymbol *fromSymbol, QgsAbstract3DSymbol *toSymbol)
Copies the material properties of a vector 3D symbol from one symbol to another.
static QIcon vectorSymbolPreviewIcon(const QgsAbstract3DSymbol *symbol, const QSize &size, const QgsScreenProperties &screen, int padding)
Returns an icon preview for a 3D vector symbol.
static bool setVectorSymbolBaseColor(QgsAbstract3DSymbol *symbol, const QColor &baseColor)
Sets the base color of the material settings associated with a 3D vector symbol.
Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
virtual void setMaterialSettings(QgsAbstractMaterialSettings *materialSettings)=0
Sets the material settings used for shading of the symbol.
virtual QgsAbstractMaterialSettings * materialSettings() const
Returns the 3D material settings associated with this symbol.
virtual QString type() const =0
Returns identifier of symbol type. Each 3D symbol implementation should return a different type.
Abstract base class for material settings.
virtual QgsAbstractMaterialSettings * clone() const =0
Clones the material settings.
virtual QColor averageColor() const =0
Returns an approximate color representing the blended material color.
virtual QString type() const =0
Returns the unique type name for the material.
virtual void setColorsFromBase(const QColor &baseColor)=0
Decomposes a base color into the material's color components, and sets the material's colors accordin...
static Qgs3DSymbolRegistry * symbol3DRegistry()
Returns registry of available 3D symbols.
Represents a coordinate reference system (CRS).
3D symbol that draws linestring geometries as planar polygons (created from lines using a buffer with...
float width() const
Returns width of the line symbol (in map units).
void setWidth(float width)
Sets width of the line symbol (in map units).
A line symbol type, for rendering LineString and MultiLineString geometries.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QgsProject * project() const
Returns the parent project if this map layer is added to a project.
A marker symbol type, for rendering Point and MultiPoint geometries.
A PBR metal rough shading material used for rendering.
void setOpacity(double opacity)
Sets the opacity of the surface.
A PBR metal rough shading material used for rendering with support for image texture maps.
void setOpacity(double opacity)
Sets the opacity of the surface.
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.
A Phong shading model with diffuse texture map.
void setOpacity(double opacity)
Sets opacity of the surface.
3D symbol that draws point geometries as 3D objects using one of the predefined shapes.
void setShape(Qgis::Point3DShape shape)
Sets 3D shape for points.
void setShapeProperties(const QVariantMap &properties)
Sets a key-value dictionary of point shape properties.
Qgis::Point3DShape shape() const
Returns 3D shape for points.
QVariant shapeProperty(const QString &property) const
Returns the value for a specific shape property.
3D symbol that draws polygon geometries as planar polygons, optionally extruded (with added walls).
QColor edgeColor() const
Returns edge lines color.
bool edgesEnabled() const
Returns whether edge highlighting is enabled.
float edgeWidth() const
Returns width of edge lines (in pixels).
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
QgsCoordinateReferenceSystem crs3D() const
Returns the CRS to use for the project when transforming 3D data, or when z/elevation value handling ...
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:120
Scoped object for saving and restoring a QPainter object's state.
Stores properties relating to a screen.
double devicePixelRatio() const
Returns the ratio between physical pixels and device-independent pixels for the screen.
bool isValid() const
Returns true if the properties are valid.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
Simple marker symbol layer, consisting of a rendered shape with solid fill color and a stroke.
Abstract base class for symbol layers.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:227
qreal opacity() const
Returns the opacity for the symbol.
Definition qgssymbol.h:677
QColor color() const
Returns the symbol's color.
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
Represents a vector layer which manages a vector based dataset.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
double convert2DSizeTo3DSceneUnits(double size, Qgis::RenderUnit unit, Qgis::DistanceUnit units2D, Qgis::DistanceUnit units3D)
#define QgsDebugError(str)
Definition qgslogger.h:71