QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsgeometrypaintdevice.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometrypaintdevice.cpp
3 --------------------------------------
4 Date : May 2024
5 Copyright : (C) 2024 by Nyall Dawson
6 Email : nyall dot dawson 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
17
18#include "qgsgeos.h"
19#include "qgslinestring.h"
20#include "qgsmultilinestring.h"
21#include "qgsmultipolygon.h"
22#include "qgspainting.h"
23#include "qgspolygon.h"
24#include "qgssymbollayerutils.h"
25
26//
27// QgsGeometryPaintEngine
28//
29
31 : QPaintEngine( QPaintEngine::AllFeatures ) // we lie and say we support all paint features, as we don't want Qt trying to be helpful and rasterizing shapes
32 , mUsePathStroker( usePathStroker )
33{
34}
35
37{
38 mStrokedPathsSegments = segments;
39}
40
42{
43 mSimplifyTolerance = tolerance;
44}
45
46bool QgsGeometryPaintEngine::begin( QPaintDevice * )
47{
48 return true;
49}
50
52{
53 return true;
54}
55
56QPaintEngine::Type QgsGeometryPaintEngine::type() const
57{
58 return QPaintEngine::User;
59}
60
61void QgsGeometryPaintEngine::updateState( const QPaintEngineState &state )
62{
63 if ( mUsePathStroker && state.state().testFlag( QPaintEngine::DirtyFlag::DirtyPen ) )
64 {
65 mPen = state.pen();
66 }
67}
68
69void QgsGeometryPaintEngine::drawImage( const QRectF &, const QImage &, const QRectF &, Qt::ImageConversionFlags )
70{
71 // ignore, we don't need to support raster drawing
72 QgsDebugError( QStringLiteral( "QgsGeometryPaintEngine does not support drawImage method" ) );
73}
74
75void QgsGeometryPaintEngine::drawPixmap( const QRectF &, const QPixmap &, const QRectF & )
76{
77 // ignore, we don't need to support raster drawing
78 QgsDebugError( QStringLiteral( "QgsGeometryPaintEngine does not support drawPixmap method" ) );
79}
80
81void QgsGeometryPaintEngine::drawTiledPixmap( const QRectF &, const QPixmap &, const QPointF & )
82{
83 // ignore, we don't need to support raster drawing
84 QgsDebugError( QStringLiteral( "QgsGeometryPaintEngine does not support drawTiledPixmap method" ) );
85}
86
87template <typename T>
88void drawLinesImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *lines, int lineCount )
89{
90 geometry.reserve( geometry.numGeometries() + lineCount );
91 if ( transform.isIdentity() )
92 {
93 for ( int i = 0; i < lineCount; ++i, ++lines )
94 {
95 geometry.addGeometry( new QgsLineString(
96 QVector<double> { static_cast< double >( lines->x1() ), static_cast< double >( lines->x2() ) },
97 QVector<double> { static_cast< double >( lines->y1() ), static_cast< double >( lines->y2() ) } )
98 );
99 }
100 }
101 else
102 {
103 for ( int i = 0; i < lineCount; ++i, ++lines )
104 {
105 double x1 = lines->x1();
106 double x2 = lines->x2();
107 double y1 = lines->y1();
108 double y2 = lines->y2();
109
110 double tx1, tx2, ty1, ty2;
111 transform.map( x1, y1, &tx1, &ty1 );
112 transform.map( x2, y2, &tx2, &ty2 );
113
114 geometry.addGeometry( new QgsLineString(
115 QVector<double> { tx1, tx2 },
116 QVector<double> { ty1, ty2 } )
117 );
118 }
119 }
120}
121
122void QgsGeometryPaintEngine::drawLines( const QLineF *lines, int lineCount )
123{
124 if ( mUsePathStroker )
125 {
126 // if stroking we have no choice but to go via the QPainterPath route
127 QPaintEngine::drawLines( lines, lineCount );
128 }
129 else
130 {
131 const QTransform transform = painter()->combinedTransform();
132 drawLinesImp( transform, mGeometry, lines, lineCount );
133 }
134}
135
136void QgsGeometryPaintEngine::drawLines( const QLine *lines, int lineCount )
137{
138 if ( mUsePathStroker )
139 {
140 // if stroking we have no choice but to go via the QPainterPath route
141 QPaintEngine::drawLines( lines, lineCount );
142 }
143 else
144 {
145 const QTransform transform = painter()->combinedTransform();
146 drawLinesImp( transform, mGeometry, lines, lineCount );
147 }
148}
149
150template <typename T>
151void drawPointsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount )
152{
153 geometry.reserve( geometry.numGeometries() + pointCount );
154 if ( transform.isIdentity() )
155 {
156 for ( int i = 0; i < pointCount; ++i, ++points )
157 {
158 geometry.addGeometry( new QgsPoint( static_cast< double >( points->x() ),
159 static_cast< double >( points->y() ) ) );
160 }
161 }
162 else
163 {
164 for ( int i = 0; i < pointCount; ++i, ++points )
165 {
166 double x = points->x();
167 double y = points->y();
168
169 double tx, ty;
170 transform.map( x, y, &tx, &ty );
171
172 geometry.addGeometry( new QgsPoint( tx, ty ) );
173 }
174 }
175}
176
177void QgsGeometryPaintEngine::drawPoints( const QPointF *points, int pointCount )
178{
179 const QTransform transform = painter()->combinedTransform();
180 drawPointsImp( transform, mGeometry, points, pointCount );
181}
182
183void QgsGeometryPaintEngine::drawPoints( const QPoint *points, int pointCount )
184{
185 const QTransform transform = painter()->combinedTransform();
186 drawPointsImp( transform, mGeometry, points, pointCount );
187}
188
189template <typename T>
190void drawRectsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *rects, int rectCount )
191{
192 geometry.reserve( geometry.numGeometries() + rectCount );
193 if ( transform.isIdentity() )
194 {
195 for ( int i = 0; i < rectCount; ++i, ++rects )
196 {
197 QgsLineString *exterior = new QgsLineString(
198 QVector<double> { static_cast< double >( rects->left() ),
199 static_cast< double >( rects->right() ),
200 static_cast< double >( rects->right() ),
201 static_cast< double >( rects->left() ),
202 static_cast< double>( rects->left() )
203 },
204 QVector<double> { static_cast< double >( rects->bottom() ),
205 static_cast< double >( rects->bottom() ),
206 static_cast< double >( rects->top() ),
207 static_cast< double >( rects->top() ),
208 static_cast< double >( rects->bottom() )
209 } );
210 geometry.addGeometry( new QgsPolygon( exterior ) );
211 }
212 }
213 else
214 {
215 for ( int i = 0; i < rectCount; ++i, ++rects )
216 {
217 const double left = rects->left();
218 const double right = rects->right();
219 const double top = rects->top();
220 const double bottom = rects->bottom();
221
222 double bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, topLeftX, topLeftY, topRightX, topRightY;
223 transform.map( left, bottom, &bottomLeftX, &bottomLeftY );
224 transform.map( right, bottom, &bottomRightX, &bottomRightY );
225 transform.map( left, top, &topLeftX, &topLeftY );
226 transform.map( right, top, &topRightX, &topRightY );
227
228 QgsLineString *exterior = new QgsLineString(
229 QVector<double> { bottomLeftX, bottomRightX, topRightX, topLeftX, bottomLeftX },
230 QVector<double> { bottomLeftY, bottomRightY, topRightY, topLeftY, bottomLeftY } );
231 geometry.addGeometry( new QgsPolygon( exterior ) );
232 }
233 }
234}
235
236void QgsGeometryPaintEngine::drawRects( const QRectF *rects, int rectCount )
237{
238 const QTransform transform = painter()->combinedTransform();
239 drawRectsImp( transform, mGeometry, rects, rectCount );
240}
241
242void QgsGeometryPaintEngine::drawRects( const QRect *rects, int rectCount )
243{
244 const QTransform transform = painter()->combinedTransform();
245 drawRectsImp( transform, mGeometry, rects, rectCount );
246}
247
248template <typename T>
249void drawPolygonImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount, QPaintEngine::PolygonDrawMode mode, double simplifyTolerance )
250{
251 QVector< double > x;
252 QVector< double > y;
253 x.resize( pointCount );
254 y.resize( pointCount );
255 double *xData = x.data();
256 double *yData = y.data();
257
258 if ( transform.isIdentity() )
259 {
260 for ( int i = 0; i < pointCount; ++i, ++points )
261 {
262 *xData++ = points->x();
263 *yData++ = points->y();
264 }
265 }
266 else
267 {
268 for ( int i = 0; i < pointCount; ++i, ++points )
269 {
270 const double x = points->x();
271 const double y = points->y();
272 double tx, ty;
273 transform.map( x, y, &tx, &ty );
274
275 *xData++ = tx;
276 *yData++ = ty;
277 }
278 }
279
280 switch ( mode )
281 {
282 case QPaintEngine::PolylineMode:
283 if ( simplifyTolerance > 0 )
284 geometry.addGeometry( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) );
285 else
286 geometry.addGeometry( new QgsLineString( x, y ) );
287 break;
288
289 case QPaintEngine::OddEvenMode:
290 case QPaintEngine::WindingMode:
291 case QPaintEngine::ConvexMode:
292 if ( simplifyTolerance > 0 )
293 geometry.addGeometry( new QgsPolygon( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) ) );
294 else
295 geometry.addGeometry( new QgsPolygon( new QgsLineString( x, y ) ) );
296 break;
297 }
298}
299
300void QgsGeometryPaintEngine::drawPolygon( const QPoint *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
301{
302 if ( mUsePathStroker && mode == PolygonDrawMode::PolylineMode )
303 {
304 // if stroking we have no choice but to go via the QPainterPath route
305 if ( pointCount > 0 )
306 {
307 QPainterPath path;
308 path.moveTo( *points++ );
309 for ( int i = 1; i < pointCount; ++i )
310 {
311 path.lineTo( *points++ );
312 }
313 drawPath( path );
314 }
315 }
316 else
317 {
318 const QTransform transform = painter()->combinedTransform();
319 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
320 }
321}
322
323void QgsGeometryPaintEngine::drawPolygon( const QPointF *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
324{
325 if ( mUsePathStroker )
326 {
327 // if stroking we have no choice but to go via the QPainterPath route
328 if ( pointCount > 0 )
329 {
330 QPainterPath path;
331 path.moveTo( *points++ );
332 for ( int i = 1; i < pointCount; ++i )
333 {
334 path.lineTo( *points++ );
335 }
336 drawPath( path );
337 }
338 }
339 else
340 {
341 const QTransform transform = painter()->combinedTransform();
342 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
343 }
344}
345
346void QgsGeometryPaintEngine::addStrokedLine( const QgsLineString *line, double penWidth, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit, const QTransform *matrix )
347{
348 std::unique_ptr< QgsAbstractGeometry > buffered;
349 if ( mSimplifyTolerance > 0 )
350 {
351 // For performance, we apply a lower level of simplification to the line BEFORE doing the buffer.
352 // This avoids making the call to GEOS buffer function too expensive, as we'd otherwise be doing it
353 // on the unsimplified line and then immediately discarding most of the detail when we simplify
354 // the resultant buffer.
355 // The 0.75 factor here is just a guess! This could likely be made smarter, eg by considering the pen width?
356 const double preBufferedSimplificationFactor = mSimplifyTolerance * 0.75;
357 std::unique_ptr< QgsLineString > simplified( line->simplifyByDistance( preBufferedSimplificationFactor ) );
358 QgsGeos geos( simplified.get() );
359 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
360 }
361 else
362 {
363 QgsGeos geos( line );
364 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
365 }
366
367 if ( !buffered )
368 return;
369
370 if ( matrix )
371 buffered->transform( *matrix );
372
373 if ( QgsGeometryCollection *bufferedCollection = qgsgeometry_cast< QgsGeometryCollection * >( buffered.get() ) )
374 {
375 if ( mSimplifyTolerance > 0 )
376 {
377 for ( auto it = bufferedCollection->const_parts_begin(); it != bufferedCollection->const_parts_end(); ++it )
378 {
379 mGeometry.addGeometry( ( *it )->simplifyByDistance( mSimplifyTolerance ) );
380 }
381 }
382 else
383 {
384 mGeometry.addGeometries( bufferedCollection->takeGeometries() );
385 }
386 }
387 else if ( buffered )
388 {
389 if ( mSimplifyTolerance > 0 )
390 {
391 mGeometry.addGeometry( buffered->simplifyByDistance( mSimplifyTolerance ) );
392 }
393 else
394 {
395 mGeometry.addGeometry( buffered.release() );
396 }
397 }
398}
399
400// based on QPainterPath::toSubpathPolygons()
401void QgsGeometryPaintEngine::addSubpathGeometries( const QPainterPath &path, const QTransform &matrix )
402{
403 if ( path.isEmpty() )
404 return;
405
406 const bool transformIsIdentity = matrix.isIdentity();
407
408 const Qgis::EndCapStyle endCapStyle = QgsSymbolLayerUtils::penCapStyleToEndCapStyle( mPen.capStyle() );
409 const Qgis::JoinStyle joinStyle = QgsSymbolLayerUtils::penJoinStyleToJoinStyle( mPen.joinStyle() );
410 const double penWidth = mPen.widthF() <= 0 ? 1 : mPen.widthF();
411 const double miterLimit = mPen.miterLimit();
412
413 QVector< double > currentX;
414 QVector< double > currentY;
415 const int count = path.elementCount();
416
417 // polygon parts get queued and post-processed before adding them to the collection
418 std::vector< std::unique_ptr< QgsPolygon > > queuedPolygons;
419
420 for ( int i = 0; i < count; ++i )
421 {
422 const QPainterPath::Element &e = path.elementAt( i );
423 switch ( e.type )
424 {
425 case QPainterPath::MoveToElement:
426 {
427 if ( currentX.size() > 1 )
428 {
429 auto line = std::make_unique< QgsLineString >( currentX, currentY );
430 if ( mUsePathStroker )
431 {
432 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
433 }
434 else if ( line->isClosed() )
435 {
436 if ( !transformIsIdentity )
437 line->transform( matrix );
438
439 if ( mSimplifyTolerance > 0 )
440 {
441 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
442 line.reset();
443 }
444 else
445 {
446 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
447 }
448 }
449 else
450 {
451 if ( !transformIsIdentity )
452 line->transform( matrix );
453 if ( mSimplifyTolerance > 0 )
454 {
455 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
456 line.reset();
457 }
458 else
459 {
460 mGeometry.addGeometry( line.release() );
461 }
462 }
463 }
464 currentX.resize( 0 );
465 currentY.resize( 0 );
466
467 currentX.reserve( 16 );
468 currentY.reserve( 16 );
469 currentX << e.x;
470 currentY << e.y;
471 break;
472 }
473
474 case QPainterPath::LineToElement:
475 {
476 currentX << e.x;
477 currentY << e.y;
478 break;
479 }
480
481 case QPainterPath::CurveToElement:
482 {
483 Q_ASSERT( path.elementAt( i + 1 ).type == QPainterPath::CurveToDataElement );
484 Q_ASSERT( path.elementAt( i + 2 ).type == QPainterPath::CurveToDataElement );
485
486 const double x1 = path.elementAt( i - 1 ).x;
487 const double y1 = path.elementAt( i - 1 ).y;
488
489 const double x3 = path.elementAt( i + 1 ).x;
490 const double y3 = path.elementAt( i + 1 ).y;
491
492 const double x4 = path.elementAt( i + 2 ).x;
493 const double y4 = path.elementAt( i + 2 ).y;
494
495 // TODO -- we could likely reduce the number of segmented points here!
496 std::unique_ptr< QgsLineString> bezier( QgsLineString::fromBezierCurve(
497 QgsPoint( x1, y1 ),
498 QgsPoint( e.x, e.y ),
499 QgsPoint( x3, y3 ),
500 QgsPoint( x4, y4 ) ) );
501
502 currentX << bezier->xVector();
503 currentY << bezier->yVector();
504
505 i += 2;
506 break;
507 }
508 case QPainterPath::CurveToDataElement:
509 Q_ASSERT( !"addSubpathGeometries(), bad element type" );
510 break;
511 }
512 }
513
514 if ( currentX.size() > 1 )
515 {
516 auto line = std::make_unique< QgsLineString >( currentX, currentY );
517 if ( mUsePathStroker )
518 {
519 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
520 }
521 else if ( line->isClosed() )
522 {
523 if ( !transformIsIdentity )
524 line->transform( matrix );
525 if ( mSimplifyTolerance > 0 )
526 {
527 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
528 line.reset();
529 }
530 else
531 {
532 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
533 }
534 }
535 else
536 {
537 if ( !transformIsIdentity )
538 line->transform( matrix );
539 if ( mSimplifyTolerance > 0 )
540 {
541 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
542 line.reset();
543 }
544 else
545 {
546 mGeometry.addGeometry( line.release() );
547 }
548 }
549 }
550
551 if ( queuedPolygons.empty() )
552 return;
553
554 mGeometry.reserve( static_cast< int >( mGeometry.numGeometries() + queuedPolygons.size() ) );
555
556 QgsMultiPolygon tempMultiPolygon;
557 tempMultiPolygon.reserve( static_cast< int >( queuedPolygons.size() ) );
558 for ( auto &part : queuedPolygons )
559 {
560 tempMultiPolygon.addGeometry( part.release() );
561 }
562
563 // ensure holes are holes, not overlapping polygons
564 QgsGeos geosCollection( &tempMultiPolygon );
565 std::unique_ptr< QgsAbstractGeometry > g = geosCollection.makeValid( Qgis::MakeValidMethod::Linework );
566 if ( !g )
567 return;
568
569 for ( auto it = g->const_parts_begin(); it != g->const_parts_end(); ++it )
570 {
571 mGeometry.addGeometry( ( *it )->clone() );
572 }
573}
574
575void QgsGeometryPaintEngine::drawPath( const QPainterPath &path )
576{
577 const QTransform transform = painter()->combinedTransform();
578 addSubpathGeometries( path, transform );
579}
580
581
582//
583// QgsGeometryPaintDevice
584//
585
587{
588 mPaintEngine = std::make_unique<QgsGeometryPaintEngine>( usePathStroker );
589}
590
592{
593 if ( mPaintEngine )
594 mPaintEngine->setStrokedPathSegments( segments );
595}
596
598{
599 if ( mPaintEngine )
600 mPaintEngine->setSimplificationTolerance( tolerance );
601}
602
604{
605 return mPaintEngine.get();
606}
607
608int QgsGeometryPaintDevice::metric( PaintDeviceMetric m ) const
609{
610 // copy/paste from qpicture.cpp
611 int val;
612
613 switch ( m )
614 {
615 case PdmWidth:
616 val = static_cast< int >( mPaintEngine->geometry().boundingBox().width() );
617 break;
618 case PdmHeight:
619 val = static_cast< int >( mPaintEngine->geometry().boundingBox().height() );
620 break;
621 case PdmWidthMM:
622 val = static_cast< int >( 25.4 / QgsPainting::qtDefaultDpiX() * mPaintEngine->geometry().boundingBox().width() );
623 break;
624 case PdmHeightMM:
625 val = static_cast< int >( 25.4 / QgsPainting::qtDefaultDpiY() * mPaintEngine->geometry().boundingBox().height() );
626 break;
627 case PdmDpiX:
628 case PdmPhysicalDpiX:
630 break;
631 case PdmDpiY:
632 case PdmPhysicalDpiY:
634 break;
635 case PdmNumColors:
636 val = 16777216;
637 break;
638 case PdmDepth:
639 val = 24;
640 break;
641 case PdmDevicePixelRatio:
642 val = 1;
643 break;
644 case PdmDevicePixelRatioScaled:
645 val = static_cast< int >( 1 * QPaintDevice::devicePixelRatioFScale() );
646 break;
647 default:
648 val = 0;
649 qWarning( "QPicture::metric: Invalid metric command" );
650 }
651 return val;
652}
653
655{
656 return mPaintEngine->geometry();
657}
658
660{
662 QPainter painter( &device );
663 painter.drawPath( path );
664 painter.end();
665 return QgsGeometry( device.geometry().clone() );
666}
667
JoinStyle
Join styles for buffers.
Definition qgis.h:2121
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2108
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2181
Abstract base class for all geometries.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
void reserve(int size)
Attempts to allocate memory for at least size geometries.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int numGeometries() const
Returns the number of geometries within the collection.
QgsGeometryPaintDevice(bool usePathStroker=false)
Constructor for QgsGeometryPaintDevice.
void setStrokedPathSegments(int segments)
Sets the number of segments to use when drawing stroked paths with a rounded pen.
int metric(PaintDeviceMetric metric) const override
void setSimplificationTolerance(double tolerance)
Sets a simplification tolerance (in painter units) to use for on-the-fly simplification of geometries...
QPaintEngine * paintEngine() const override
static QgsGeometry painterPathToGeometry(const QPainterPath &path)
Converts a painter path to a QgsGeometry.
const QgsAbstractGeometry & geometry() const
Returns the rendered geometry.
void setSimplificationTolerance(double tolerance)
Sets a simplification tolerance (in painter units) to use for on-the-fly simplification of geometries...
void drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags=Qt::AutoColor) final
void drawRects(const QRectF *rects, int rectCount) final
void drawPolygon(const QPointF *points, int pointCount, QPaintEngine::PolygonDrawMode mode) final
void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) final
void drawPoints(const QPointF *points, int pointCount) final
bool begin(QPaintDevice *) final
QgsGeometryPaintEngine(bool usePathStroker=false)
Constructor for QgsGeometryPaintEngine.
void drawLines(const QLineF *lines, int lineCount) final
QPaintEngine::Type type() const final
void drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p) final
void updateState(const QPaintEngineState &) final
void drawPath(const QPainterPath &path) final
void setStrokedPathSegments(int segments)
Sets the number of segments to use when drawing stroked paths with a rounded pen.
A geometry is the spatial representation of a feature.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:141
Line string geometry type, with support for z-dimension and m-values.
static std::unique_ptr< QgsLineString > fromBezierCurve(const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments=30)
Returns a new linestring created by segmentizing the bezier curve between start and end,...
bool isClosed() const override
Returns true if the curve is closed.
QgsLineString * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
static int qtDefaultDpiY()
Returns the default Qt vertical DPI.
static int qtDefaultDpiX()
Returns the default Qt horizontal DPI.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Polygon geometry type.
Definition qgspolygon.h:33
static Qgis::EndCapStyle penCapStyleToEndCapStyle(Qt::PenCapStyle style)
Converts a Qt pen cap style to a QGIS end cap style.
static Qgis::JoinStyle penJoinStyleToJoinStyle(Qt::PenJoinStyle style)
Converts a Qt pen joinstyle to a QGIS join style.
Contains geos related utilities and functions.
Definition qgsgeos.h:77
T qgsgeometry_cast(QgsAbstractGeometry *geom)
void drawPointsImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount)
void drawRectsImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *rects, int rectCount)
void drawLinesImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *lines, int lineCount)
void drawPolygonImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount, QPaintEngine::PolygonDrawMode mode, double simplifyTolerance)
#define QgsDebugError(str)
Definition qgslogger.h:57