diff --git a/python/PyQt6/core/auto_additions/qgsabstract3dsymbol.py b/python/PyQt6/core/auto_additions/qgsabstract3dsymbol.py index 86392a491876..48975ee682a1 100644 --- a/python/PyQt6/core/auto_additions/qgsabstract3dsymbol.py +++ b/python/PyQt6/core/auto_additions/qgsabstract3dsymbol.py @@ -8,5 +8,29 @@ QgsAbstract3DSymbol.Property.PropertyExtrusionHeight = QgsAbstract3DSymbol.Property.ExtrusionHeight QgsAbstract3DSymbol.PropertyExtrusionHeight.is_monkey_patched = True QgsAbstract3DSymbol.PropertyExtrusionHeight.__doc__ = "Extrusion height (zero means no extrusion)" -QgsAbstract3DSymbol.Property.__doc__ = "Data definable properties.\n\n" + '* ``PropertyHeight``: ' + QgsAbstract3DSymbol.Property.Height.__doc__ + '\n' + '* ``PropertyExtrusionHeight``: ' + QgsAbstract3DSymbol.Property.ExtrusionHeight.__doc__ +QgsAbstract3DSymbol.PropertyLength = QgsAbstract3DSymbol.Property.Length +QgsAbstract3DSymbol.Property.PropertyLength = QgsAbstract3DSymbol.Property.Length +QgsAbstract3DSymbol.PropertyLength.is_monkey_patched = True +QgsAbstract3DSymbol.PropertyLength.__doc__ = "Length" +QgsAbstract3DSymbol.PropertyRadius = QgsAbstract3DSymbol.Property.Radius +QgsAbstract3DSymbol.Property.PropertyRadius = QgsAbstract3DSymbol.Property.Radius +QgsAbstract3DSymbol.PropertyRadius.is_monkey_patched = True +QgsAbstract3DSymbol.PropertyRadius.__doc__ = "Radius" +QgsAbstract3DSymbol.PropertyHeight = QgsAbstract3DSymbol.Property.BottomRadius +QgsAbstract3DSymbol.Property.PropertyHeight = QgsAbstract3DSymbol.Property.BottomRadius +QgsAbstract3DSymbol.PropertyHeight.is_monkey_patched = True +QgsAbstract3DSymbol.PropertyHeight.__doc__ = "Bottom Radius" +QgsAbstract3DSymbol.PropertySize = QgsAbstract3DSymbol.Property.MinorRadius +QgsAbstract3DSymbol.Property.PropertySize = QgsAbstract3DSymbol.Property.MinorRadius +QgsAbstract3DSymbol.PropertySize.is_monkey_patched = True +QgsAbstract3DSymbol.PropertySize.__doc__ = "Minor Radius" +QgsAbstract3DSymbol.PropertyHeight = QgsAbstract3DSymbol.Property.TopRadius +QgsAbstract3DSymbol.Property.PropertyHeight = QgsAbstract3DSymbol.Property.TopRadius +QgsAbstract3DSymbol.PropertyHeight.is_monkey_patched = True +QgsAbstract3DSymbol.PropertyHeight.__doc__ = "Top Radius" +QgsAbstract3DSymbol.PropertySize = QgsAbstract3DSymbol.Property.Size +QgsAbstract3DSymbol.Property.PropertySize = QgsAbstract3DSymbol.Property.Size +QgsAbstract3DSymbol.PropertySize.is_monkey_patched = True +QgsAbstract3DSymbol.PropertySize.__doc__ = "Size" +QgsAbstract3DSymbol.Property.__doc__ = "Data definable properties.\n\n" + '* ``PropertyHeight``: ' + QgsAbstract3DSymbol.Property.Height.__doc__ + '\n' + '* ``PropertyExtrusionHeight``: ' + QgsAbstract3DSymbol.Property.ExtrusionHeight.__doc__ + '\n' + '* ``PropertyLength``: ' + QgsAbstract3DSymbol.Property.Length.__doc__ + '\n' + '* ``PropertyRadius``: ' + QgsAbstract3DSymbol.Property.Radius.__doc__ + '\n' + '* ``PropertyHeight``: ' + QgsAbstract3DSymbol.Property.BottomRadius.__doc__ + '\n' + '* ``PropertySize``: ' + QgsAbstract3DSymbol.Property.MinorRadius.__doc__ + '\n' + '* ``PropertyHeight``: ' + QgsAbstract3DSymbol.Property.TopRadius.__doc__ + '\n' + '* ``PropertySize``: ' + QgsAbstract3DSymbol.Property.Size.__doc__ # -- diff --git a/python/PyQt6/core/auto_generated/3d/qgsabstract3dsymbol.sip.in b/python/PyQt6/core/auto_generated/3d/qgsabstract3dsymbol.sip.in index f24eef013177..85c034b3af1a 100644 --- a/python/PyQt6/core/auto_generated/3d/qgsabstract3dsymbol.sip.in +++ b/python/PyQt6/core/auto_generated/3d/qgsabstract3dsymbol.sip.in @@ -65,6 +65,12 @@ Returns the list of the vector layer geometry types which are compatible with th { Height, ExtrusionHeight, + Length, + Radius, + BottomRadius, + MinorRadius, + TopRadius, + Size, }; static const QgsPropertiesDefinition &propertyDefinitions(); diff --git a/python/core/auto_additions/qgsabstract3dsymbol.py b/python/core/auto_additions/qgsabstract3dsymbol.py index 86392a491876..48975ee682a1 100644 --- a/python/core/auto_additions/qgsabstract3dsymbol.py +++ b/python/core/auto_additions/qgsabstract3dsymbol.py @@ -8,5 +8,29 @@ QgsAbstract3DSymbol.Property.PropertyExtrusionHeight = QgsAbstract3DSymbol.Property.ExtrusionHeight QgsAbstract3DSymbol.PropertyExtrusionHeight.is_monkey_patched = True QgsAbstract3DSymbol.PropertyExtrusionHeight.__doc__ = "Extrusion height (zero means no extrusion)" -QgsAbstract3DSymbol.Property.__doc__ = "Data definable properties.\n\n" + '* ``PropertyHeight``: ' + QgsAbstract3DSymbol.Property.Height.__doc__ + '\n' + '* ``PropertyExtrusionHeight``: ' + QgsAbstract3DSymbol.Property.ExtrusionHeight.__doc__ +QgsAbstract3DSymbol.PropertyLength = QgsAbstract3DSymbol.Property.Length +QgsAbstract3DSymbol.Property.PropertyLength = QgsAbstract3DSymbol.Property.Length +QgsAbstract3DSymbol.PropertyLength.is_monkey_patched = True +QgsAbstract3DSymbol.PropertyLength.__doc__ = "Length" +QgsAbstract3DSymbol.PropertyRadius = QgsAbstract3DSymbol.Property.Radius +QgsAbstract3DSymbol.Property.PropertyRadius = QgsAbstract3DSymbol.Property.Radius +QgsAbstract3DSymbol.PropertyRadius.is_monkey_patched = True +QgsAbstract3DSymbol.PropertyRadius.__doc__ = "Radius" +QgsAbstract3DSymbol.PropertyHeight = QgsAbstract3DSymbol.Property.BottomRadius +QgsAbstract3DSymbol.Property.PropertyHeight = QgsAbstract3DSymbol.Property.BottomRadius +QgsAbstract3DSymbol.PropertyHeight.is_monkey_patched = True +QgsAbstract3DSymbol.PropertyHeight.__doc__ = "Bottom Radius" +QgsAbstract3DSymbol.PropertySize = QgsAbstract3DSymbol.Property.MinorRadius +QgsAbstract3DSymbol.Property.PropertySize = QgsAbstract3DSymbol.Property.MinorRadius +QgsAbstract3DSymbol.PropertySize.is_monkey_patched = True +QgsAbstract3DSymbol.PropertySize.__doc__ = "Minor Radius" +QgsAbstract3DSymbol.PropertyHeight = QgsAbstract3DSymbol.Property.TopRadius +QgsAbstract3DSymbol.Property.PropertyHeight = QgsAbstract3DSymbol.Property.TopRadius +QgsAbstract3DSymbol.PropertyHeight.is_monkey_patched = True +QgsAbstract3DSymbol.PropertyHeight.__doc__ = "Top Radius" +QgsAbstract3DSymbol.PropertySize = QgsAbstract3DSymbol.Property.Size +QgsAbstract3DSymbol.Property.PropertySize = QgsAbstract3DSymbol.Property.Size +QgsAbstract3DSymbol.PropertySize.is_monkey_patched = True +QgsAbstract3DSymbol.PropertySize.__doc__ = "Size" +QgsAbstract3DSymbol.Property.__doc__ = "Data definable properties.\n\n" + '* ``PropertyHeight``: ' + QgsAbstract3DSymbol.Property.Height.__doc__ + '\n' + '* ``PropertyExtrusionHeight``: ' + QgsAbstract3DSymbol.Property.ExtrusionHeight.__doc__ + '\n' + '* ``PropertyLength``: ' + QgsAbstract3DSymbol.Property.Length.__doc__ + '\n' + '* ``PropertyRadius``: ' + QgsAbstract3DSymbol.Property.Radius.__doc__ + '\n' + '* ``PropertyHeight``: ' + QgsAbstract3DSymbol.Property.BottomRadius.__doc__ + '\n' + '* ``PropertySize``: ' + QgsAbstract3DSymbol.Property.MinorRadius.__doc__ + '\n' + '* ``PropertyHeight``: ' + QgsAbstract3DSymbol.Property.TopRadius.__doc__ + '\n' + '* ``PropertySize``: ' + QgsAbstract3DSymbol.Property.Size.__doc__ # -- diff --git a/python/core/auto_generated/3d/qgsabstract3dsymbol.sip.in b/python/core/auto_generated/3d/qgsabstract3dsymbol.sip.in index 13016926f696..4f99b2575716 100644 --- a/python/core/auto_generated/3d/qgsabstract3dsymbol.sip.in +++ b/python/core/auto_generated/3d/qgsabstract3dsymbol.sip.in @@ -65,6 +65,12 @@ Returns the list of the vector layer geometry types which are compatible with th { Height, ExtrusionHeight, + Length, + Radius, + BottomRadius, + MinorRadius, + TopRadius, + Size, }; static const QgsPropertiesDefinition &propertyDefinitions(); diff --git a/src/3d/symbols/qgspoint3dsymbol.cpp b/src/3d/symbols/qgspoint3dsymbol.cpp index 47236c92b850..d7cac9f2be57 100644 --- a/src/3d/symbols/qgspoint3dsymbol.cpp +++ b/src/3d/symbols/qgspoint3dsymbol.cpp @@ -88,6 +88,10 @@ void QgsPoint3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext &c elem.appendChild( symbolElem ); } + + QDomElement elemDDP = doc.createElement( QStringLiteral( "data-defined-properties" ) ); + mDataDefinedProperties.writeXml( elemDDP, propertyDefinitions() ); + elem.appendChild( elemDDP ); } void QgsPoint3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteContext &context ) @@ -114,6 +118,10 @@ void QgsPoint3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteConte const QDomElement symbolElem = elem.firstChildElement( QStringLiteral( "symbol" ) ); setBillboardSymbol( QgsSymbolLayerUtils::loadSymbol< QgsMarkerSymbol >( symbolElem, context ) ); + + const QDomElement elemDDP = elem.firstChildElement( QStringLiteral( "data-defined-properties" ) ); + if ( !elemDDP.isNull() ) + mDataDefinedProperties.readXml( elemDDP, propertyDefinitions() ); } QList QgsPoint3DSymbol::compatibleGeometryTypes() const diff --git a/src/3d/symbols/qgspoint3dsymbol_p.cpp b/src/3d/symbols/qgspoint3dsymbol_p.cpp index 291a79118e24..eac763c34e43 100644 --- a/src/3d/symbols/qgspoint3dsymbol_p.cpp +++ b/src/3d/symbols/qgspoint3dsymbol_p.cpp @@ -85,17 +85,38 @@ class QgsInstancedPoint3DSymbolHandler : public QgsFeature3DHandler private: - static Qt3DRender::QMaterial *material( const QgsPoint3DSymbol *symbol, const QgsMaterialContext &materialContext ); - static Qt3DRender::QGeometryRenderer *renderer( const QgsPoint3DSymbol *symbol, const QVector &positions ); - static Qt3DQGeometry *symbolGeometry( const QgsPoint3DSymbol *symbol ); - //! temporary data we will pass to the tessellator - struct PointData + class PointData { - QVector positions; // contains triplets of float x,y,z for each point + public: + + PointData() = default; + + float length = 0.0f; + float radius = 0.0f; + float bottomRadius = 0.0f; + float minorRadius = 0.0f; + float topRadius = 0.0f; + float size = 0.0f; + QVector positions; + + bool operator==( const PointData &other ) const + { + return ( length == other.length && radius == other.radius + && bottomRadius == other.bottomRadius && minorRadius == other.minorRadius + && topRadius == other.topRadius && size == other.size ); + } }; - void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected ); + //!< Create a material from a point symbol and context. This is call by makeEntity(). + static Qt3DRender::QMaterial *material( const QgsPoint3DSymbol *symbol, const QgsMaterialContext &materialContext ); + //!< Create a geometry renderer from a point symbol, position and shape. This is call by makeEntity(). + static Qt3DRender::QGeometryRenderer *renderer( const QgsPoint3DSymbol *symbol, const PointData &pointData ); + //!< Create a geometry from a point symbol and shape. This is call by renderer(). + static Qt3DQGeometry *symbolGeometry( const QgsPoint3DSymbol *symbol, const PointData &pointData ); + + //!< Create one entity per PointData. This is called by finalize(). + void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QVector &out, bool selected ); // input specific for this class std::unique_ptr< QgsPoint3DSymbol > mSymbol; @@ -103,26 +124,89 @@ class QgsInstancedPoint3DSymbolHandler : public QgsFeature3DHandler QgsFeatureIds mSelectedIds; // outputs - PointData outNormal; //!< Features that are not selected - PointData outSelected; //!< Features that are selected + QVector outNormal; //!< Features that are not selected + QVector outSelected; //!< Features that are selected }; - bool QgsInstancedPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) { - Q_UNUSED( context ) - Q_UNUSED( attributeNames ) + QSet attrs = mSymbol->dataDefinedProperties().referencedFields( context.expressionContext() ); + attributeNames.unite( attrs ); return true; } void QgsInstancedPoint3DSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) { - PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal; + QVector &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal; if ( feature.geometry().isNull() ) return; - Qgs3DUtils::extractPointPositions( feature, context.map(), mSymbol->altitudeClamping(), out.positions ); + const QgsPropertyCollection &ddp = mSymbol->dataDefinedProperties(); + const bool hasDDLength = ddp.isActive( QgsAbstract3DSymbol::Property::Length ); + float length = mSymbol->shapeProperty( QStringLiteral( "length" ) ).toFloat(); + if ( hasDDLength ) + { + length = static_cast( ddp.valueAsDouble( QgsAbstract3DSymbol::Property::Length, context.expressionContext(), length ) ); + } + + const bool hasDDRadius = ddp.isActive( QgsAbstract3DSymbol::Property::Radius ); + float radius = mSymbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat(); + if ( hasDDRadius ) + { + radius = static_cast( ddp.valueAsDouble( QgsAbstract3DSymbol::Property::Radius, context.expressionContext(), radius ) ); + } + + const bool hasDDBottomRadius = ddp.isActive( QgsAbstract3DSymbol::Property::BottomRadius ); + float bottomRadius = mSymbol->shapeProperty( QStringLiteral( "bottomRadius" ) ).toFloat(); + if ( hasDDBottomRadius ) + { + bottomRadius = static_cast( ddp.valueAsDouble( QgsAbstract3DSymbol::Property::BottomRadius, context.expressionContext(), bottomRadius ) ); + } + + const bool hasDDMinorRadius = ddp.isActive( QgsAbstract3DSymbol::Property::MinorRadius ); + float minorRadius = mSymbol->shapeProperty( QStringLiteral( "minorRadius" ) ).toFloat(); + if ( hasDDMinorRadius ) + { + minorRadius = static_cast( ddp.valueAsDouble( QgsAbstract3DSymbol::Property::MinorRadius, context.expressionContext(), minorRadius ) ); + } + + const bool hasDDTopRadius = ddp.isActive( QgsAbstract3DSymbol::Property::TopRadius ); + float topRadius = mSymbol->shapeProperty( QStringLiteral( "topRadius" ) ).toFloat(); + if ( hasDDTopRadius ) + { + topRadius = static_cast( ddp.valueAsDouble( QgsAbstract3DSymbol::Property::TopRadius, context.expressionContext(), topRadius ) ); + } + + const bool hasDDSize = ddp.isActive( QgsAbstract3DSymbol::Property::Size ); + float size = mSymbol->shapeProperty( QStringLiteral( "size" ) ).toFloat(); + if ( hasDDSize ) + { + size = static_cast( ddp.valueAsDouble( QgsAbstract3DSymbol::Property::Size, context.expressionContext(), size ) ); + } + + QVector positions; + Qgs3DUtils::extractPointPositions( feature, context.map(), mSymbol->altitudeClamping(), positions ); + + PointData newPointData; + newPointData.length = length; + newPointData.radius = radius; + newPointData.bottomRadius = bottomRadius; + newPointData.minorRadius = minorRadius; + newPointData.topRadius = topRadius; + newPointData.size = size; + + PointData *existingPointData = std::find( out.begin(), out.end(), newPointData ); + if ( existingPointData == out.end() ) + { + newPointData.positions = QVector {positions[0]}; + out.append( newPointData ); + } + else + { + existingPointData->positions.append( positions[0] ); + } + mFeatureCount++; } @@ -131,8 +215,15 @@ void QgsInstancedPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, cons makeEntity( parent, context, outNormal, false ); makeEntity( parent, context, outSelected, true ); - updateZRangeFromPositions( outNormal.positions ); - updateZRangeFromPositions( outSelected.positions ); + for ( PointData &pointData : outNormal ) + { + updateZRangeFromPositions( pointData.positions ); + } + + for ( PointData &pointData : outSelected ) + { + updateZRangeFromPositions( pointData.positions ); + } // the elevation offset is applied in the vertex shader so let's account for it as well const float symbolOffset = mSymbol->transform().data()[13]; @@ -143,41 +234,101 @@ void QgsInstancedPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, cons { case Qgis::Point3DShape::Cylinder: { - const float length = mSymbol->shapeProperty( QStringLiteral( "length" ) ).toFloat(); - mZMin -= length * 0.5f; - mZMax += length * 0.5f; + float lengthMax = 0.0; + for ( const PointData &pointData : outNormal ) + { + if ( pointData.length > lengthMax ) + lengthMax = pointData.length; + } + + for ( const PointData &pointData : outSelected ) + { + if ( pointData.length > lengthMax ) + lengthMax = pointData.length; + } + + mZMin -= lengthMax * 0.5f; + mZMax += lengthMax * 0.5f; break; } case Qgis::Point3DShape::Sphere: { - const float radius = mSymbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat(); - mZMin -= radius; - mZMax += radius; + float radiusMax = 0.0; + for ( const PointData &pointData : outNormal ) + { + if ( pointData.radius > radiusMax ) + radiusMax = pointData.radius; + } + + for ( const PointData &pointData : outSelected ) + { + if ( pointData.radius > radiusMax ) + radiusMax = pointData.radius; + } + + mZMin -= radiusMax; + mZMax += radiusMax; break; } case Qgis::Point3DShape::Cone: { - const float length = mSymbol->shapeProperty( QStringLiteral( "length" ) ).toFloat(); - mZMin -= length * 0.5f; - mZMax += length * 0.5f; + float lengthMax = 0.0; + for ( const PointData &pointData : outNormal ) + { + if ( pointData.length > lengthMax ) + lengthMax = pointData.length; + } + + for ( const PointData &pointData : outSelected ) + { + if ( pointData.length > lengthMax ) + lengthMax = pointData.length; + } + + mZMin -= lengthMax * 0.5f; + mZMax += lengthMax * 0.5f; break; } case Qgis::Point3DShape::Cube: { - const float size = mSymbol->shapeProperty( QStringLiteral( "size" ) ).toFloat(); - mZMin -= size * 0.5f; - mZMax += size * 0.5f; + float sizeMax = 0.0; + for ( const PointData &pointData : outNormal ) + { + if ( pointData.size > sizeMax ) + sizeMax = pointData.size; + } + + for ( const PointData &pointData : outSelected ) + { + if ( pointData.size > sizeMax ) + sizeMax = pointData.size; + } + + mZMin -= sizeMax * 0.5f; + mZMax += sizeMax * 0.5f; break; } case Qgis::Point3DShape::Torus: { - const float radius = mSymbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat(); - mZMin -= radius; - mZMax += radius; + float radiusMax = 0.0; + for ( const PointData &pointData : outNormal ) + { + if ( pointData.radius > radiusMax ) + radiusMax = pointData.radius; + } + + for ( const PointData &pointData : outSelected ) + { + if ( pointData.radius > radiusMax ) + radiusMax = pointData.radius; + } + + mZMin -= radiusMax; + mZMax += radiusMax; break; } @@ -185,9 +336,21 @@ void QgsInstancedPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, cons { // worst case scenario -- even though planes are usually rotated so that they are flat, // let's account for possible overridden rotation - const float size = mSymbol->shapeProperty( QStringLiteral( "size" ) ).toFloat(); - mZMin -= size * 0.5f; - mZMax += size * 0.5f; + float sizeMax = 0.0; + for ( const PointData &pointData : outNormal ) + { + if ( pointData.size > sizeMax ) + sizeMax = pointData.size; + } + + for ( const PointData &pointData : outSelected ) + { + if ( pointData.size > sizeMax ) + sizeMax = pointData.size; + } + + mZMin -= sizeMax * 0.5f; + mZMax += sizeMax * 0.5f; break; } @@ -201,7 +364,7 @@ void QgsInstancedPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, cons mZMax += symbolOffset; } -void QgsInstancedPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected ) +void QgsInstancedPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QVector &out, bool selected ) { // build the default material QgsMaterialContext materialContext; @@ -210,10 +373,13 @@ void QgsInstancedPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, co Qt3DRender::QMaterial *mat = material( mSymbol.get(), materialContext ); // build the entity - Qt3DCore::QEntity *entity = new Qt3DCore::QEntity; - entity->addComponent( renderer( mSymbol.get(), out.positions ) ); - entity->addComponent( mat ); - entity->setParent( parent ); + for ( const PointData *pointData = out.cbegin(), *end = out.cend(); pointData != end; ++pointData ) + { + Qt3DCore::QEntity *entity = new Qt3DCore::QEntity; + entity->addComponent( renderer( mSymbol.get(), *pointData ) ); + entity->addComponent( mat ); + entity->setParent( parent ); + } // cppcheck wrongly believes entity will leak // cppcheck-suppress memleak @@ -274,10 +440,11 @@ Qt3DRender::QMaterial *QgsInstancedPoint3DSymbolHandler::material( const QgsPoin return material; } -Qt3DRender::QGeometryRenderer *QgsInstancedPoint3DSymbolHandler::renderer( const QgsPoint3DSymbol *symbol, const QVector &positions ) +Qt3DRender::QGeometryRenderer *QgsInstancedPoint3DSymbolHandler::renderer( const QgsPoint3DSymbol *symbol, const PointData &pointData ) { + const QVector positions = pointData.positions; const int count = positions.count(); - const int byteCount = positions.count() * sizeof( QVector3D ); + const int byteCount = static_cast( count * sizeof( QVector3D ) ); QByteArray ba; ba.resize( byteCount ); memcpy( ba.data(), positions.constData(), byteCount ); @@ -296,7 +463,7 @@ Qt3DRender::QGeometryRenderer *QgsInstancedPoint3DSymbolHandler::renderer( const instanceDataAttribute->setCount( count ); instanceDataAttribute->setByteStride( 3 * sizeof( float ) ); - Qt3DQGeometry *geometry = symbolGeometry( symbol ); + Qt3DQGeometry *geometry = symbolGeometry( symbol, pointData ); geometry->addAttribute( instanceDataAttribute ); geometry->setBoundingVolumePositionAttribute( instanceDataAttribute ); @@ -307,72 +474,61 @@ Qt3DRender::QGeometryRenderer *QgsInstancedPoint3DSymbolHandler::renderer( const return renderer; } -Qt3DQGeometry *QgsInstancedPoint3DSymbolHandler::symbolGeometry( const QgsPoint3DSymbol *symbol ) +Qt3DQGeometry *QgsInstancedPoint3DSymbolHandler::symbolGeometry( const QgsPoint3DSymbol *symbol, const PointData &pointData ) { switch ( symbol->shape() ) { case Qgis::Point3DShape::Cylinder: { - const float radius = symbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat(); - const float length = symbol->shapeProperty( QStringLiteral( "length" ) ).toFloat(); - Qt3DExtras::QCylinderGeometry *g = new Qt3DExtras::QCylinderGeometry; - //g->setRings(2); // how many vertices vertically - //g->setSlices(8); // how many vertices on circumference - g->setRadius( radius ); - g->setLength( length ); - return g; + Qt3DExtras::QCylinderGeometry *geometry = new Qt3DExtras::QCylinderGeometry; + // geometry->setRings(2); // how many vertices vertically + // geometry->setSlices(8); // how many vertices on circumference + geometry->setRadius( pointData.radius ); + geometry->setLength( pointData.length ); + return geometry; } case Qgis::Point3DShape::Sphere: { - const float radius = symbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat(); - Qt3DExtras::QSphereGeometry *g = new Qt3DExtras::QSphereGeometry; - g->setRadius( radius ); - return g; + Qt3DExtras::QSphereGeometry *geometry = new Qt3DExtras::QSphereGeometry; + geometry->setRadius( pointData.radius ); + return geometry; } case Qgis::Point3DShape::Cone: { - const float length = symbol->shapeProperty( QStringLiteral( "length" ) ).toFloat(); - const float bottomRadius = symbol->shapeProperty( QStringLiteral( "bottomRadius" ) ).toFloat(); - const float topRadius = symbol->shapeProperty( QStringLiteral( "topRadius" ) ).toFloat(); - - Qt3DExtras::QConeGeometry *g = new Qt3DExtras::QConeGeometry; - g->setLength( length ); - g->setBottomRadius( bottomRadius ); - g->setTopRadius( topRadius ); - //g->setHasBottomEndcap(hasBottomEndcap); - //g->setHasTopEndcap(hasTopEndcap); - return g; + Qt3DExtras::QConeGeometry *geometry = new Qt3DExtras::QConeGeometry; + geometry->setLength( pointData.length ); + geometry->setBottomRadius( pointData.bottomRadius ); + geometry->setTopRadius( pointData.topRadius ); + // geometry->setHasBottomEndcap(hasBottomEndcap); + // geometry->setHasTopEndcap(hasTopEndcap); + return geometry; } case Qgis::Point3DShape::Cube: { - const float size = symbol->shapeProperty( QStringLiteral( "size" ) ).toFloat(); - Qt3DExtras::QCuboidGeometry *g = new Qt3DExtras::QCuboidGeometry; - g->setXExtent( size ); - g->setYExtent( size ); - g->setZExtent( size ); - return g; + Qt3DExtras::QCuboidGeometry *geometry = new Qt3DExtras::QCuboidGeometry; + geometry->setXExtent( pointData.size ); + geometry->setYExtent( pointData.size ); + geometry->setZExtent( pointData.size ); + return geometry; } case Qgis::Point3DShape::Torus: { - const float radius = symbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat(); - const float minorRadius = symbol->shapeProperty( QStringLiteral( "minorRadius" ) ).toFloat(); - Qt3DExtras::QTorusGeometry *g = new Qt3DExtras::QTorusGeometry; - g->setRadius( radius ); - g->setMinorRadius( minorRadius ); - return g; + Qt3DExtras::QTorusGeometry *geometry = new Qt3DExtras::QTorusGeometry; + geometry->setRadius( pointData.radius ); + geometry->setMinorRadius( pointData.minorRadius ); + return geometry; } case Qgis::Point3DShape::Plane: { - const float size = symbol->shapeProperty( QStringLiteral( "size" ) ).toFloat(); - Qt3DExtras::QPlaneGeometry *g = new Qt3DExtras::QPlaneGeometry; - g->setWidth( size ); - g->setHeight( size ); - return g; + Qt3DExtras::QPlaneGeometry *geometry = new Qt3DExtras::QPlaneGeometry; + geometry->setWidth( pointData.size ); + geometry->setHeight( pointData.size ); + return geometry; } case Qgis::Point3DShape::ExtrudedText: diff --git a/src/app/3d/qgspoint3dsymbolwidget.cpp b/src/app/3d/qgspoint3dsymbolwidget.cpp index a600c49ba70a..f470731dd376 100644 --- a/src/app/3d/qgspoint3dsymbolwidget.cpp +++ b/src/app/3d/qgspoint3dsymbolwidget.cpp @@ -78,6 +78,13 @@ QgsPoint3DSymbolWidget::QgsPoint3DSymbolWidget( QWidget *parent ) // Sync between billboard height and TZ connect( spinBillboardHeight, static_cast( &QDoubleSpinBox::valueChanged ), spinTZ, &QDoubleSpinBox::setValue ); connect( spinTZ, static_cast( &QDoubleSpinBox::valueChanged ), spinBillboardHeight, &QDoubleSpinBox::setValue ); + + connect( mBtnLengthOverride, &QgsPropertyOverrideButton::changed, this, &QgsPoint3DSymbolWidget::changed ); + connect( mBtnRadiusOverride, &QgsPropertyOverrideButton::changed, this, &QgsPoint3DSymbolWidget::changed ); + connect( mBtnBottomRadiusOverride, &QgsPropertyOverrideButton::changed, this, &QgsPoint3DSymbolWidget::changed ); + connect( mBtnMinorRadiusOverride, &QgsPropertyOverrideButton::changed, this, &QgsPoint3DSymbolWidget::changed ); + connect( mBtnTopRadiusOverride, &QgsPropertyOverrideButton::changed, this, &QgsPoint3DSymbolWidget::changed ); + connect( mBtnSizeOverride, &QgsPropertyOverrideButton::changed, this, &QgsPoint3DSymbolWidget::changed ); } Qgs3DSymbolWidget *QgsPoint3DSymbolWidget::create( QgsVectorLayer * ) @@ -141,6 +148,16 @@ void QgsPoint3DSymbolWidget::setSymbol( const QgsAbstract3DSymbol *symbol, QgsVe break; } + // setSymbol is only called once. + // the data defined properties need to be all init to make them work if a user-interaction + // changes the shape. + mBtnLengthOverride->init( static_cast< int >( QgsAbstract3DSymbol::Property::Length ), pointSymbol->dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true ); + mBtnRadiusOverride->init( static_cast< int >( QgsAbstract3DSymbol::Property::Radius ), pointSymbol->dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true ); + mBtnBottomRadiusOverride->init( static_cast< int >( QgsAbstract3DSymbol::Property::BottomRadius ), pointSymbol->dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true ); + mBtnMinorRadiusOverride->init( static_cast< int >( QgsAbstract3DSymbol::Property::MinorRadius ), pointSymbol->dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true ); + mBtnTopRadiusOverride->init( static_cast< int >( QgsAbstract3DSymbol::Property::TopRadius ), pointSymbol->dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true ); + mBtnSizeOverride->init( static_cast< int >( QgsAbstract3DSymbol::Property::Size ), pointSymbol->dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true ); + widgetMaterial->setSettings( pointSymbol->materialSettings(), layer ); widgetMaterial->setTechnique( technique ); @@ -186,29 +203,40 @@ QgsAbstract3DSymbol *QgsPoint3DSymbolWidget::symbol() QVariantMap vm; std::unique_ptr< QgsPoint3DSymbol > sym = std::make_unique< QgsPoint3DSymbol >(); sym->setBillboardSymbol( static_cast( QgsSymbol::defaultSymbol( Qgis::GeometryType::Point ) ) ); + QgsPropertyCollection ddp; switch ( cboShape->currentData().value< Qgis::Point3DShape >() ) { case Qgis::Point3DShape::Sphere: vm[QStringLiteral( "radius" )] = spinRadius->value(); + ddp.setProperty( QgsAbstract3DSymbol::Property::Radius, mBtnRadiusOverride->toProperty() ); break; case Qgis::Point3DShape::Cylinder: vm[QStringLiteral( "radius" )] = spinRadius->value(); vm[QStringLiteral( "length" )] = spinLength->value(); + ddp.setProperty( QgsAbstract3DSymbol::Property::Radius, mBtnRadiusOverride->toProperty() ); + ddp.setProperty( QgsAbstract3DSymbol::Property::Length, mBtnLengthOverride->toProperty() ); break; case Qgis::Point3DShape::Cube: vm[QStringLiteral( "size" )] = spinSize->value(); + ddp.setProperty( QgsAbstract3DSymbol::Property::Size, mBtnSizeOverride->toProperty() ); break; case Qgis::Point3DShape::Cone: vm[QStringLiteral( "topRadius" )] = spinTopRadius->value(); vm[QStringLiteral( "bottomRadius" )] = spinBottomRadius->value(); vm[QStringLiteral( "length" )] = spinLength->value(); + ddp.setProperty( QgsAbstract3DSymbol::Property::Length, mBtnLengthOverride->toProperty() ); + ddp.setProperty( QgsAbstract3DSymbol::Property::TopRadius, mBtnTopRadiusOverride->toProperty() ); + ddp.setProperty( QgsAbstract3DSymbol::Property::BottomRadius, mBtnBottomRadiusOverride->toProperty() ); break; case Qgis::Point3DShape::Plane: vm[QStringLiteral( "size" )] = spinSize->value(); + ddp.setProperty( QgsAbstract3DSymbol::Property::Size, mBtnSizeOverride->toProperty() ); break; case Qgis::Point3DShape::Torus: vm[QStringLiteral( "radius" )] = spinRadius->value(); vm[QStringLiteral( "minorRadius" )] = spinMinorRadius->value(); + ddp.setProperty( QgsAbstract3DSymbol::Property::Radius, mBtnRadiusOverride->toProperty() ); + ddp.setProperty( QgsAbstract3DSymbol::Property::MinorRadius, mBtnMinorRadiusOverride->toProperty() ); break; case Qgis::Point3DShape::Model: vm[QStringLiteral( "model" )] = lineEditModel->source(); @@ -237,6 +265,7 @@ QgsAbstract3DSymbol *QgsPoint3DSymbolWidget::symbol() sym->setShapeProperties( vm ); sym->setMaterialSettings( widgetMaterial->settings() ); sym->setTransform( tr ); + sym->setDataDefinedProperties( ddp ); return sym.release(); } @@ -248,12 +277,12 @@ QString QgsPoint3DSymbolWidget::symbolType() const void QgsPoint3DSymbolWidget::onShapeChanged() { QList allWidgets; - allWidgets << labelSize << spinSize - << labelRadius << spinRadius - << labelMinorRadius << spinMinorRadius - << labelTopRadius << spinTopRadius - << labelBottomRadius << spinBottomRadius - << labelLength << spinLength + allWidgets << labelSize << spinSize << mBtnSizeOverride + << labelRadius << spinRadius << mBtnRadiusOverride + << labelMinorRadius << spinMinorRadius << mBtnMinorRadiusOverride + << labelTopRadius << spinTopRadius << mBtnTopRadiusOverride + << labelBottomRadius << spinBottomRadius << mBtnBottomRadiusOverride + << labelLength << spinLength << mBtnLengthOverride << labelModel << lineEditModel << labelBillboardHeight << spinBillboardHeight << labelBillboardSymbol << btnChangeSymbol; @@ -264,22 +293,22 @@ void QgsPoint3DSymbolWidget::onShapeChanged() switch ( cboShape->currentData().value< Qgis::Point3DShape >() ) { case Qgis::Point3DShape::Sphere: - activeWidgets << labelRadius << spinRadius; + activeWidgets << labelRadius << spinRadius << mBtnRadiusOverride; break; case Qgis::Point3DShape::Cylinder: - activeWidgets << labelRadius << spinRadius << labelLength << spinLength; + activeWidgets << labelRadius << spinRadius << mBtnRadiusOverride << labelLength << spinLength << mBtnLengthOverride; break; case Qgis::Point3DShape::Cube: - activeWidgets << labelSize << spinSize; + activeWidgets << labelSize << spinSize << mBtnSizeOverride; break; case Qgis::Point3DShape::Cone: - activeWidgets << labelTopRadius << spinTopRadius << labelBottomRadius << spinBottomRadius << labelLength << spinLength; + activeWidgets << labelTopRadius << spinTopRadius << mBtnTopRadiusOverride << labelBottomRadius << spinBottomRadius << mBtnBottomRadiusOverride << labelLength << spinLength << mBtnLengthOverride; break; case Qgis::Point3DShape::Plane: - activeWidgets << labelSize << spinSize; + activeWidgets << labelSize << spinSize << mBtnSizeOverride; break; case Qgis::Point3DShape::Torus: - activeWidgets << labelRadius << spinRadius << labelMinorRadius << spinMinorRadius; + activeWidgets << labelRadius << spinRadius << mBtnRadiusOverride << labelMinorRadius << spinMinorRadius << mBtnMinorRadiusOverride; break; case Qgis::Point3DShape::Model: activeWidgets << labelModel << lineEditModel; diff --git a/src/core/3d/qgsabstract3dsymbol.cpp b/src/core/3d/qgsabstract3dsymbol.cpp index d3d6878b709a..01f3151713bc 100644 --- a/src/core/3d/qgsabstract3dsymbol.cpp +++ b/src/core/3d/qgsabstract3dsymbol.cpp @@ -46,6 +46,12 @@ void QgsAbstract3DSymbol::initPropertyDefinitions() { { static_cast< int >( Property::Height ), QgsPropertyDefinition( "height", QObject::tr( "Height" ), QgsPropertyDefinition::Double, origin ) }, { static_cast< int >( Property::ExtrusionHeight ), QgsPropertyDefinition( "extrusionHeight", QObject::tr( "ExtrusionHeight" ), QgsPropertyDefinition::DoublePositive, origin ) }, + { static_cast< int >( Property::Length ), QgsPropertyDefinition( "length", QObject::tr( "Length" ), QgsPropertyDefinition::DoublePositive, origin ) }, + { static_cast< int >( Property::Radius ), QgsPropertyDefinition( "radius", QObject::tr( "Radius" ), QgsPropertyDefinition::DoublePositive, origin ) }, + { static_cast< int >( Property::BottomRadius ), QgsPropertyDefinition( "bottomRadius", QObject::tr( "BottomRadius" ), QgsPropertyDefinition::DoublePositive, origin ) }, + { static_cast< int >( Property::MinorRadius ), QgsPropertyDefinition( "minorRadius", QObject::tr( "MinorRadius" ), QgsPropertyDefinition::DoublePositive, origin ) }, + { static_cast< int >( Property::TopRadius ), QgsPropertyDefinition( "topRadius", QObject::tr( "TopRadius" ), QgsPropertyDefinition::DoublePositive, origin ) }, + { static_cast< int >( Property::Size ), QgsPropertyDefinition( "size", QObject::tr( "Size" ), QgsPropertyDefinition::DoublePositive, origin ) }, }; } diff --git a/src/core/3d/qgsabstract3dsymbol.h b/src/core/3d/qgsabstract3dsymbol.h index 16ef3fa6ea19..e6b7bf4135a2 100644 --- a/src/core/3d/qgsabstract3dsymbol.h +++ b/src/core/3d/qgsabstract3dsymbol.h @@ -70,6 +70,12 @@ class CORE_EXPORT QgsAbstract3DSymbol { Height SIP_MONKEYPATCH_COMPAT_NAME( PropertyHeight ) = 0, //!< Height (altitude) ExtrusionHeight SIP_MONKEYPATCH_COMPAT_NAME( PropertyExtrusionHeight ), //!< Extrusion height (zero means no extrusion) + Length SIP_MONKEYPATCH_COMPAT_NAME( PropertyLength ), //!< Length + Radius SIP_MONKEYPATCH_COMPAT_NAME( PropertyRadius ), //!< Radius + BottomRadius SIP_MONKEYPATCH_COMPAT_NAME( PropertyHeight ), //!< Bottom Radius + MinorRadius SIP_MONKEYPATCH_COMPAT_NAME( PropertySize ), //!< Minor Radius + TopRadius SIP_MONKEYPATCH_COMPAT_NAME( PropertyHeight ), //!< Top Radius + Size SIP_MONKEYPATCH_COMPAT_NAME( PropertySize ), //!< Size }; // *INDENT-ON* diff --git a/src/ui/3d/point3dsymbolwidget.ui b/src/ui/3d/point3dsymbolwidget.ui index ba89a59c11fd..b7f0c8c040f8 100644 --- a/src/ui/3d/point3dsymbolwidget.ui +++ b/src/ui/3d/point3dsymbolwidget.ui @@ -234,6 +234,13 @@ + + + + + + + @@ -288,6 +295,13 @@ + + + + + + + @@ -319,6 +333,13 @@ + + + + + + + @@ -349,6 +370,13 @@ + + + + + + + @@ -387,6 +415,13 @@ + + + + + + + @@ -413,6 +448,13 @@ + + + + + + + @@ -460,15 +502,26 @@
qgscollapsiblegroupbox.h
1 + + QgsPropertyOverrideButton + QToolButton +
qgspropertyoverridebutton.h
+
cboShape spinRadius + mBtnRadiusOverride spinMinorRadius + mBtnMinorRadiusOverride spinTopRadius + mBtnTopRadiusOverride spinBottomRadius + mBtnBottomRadiusOverride spinSize + mBtnSizeOverride spinLength + mBtnLengthOverride> spinBillboardHeight btnChangeSymbol cboAltClamping