QgsGeometry QgsInternalGeometryEngine::variableWidthBuffer( int segments, const std::function< std::unique_ptr< double[] >( const QgsLineString *line ) > &widthFunction ) const { if ( !mGeometry ) { return QgsGeometry(); } std::vector< std::unique_ptr<QgsLineString > > linesToProcess; const QgsMultiCurve *multiCurve = qgsgeometry_cast< const QgsMultiCurve * >( mGeometry ); if ( multiCurve ) { for ( int i = 0; i < multiCurve->partCount(); ++i ) { if ( static_cast< const QgsCurve * >( multiCurve->geometryN( i ) )->nCoordinates() == 0 ) continue; // skip 0 length lines linesToProcess.emplace_back( static_cast<QgsLineString *>( multiCurve->geometryN( i )->clone() ) ); } } const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( mGeometry ); if ( curve ) { if ( curve->nCoordinates() > 0 ) linesToProcess.emplace_back( static_cast<QgsLineString *>( curve->segmentize() ) ); } if ( linesToProcess.empty() ) { QgsGeometry g; g.mLastError = QStringLiteral( "Input geometry was not a curve type geometry" ); return g; } QVector<QgsGeometry> bufferedLines; for ( std::unique_ptr< QgsLineString > &line : linesToProcess ) { QVector<QgsGeometry> parts; QgsPoint prevPoint; double prevRadius = 0; QgsGeometry prevCircle; std::unique_ptr< double[] > widths = widthFunction( line.get() ) ; for ( int i = 0; i < line->nCoordinates(); ++i ) { QgsPoint thisPoint = line->pointN( i ); QgsGeometry thisCircle; double thisRadius = widths[ i ] / 2.0; if ( thisRadius > 0 ) { QgsGeometry p = QgsGeometry( thisPoint.clone() ); QgsCircle circ( thisPoint, thisRadius ); thisCircle = QgsGeometry( circ.toPolygon( segments * 4 ) ); parts << thisCircle; } else { thisCircle = QgsGeometry( thisPoint.clone() ); } if ( i > 0 ) { if ( prevRadius > 0 || thisRadius > 0 ) { QVector< QgsPointXY > points = generateSegmentCurve( prevPoint, prevRadius, thisPoint, thisRadius ); if ( !points.empty() ) { // snap points to circle vertices int atVertex = 0; int beforeVertex = 0; int afterVertex = 0; double sqrDist = 0; double sqrDistPrev = 0; for ( int j = 0; j < points.count(); ++j ) { QgsPointXY pA = prevCircle.closestVertex( points.at( j ), atVertex, beforeVertex, afterVertex, sqrDistPrev ); QgsPointXY pB = thisCircle.closestVertex( points.at( j ), atVertex, beforeVertex, afterVertex, sqrDist ); points[j] = sqrDistPrev < sqrDist ? pA : pB; } // close ring points.append( points.at( 0 ) ); std::unique_ptr< QgsPolygon > poly = qgis::make_unique< QgsPolygon >(); poly->setExteriorRing( new QgsLineString( points ) ); if ( poly->area() > 0 ) parts << QgsGeometry( std::move( poly ) ); } } } prevPoint = thisPoint; prevRadius = thisRadius; prevCircle = thisCircle; } bufferedLines << QgsGeometry::unaryUnion( parts ); } return QgsGeometry::collectGeometry( bufferedLines ); }