QGIS API Documentation 3.39.0-Master (47f7b3a4989)
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#include "qgspolygon.h"
18#include "qgslinestring.h"
19#include "qgsgeos.h"
20#include "qgsmultipolygon.h"
21#include "qgsmultilinestring.h"
22
23Q_GUI_EXPORT extern int qt_defaultDpiX();
24Q_GUI_EXPORT extern int qt_defaultDpiY();
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}
73
74void QgsGeometryPaintEngine::drawPixmap( const QRectF &, const QPixmap &, const QRectF & )
75{
76 // ignore, we don't need to support raster drawing
77}
78
79void QgsGeometryPaintEngine::drawTiledPixmap( const QRectF &, const QPixmap &, const QPointF & )
80{
81 // ignore, we don't need to support raster drawing
82}
83
84template <typename T>
85void drawLinesImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *lines, int lineCount )
86{
87 geometry.reserve( geometry.numGeometries() + lineCount );
88 if ( transform.isIdentity() )
89 {
90 for ( int i = 0; i < lineCount; ++i, ++lines )
91 {
92 geometry.addGeometry( new QgsLineString(
93 QVector<double> { static_cast< double >( lines->x1() ), static_cast< double >( lines->x2() ) },
94 QVector<double> { static_cast< double >( lines->y1() ), static_cast< double >( lines->y2() ) } )
95 );
96 }
97 }
98 else
99 {
100 for ( int i = 0; i < lineCount; ++i, ++lines )
101 {
102 double x1 = lines->x1();
103 double x2 = lines->x2();
104 double y1 = lines->y1();
105 double y2 = lines->y2();
106
107 double tx1, tx2, ty1, ty2;
108 transform.map( x1, y1, &tx1, &ty1 );
109 transform.map( x2, y2, &tx2, &ty2 );
110
111 geometry.addGeometry( new QgsLineString(
112 QVector<double> { tx1, tx2 },
113 QVector<double> { ty1, ty2 } )
114 );
115 }
116 }
117}
118
119void QgsGeometryPaintEngine::drawLines( const QLineF *lines, int lineCount )
120{
121 if ( mUsePathStroker )
122 {
123 // if stroking we have no choice but to go via the QPainterPath route
124 QPaintEngine::drawLines( lines, lineCount );
125 }
126 else
127 {
128 const QTransform transform = painter()->combinedTransform();
129 drawLinesImp( transform, mGeometry, lines, lineCount );
130 }
131}
132
133void QgsGeometryPaintEngine::drawLines( const QLine *lines, int lineCount )
134{
135 if ( mUsePathStroker )
136 {
137 // if stroking we have no choice but to go via the QPainterPath route
138 QPaintEngine::drawLines( lines, lineCount );
139 }
140 else
141 {
142 const QTransform transform = painter()->combinedTransform();
143 drawLinesImp( transform, mGeometry, lines, lineCount );
144 }
145}
146
147template <typename T>
148void drawPointsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount )
149{
150 geometry.reserve( geometry.numGeometries() + pointCount );
151 if ( transform.isIdentity() )
152 {
153 for ( int i = 0; i < pointCount; ++i, ++points )
154 {
155 geometry.addGeometry( new QgsPoint( static_cast< double >( points->x() ),
156 static_cast< double >( points->y() ) ) );
157 }
158 }
159 else
160 {
161 for ( int i = 0; i < pointCount; ++i, ++points )
162 {
163 double x = points->x();
164 double y = points->y();
165
166 double tx, ty;
167 transform.map( x, y, &tx, &ty );
168
169 geometry.addGeometry( new QgsPoint( tx, ty ) );
170 }
171 }
172}
173
174void QgsGeometryPaintEngine::drawPoints( const QPointF *points, int pointCount )
175{
176 const QTransform transform = painter()->combinedTransform();
177 drawPointsImp( transform, mGeometry, points, pointCount );
178}
179
180void QgsGeometryPaintEngine::drawPoints( const QPoint *points, int pointCount )
181{
182 const QTransform transform = painter()->combinedTransform();
183 drawPointsImp( transform, mGeometry, points, pointCount );
184}
185
186template <typename T>
187void drawRectsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *rects, int rectCount )
188{
189 geometry.reserve( geometry.numGeometries() + rectCount );
190 if ( transform.isIdentity() )
191 {
192 for ( int i = 0; i < rectCount; ++i, ++rects )
193 {
194 QgsLineString *exterior = new QgsLineString(
195 QVector<double> { static_cast< double >( rects->left() ),
196 static_cast< double >( rects->right() ),
197 static_cast< double >( rects->right() ),
198 static_cast< double >( rects->left() ),
199 static_cast< double>( rects->left() )
200 },
201 QVector<double> { static_cast< double >( rects->bottom() ),
202 static_cast< double >( rects->bottom() ),
203 static_cast< double >( rects->top() ),
204 static_cast< double >( rects->top() ),
205 static_cast< double >( rects->bottom() )
206 } );
207 geometry.addGeometry( new QgsPolygon( exterior ) );
208 }
209 }
210 else
211 {
212 for ( int i = 0; i < rectCount; ++i, ++rects )
213 {
214 const double left = rects->left();
215 const double right = rects->right();
216 const double top = rects->top();
217 const double bottom = rects->bottom();
218
219 double bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, topLeftX, topLeftY, topRightX, topRightY;
220 transform.map( left, bottom, &bottomLeftX, &bottomLeftY );
221 transform.map( right, bottom, &bottomRightX, &bottomRightY );
222 transform.map( left, top, &topLeftX, &topLeftY );
223 transform.map( right, top, &topRightX, &topRightY );
224
225 QgsLineString *exterior = new QgsLineString(
226 QVector<double> { bottomLeftX, bottomRightX, topRightX, topLeftX, bottomLeftX },
227 QVector<double> { bottomLeftY, bottomRightY, topRightY, topLeftY, bottomLeftY } );
228 geometry.addGeometry( new QgsPolygon( exterior ) );
229 }
230 }
231}
232
233void QgsGeometryPaintEngine::drawRects( const QRectF *rects, int rectCount )
234{
235 const QTransform transform = painter()->combinedTransform();
236 drawRectsImp( transform, mGeometry, rects, rectCount );
237}
238
239void QgsGeometryPaintEngine::drawRects( const QRect *rects, int rectCount )
240{
241 const QTransform transform = painter()->combinedTransform();
242 drawRectsImp( transform, mGeometry, rects, rectCount );
243}
244
245template <typename T>
246void drawPolygonImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount, QPaintEngine::PolygonDrawMode mode, double simplifyTolerance )
247{
248 QVector< double > x;
249 QVector< double > y;
250 x.resize( pointCount );
251 y.resize( pointCount );
252 double *xData = x.data();
253 double *yData = y.data();
254
255 if ( transform.isIdentity() )
256 {
257 for ( int i = 0; i < pointCount; ++i, ++points )
258 {
259 *xData++ = points->x();
260 *yData++ = points->y();
261 }
262 }
263 else
264 {
265 for ( int i = 0; i < pointCount; ++i, ++points )
266 {
267 const double x = points->x();
268 const double y = points->y();
269 double tx, ty;
270 transform.map( x, y, &tx, &ty );
271
272 *xData++ = tx;
273 *yData++ = ty;
274 }
275 }
276
277 switch ( mode )
278 {
279 case QPaintEngine::PolylineMode:
280 if ( simplifyTolerance > 0 )
281 geometry.addGeometry( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) );
282 else
283 geometry.addGeometry( new QgsLineString( x, y ) );
284 break;
285
286 case QPaintEngine::OddEvenMode:
287 case QPaintEngine::WindingMode:
288 case QPaintEngine::ConvexMode:
289 if ( simplifyTolerance > 0 )
290 geometry.addGeometry( new QgsPolygon( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) ) );
291 else
292 geometry.addGeometry( new QgsPolygon( new QgsLineString( x, y ) ) );
293 break;
294 }
295}
296
297void QgsGeometryPaintEngine::drawPolygon( const QPoint *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
298{
299 if ( mUsePathStroker && mode == PolygonDrawMode::PolylineMode )
300 {
301 // if stroking we have no choice but to go via the QPainterPath route
302 if ( pointCount > 0 )
303 {
304 QPainterPath path;
305 path.moveTo( *points++ );
306 for ( int i = 1; i < pointCount; ++i )
307 {
308 path.lineTo( *points++ );
309 }
310 drawPath( path );
311 }
312 }
313 else
314 {
315 const QTransform transform = painter()->combinedTransform();
316 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
317 }
318}
319
320void QgsGeometryPaintEngine::drawPolygon( const QPointF *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
321{
322 if ( mUsePathStroker )
323 {
324 // if stroking we have no choice but to go via the QPainterPath route
325 if ( pointCount > 0 )
326 {
327 QPainterPath path;
328 path.moveTo( *points++ );
329 for ( int i = 1; i < pointCount; ++i )
330 {
331 path.lineTo( *points++ );
332 }
333 drawPath( path );
334 }
335 }
336 else
337 {
338 const QTransform transform = painter()->combinedTransform();
339 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
340 }
341}
342
343void QgsGeometryPaintEngine::addStrokedLine( const QgsLineString *line, double penWidth, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit, const QTransform *matrix )
344{
345 std::unique_ptr< QgsAbstractGeometry > buffered;
346 if ( mSimplifyTolerance > 0 )
347 {
348 // For performance, we apply a lower level of simplification to the line BEFORE doing the buffer.
349 // This avoids making the call to GEOS buffer function too expensive, as we'd otherwise be doing it
350 // on the unsimplified line and then immediately discarding most of the detail when we simplify
351 // the resultant buffer.
352 // The 0.75 factor here is just a guess! This could likely be made smarter, eg by considering the pen width?
353 const double preBufferedSimplificationFactor = mSimplifyTolerance * 0.75;
354 std::unique_ptr< QgsLineString > simplified( line->simplifyByDistance( preBufferedSimplificationFactor ) );
355 QgsGeos geos( simplified.get() );
356 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
357 }
358 else
359 {
360 QgsGeos geos( line );
361 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
362 }
363
364 if ( !buffered )
365 return;
366
367 if ( matrix )
368 buffered->transform( *matrix );
369
370 if ( QgsGeometryCollection *bufferedCollection = qgsgeometry_cast< QgsGeometryCollection * >( buffered.get() ) )
371 {
372 if ( mSimplifyTolerance > 0 )
373 {
374 for ( auto it = bufferedCollection->const_parts_begin(); it != bufferedCollection->const_parts_end(); ++it )
375 {
376 mGeometry.addGeometry( ( *it )->simplifyByDistance( mSimplifyTolerance ) );
377 }
378 }
379 else
380 {
381 mGeometry.addGeometries( bufferedCollection->takeGeometries() );
382 }
383 }
384 else if ( buffered )
385 {
386 if ( mSimplifyTolerance > 0 )
387 {
388 mGeometry.addGeometry( buffered->simplifyByDistance( mSimplifyTolerance ) );
389 }
390 else
391 {
392 mGeometry.addGeometry( buffered.release() );
393 }
394 }
395}
396
397Qgis::EndCapStyle QgsGeometryPaintEngine::penStyleToCapStyle( Qt::PenCapStyle style )
398{
399 switch ( style )
400 {
401 case Qt::FlatCap:
403 case Qt::SquareCap:
405 case Qt::RoundCap:
407 case Qt::MPenCapStyle:
408 // undocumented?
409 break;
410 }
411
413}
414
415Qgis::JoinStyle QgsGeometryPaintEngine::penStyleToJoinStyle( Qt::PenJoinStyle style )
416{
417 switch ( style )
418 {
419 case Qt::MiterJoin:
420 case Qt::SvgMiterJoin:
422 case Qt::BevelJoin:
424 case Qt::RoundJoin:
426 case Qt::MPenJoinStyle:
427 // undocumented?
428 break;
429 }
431}
432
433// based on QPainterPath::toSubpathPolygons()
434void QgsGeometryPaintEngine::addSubpathGeometries( const QPainterPath &path, const QTransform &matrix )
435{
436 if ( path.isEmpty() )
437 return;
438
439 const bool transformIsIdentity = matrix.isIdentity();
440
441 const Qgis::EndCapStyle endCapStyle = penStyleToCapStyle( mPen.capStyle() );
442 const Qgis::JoinStyle joinStyle = penStyleToJoinStyle( mPen.joinStyle() );
443 const double penWidth = mPen.widthF() <= 0 ? 1 : mPen.widthF();
444 const double miterLimit = mPen.miterLimit();
445
446 QVector< double > currentX;
447 QVector< double > currentY;
448 const int count = path.elementCount();
449
450 // polygon parts get queued and post-processed before adding them to the collection
451 std::vector< std::unique_ptr< QgsPolygon > > queuedPolygons;
452
453 for ( int i = 0; i < count; ++i )
454 {
455 const QPainterPath::Element &e = path.elementAt( i );
456 switch ( e.type )
457 {
458 case QPainterPath::MoveToElement:
459 {
460 if ( currentX.size() > 1 )
461 {
462 std::unique_ptr< QgsLineString > line = std::make_unique< QgsLineString >( currentX, currentY );
463 if ( mUsePathStroker )
464 {
465 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
466 }
467 else if ( line->isClosed() )
468 {
469 if ( !transformIsIdentity )
470 line->transform( matrix );
471
472 if ( mSimplifyTolerance > 0 )
473 {
474 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
475 line.reset();
476 }
477 else
478 {
479 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
480 }
481 }
482 else
483 {
484 if ( !transformIsIdentity )
485 line->transform( matrix );
486 if ( mSimplifyTolerance > 0 )
487 {
488 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
489 line.reset();
490 }
491 else
492 {
493 mGeometry.addGeometry( line.release() );
494 }
495 }
496 }
497 currentX.resize( 0 );
498 currentY.resize( 0 );
499
500 currentX.reserve( 16 );
501 currentY.reserve( 16 );
502 currentX << e.x;
503 currentY << e.y;
504 break;
505 }
506
507 case QPainterPath::LineToElement:
508 {
509 currentX << e.x;
510 currentY << e.y;
511 break;
512 }
513
514 case QPainterPath::CurveToElement:
515 {
516 Q_ASSERT( path.elementAt( i + 1 ).type == QPainterPath::CurveToDataElement );
517 Q_ASSERT( path.elementAt( i + 2 ).type == QPainterPath::CurveToDataElement );
518
519 const double x1 = path.elementAt( i - 1 ).x;
520 const double y1 = path.elementAt( i - 1 ).y;
521
522 const double x3 = path.elementAt( i + 1 ).x;
523 const double y3 = path.elementAt( i + 1 ).y;
524
525 const double x4 = path.elementAt( i + 2 ).x;
526 const double y4 = path.elementAt( i + 2 ).y;
527
528 // TODO -- we could likely reduce the number of segmented points here!
529 std::unique_ptr< QgsLineString> bezier( QgsLineString::fromBezierCurve(
530 QgsPoint( x1, y1 ),
531 QgsPoint( e.x, e.y ),
532 QgsPoint( x3, y3 ),
533 QgsPoint( x4, y4 ) ) );
534
535 currentX << bezier->xVector();
536 currentY << bezier->yVector();
537
538 i += 2;
539 break;
540 }
541 case QPainterPath::CurveToDataElement:
542 Q_ASSERT( !"addSubpathGeometries(), bad element type" );
543 break;
544 }
545 }
546
547 if ( currentX.size() > 1 )
548 {
549 std::unique_ptr< QgsLineString > line = std::make_unique< QgsLineString >( currentX, currentY );
550 if ( mUsePathStroker )
551 {
552 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
553 }
554 else if ( line->isClosed() )
555 {
556 if ( !transformIsIdentity )
557 line->transform( matrix );
558 if ( mSimplifyTolerance > 0 )
559 {
560 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
561 line.reset();
562 }
563 else
564 {
565 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
566 }
567 }
568 else
569 {
570 if ( !transformIsIdentity )
571 line->transform( matrix );
572 if ( mSimplifyTolerance > 0 )
573 {
574 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
575 line.reset();
576 }
577 else
578 {
579 mGeometry.addGeometry( line.release() );
580 }
581 }
582 }
583
584 if ( queuedPolygons.empty() )
585 return;
586
587 mGeometry.reserve( static_cast< int >( mGeometry.numGeometries() + queuedPolygons.size() ) );
588
589 QgsMultiPolygon tempMultiPolygon;
590 tempMultiPolygon.reserve( static_cast< int >( queuedPolygons.size() ) );
591 for ( auto &part : queuedPolygons )
592 {
593 tempMultiPolygon.addGeometry( part.release() );
594 }
595
596 // ensure holes are holes, not overlapping polygons
597 QgsGeos geosCollection( &tempMultiPolygon );
598 std::unique_ptr< QgsAbstractGeometry > g = geosCollection.makeValid( Qgis::MakeValidMethod::Linework );
599 if ( !g )
600 return;
601
602 for ( auto it = g->const_parts_begin(); it != g->const_parts_end(); ++it )
603 {
604 mGeometry.addGeometry( ( *it )->clone() );
605 }
606}
607
608void QgsGeometryPaintEngine::drawPath( const QPainterPath &path )
609{
610 const QTransform transform = painter()->combinedTransform();
611 addSubpathGeometries( path, transform );
612}
613
614
615//
616// QgsGeometryPaintDevice
617//
618
620{
621 mPaintEngine = std::make_unique<QgsGeometryPaintEngine>( usePathStroker );
622}
623
625{
626 if ( mPaintEngine )
627 mPaintEngine->setStrokedPathSegments( segments );
628}
629
631{
632 if ( mPaintEngine )
633 mPaintEngine->setSimplificationTolerance( tolerance );
634}
635
637{
638 return mPaintEngine.get();
639}
640
641int QgsGeometryPaintDevice::metric( PaintDeviceMetric m ) const
642{
643 // copy/paste from qpicture.cpp
644 int val;
645
646 switch ( m )
647 {
648 case PdmWidth:
649 val = static_cast< int >( mPaintEngine->geometry().boundingBox().width() );
650 break;
651 case PdmHeight:
652 val = static_cast< int >( mPaintEngine->geometry().boundingBox().height() );
653 break;
654 case PdmWidthMM:
655 val = static_cast< int >( 25.4 / qt_defaultDpiX() * mPaintEngine->geometry().boundingBox().width() );
656 break;
657 case PdmHeightMM:
658 val = static_cast< int >( 25.4 / qt_defaultDpiY() * mPaintEngine->geometry().boundingBox().height() );
659 break;
660 case PdmDpiX:
661 case PdmPhysicalDpiX:
662 val = qt_defaultDpiX();
663 break;
664 case PdmDpiY:
665 case PdmPhysicalDpiY:
666 val = qt_defaultDpiY();
667 break;
668 case PdmNumColors:
669 val = 16777216;
670 break;
671 case PdmDepth:
672 val = 24;
673 break;
674 case PdmDevicePixelRatio:
675 val = 1;
676 break;
677 case PdmDevicePixelRatioScaled:
678 val = static_cast< int >( 1 * QPaintDevice::devicePixelRatioFScale() );
679 break;
680 default:
681 val = 0;
682 qWarning( "QPicture::metric: Invalid metric command" );
683 }
684 return val;
685}
686
688{
689 return mPaintEngine->geometry();
690}
691
693{
695 QPainter painter( &device );
696 painter.drawPath( path );
697 painter.end();
698 return QgsGeometry( device.geometry().clone() );
699}
700
JoinStyle
Join styles for buffers.
Definition qgis.h:1791
@ Bevel
Use beveled joins.
@ Round
Use rounded joins.
@ Miter
Use mitered joins.
EndCapStyle
End cap styles for buffers.
Definition qgis.h:1778
@ Flat
Flat cap (in line with start/end of line)
@ Round
Round cap.
@ Square
Square cap (extends past start/end of line by buffer distance)
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
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 addGeometries(const QVector< QgsAbstractGeometry * > &geometries)
Adds a list of geometries to the collection, transferring ownership to the collection.
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.
A paint device which converts everything renderer to a QgsGeometry representation of the rendered sha...
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, exception handling*.
Definition qgsgeos.h:137
Line string geometry type, with support for z-dimension and m-values.
QgsLineString * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
static 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,...
Multi polygon geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Polygon geometry type.
Definition qgspolygon.h:33
Contains geos related utilities and functions.
Definition qgsgeos.h:75
void drawPointsImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount)
Q_GUI_EXPORT int qt_defaultDpiX()
void drawRectsImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *rects, int rectCount)
Q_GUI_EXPORT int qt_defaultDpiY()
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)
Q_GUI_EXPORT int qt_defaultDpiX()
Q_GUI_EXPORT int qt_defaultDpiY()