QGIS API Documentation 3.39.0-Master (3783037d301)
Loading...
Searching...
No Matches
qgslayoutitempolyline.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitempolyline.cpp
3 begin : March 2016
4 copyright : (C) 2016 Paul Blottiere, Oslandia
5 email : paul dot blottiere at oslandia dot com
6 ***************************************************************************/
7
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
19#include "qgssymbollayerutils.h"
20#include "qgscolorutils.h"
21#include "qgssymbol.h"
22#include "qgslayout.h"
24#include "qgslayoututils.h"
25#include "qgsreadwritecontext.h"
26#include "qgssvgcache.h"
28#include "qgslinesymbol.h"
29
30#include <QSvgRenderer>
31#include <limits>
32#include <QGraphicsPathItem>
33#include <QVector2D>
34
36 : QgsLayoutNodesItem( layout )
37{
38 createDefaultPolylineStyleSymbol();
39}
40
41QgsLayoutItemPolyline::QgsLayoutItemPolyline( const QPolygonF &polyline, QgsLayout *layout )
42 : QgsLayoutNodesItem( polyline, layout )
43{
44 createDefaultPolylineStyleSymbol();
45}
46
48
53
58
60{
61 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemPolyline.svg" ) );
62}
63
64bool QgsLayoutItemPolyline::_addNode( const int indexPoint,
65 QPointF newPoint,
66 const double radius )
67{
68 const double distStart = computeDistance( newPoint, mPolygon[0] );
69 const double distEnd = computeDistance( newPoint, mPolygon[mPolygon.size() - 1] );
70
71 if ( indexPoint == ( mPolygon.size() - 1 ) )
72 {
73 if ( distEnd < radius )
74 mPolygon.append( newPoint );
75 else if ( distStart < radius )
76 mPolygon.insert( 0, newPoint );
77 }
78 else
79 mPolygon.insert( indexPoint + 1, newPoint );
80
81 return true;
82}
83
85{
86 if ( index < 0 || index >= mPolygon.size() )
87 return false;
88
89 mPolygon.remove( index );
90
91 if ( mPolygon.size() < 2 )
92 mPolygon.clear();
93 else
94 {
95 int newSelectNode = index;
96 if ( index >= mPolygon.size() )
97 newSelectNode = mPolygon.size() - 1;
98 setSelectedNode( newSelectNode );
99 }
100
101 return true;
102}
103
104void QgsLayoutItemPolyline::createDefaultPolylineStyleSymbol()
105{
106 QVariantMap properties;
107 properties.insert( QStringLiteral( "color" ), QStringLiteral( "0,0,0,255" ) );
108 properties.insert( QStringLiteral( "width" ), QStringLiteral( "0.3" ) );
109 properties.insert( QStringLiteral( "capstyle" ), QStringLiteral( "square" ) );
110
111 mPolylineStyleSymbol.reset( QgsLineSymbol::createSimple( properties ) );
112 refreshSymbol();
113}
114
115void QgsLayoutItemPolyline::refreshSymbol()
116{
117 if ( auto *lLayout = layout() )
118 {
119 const QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( lLayout, nullptr, lLayout->renderContext().dpi() );
120 mMaxSymbolBleed = ( 25.4 / lLayout->renderContext().dpi() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mPolylineStyleSymbol.get(), rc );
121 }
122
124
125 emit frameChanged();
126}
127
128void QgsLayoutItemPolyline::drawStartMarker( QPainter *painter )
129{
130 if ( mPolygon.size() < 2 )
131 return;
132
133 switch ( mStartMarker )
134 {
136 break;
137
139 {
140 // calculate angle at start of line
141 const QLineF startLine( mPolygon.at( 0 ), mPolygon.at( 1 ) );
142 const double angle = startLine.angle();
143 drawArrow( painter, mPolygon.at( 0 ), angle );
144 break;
145 }
146
148 {
149 // calculate angle at start of line
150 const QLineF startLine( mPolygon.at( 0 ), mPolygon.at( 1 ) );
151 const double angle = startLine.angle();
152 drawSvgMarker( painter, mPolygon.at( 0 ), angle, mStartMarkerFile, mStartArrowHeadHeight );
153 break;
154 }
155 }
156
157}
158
159void QgsLayoutItemPolyline::drawEndMarker( QPainter *painter )
160{
161 if ( mPolygon.size() < 2 )
162 return;
163
164 switch ( mEndMarker )
165 {
167 break;
168
170 {
171 // calculate angle at end of line
172 const QLineF endLine( mPolygon.at( mPolygon.count() - 2 ), mPolygon.at( mPolygon.count() - 1 ) );
173 const double angle = endLine.angle();
174
175 //move end point depending on arrow width
176 const QVector2D dir = QVector2D( endLine.dx(), endLine.dy() ).normalized();
177 QPointF endPoint = endLine.p2();
178 endPoint += ( dir * 0.5 * mArrowHeadWidth ).toPointF();
179
180 drawArrow( painter, endPoint, angle );
181 break;
182 }
184 {
185 // calculate angle at end of line
186 const QLineF endLine( mPolygon.at( mPolygon.count() - 2 ), mPolygon.at( mPolygon.count() - 1 ) );
187 const double angle = endLine.angle();
188 drawSvgMarker( painter, endLine.p2(), angle, mEndMarkerFile, mEndArrowHeadHeight );
189 break;
190 }
191 }
192}
193
194void QgsLayoutItemPolyline::drawArrow( QPainter *painter, QPointF center, double angle )
195{
196 // translate angle from ccw from axis to cw from north
197 angle = 90 - angle;
198 QPen p;
199 p.setColor( mArrowHeadStrokeColor );
200 p.setWidthF( mArrowHeadStrokeWidth );
201 painter->setPen( p );
202 QBrush b;
203 b.setColor( mArrowHeadFillColor );
204 painter->setBrush( b );
205
206 drawArrowHead( painter, center.x(), center.y(), angle, mArrowHeadWidth );
207}
208
209void QgsLayoutItemPolyline::updateMarkerSvgSizes()
210{
211 setStartSvgMarkerPath( mStartMarkerFile );
212 setEndSvgMarkerPath( mEndMarkerFile );
213}
214
215void QgsLayoutItemPolyline::drawArrowHead( QPainter *p, const double x, const double y, const double angle, const double arrowHeadWidth )
216{
217 if ( !p )
218 return;
219
220 const double angleRad = angle / 180.0 * M_PI;
221 const QPointF middlePoint( x, y );
222
223 //rotate both arrow points
224 const QPointF p1 = QPointF( -arrowHeadWidth / 2.0, arrowHeadWidth );
225 const QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth );
226
227 QPointF p1Rotated, p2Rotated;
228 p1Rotated.setX( p1.x() * std::cos( angleRad ) + p1.y() * -std::sin( angleRad ) );
229 p1Rotated.setY( p1.x() * std::sin( angleRad ) + p1.y() * std::cos( angleRad ) );
230 p2Rotated.setX( p2.x() * std::cos( angleRad ) + p2.y() * -std::sin( angleRad ) );
231 p2Rotated.setY( p2.x() * std::sin( angleRad ) + p2.y() * std::cos( angleRad ) );
232
233 QPolygonF arrowHeadPoly;
234 arrowHeadPoly << middlePoint;
235 arrowHeadPoly << QPointF( middlePoint.x() + p1Rotated.x(), middlePoint.y() + p1Rotated.y() );
236 arrowHeadPoly << QPointF( middlePoint.x() + p2Rotated.x(), middlePoint.y() + p2Rotated.y() );
237 QPen arrowPen = p->pen();
238 arrowPen.setJoinStyle( Qt::RoundJoin );
239 QBrush arrowBrush = p->brush();
240 arrowBrush.setStyle( Qt::SolidPattern );
241 p->setPen( arrowPen );
242 p->setBrush( arrowBrush );
243 arrowBrush.setStyle( Qt::SolidPattern );
244 p->drawPolygon( arrowHeadPoly );
245}
246
247void QgsLayoutItemPolyline::drawSvgMarker( QPainter *p, QPointF point, double angle, const QString &markerPath, double height ) const
248{
249 // translate angle from ccw from axis to cw from north
250 angle = 90 - angle;
251
252 if ( mArrowHeadWidth <= 0 || height <= 0 )
253 {
254 //bad image size
255 return;
256 }
257
258 if ( markerPath.isEmpty() )
259 return;
260
261 QSvgRenderer r;
262 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( markerPath, mArrowHeadWidth, mArrowHeadFillColor, mArrowHeadStrokeColor, mArrowHeadStrokeWidth,
263 1.0 );
264 r.load( svgContent );
265
266 const QgsScopedQPainterState painterState( p );
267 p->translate( point.x(), point.y() );
268 p->rotate( angle );
269 p->translate( -mArrowHeadWidth / 2.0, -height / 2.0 );
270 r.render( p, QRectF( 0, 0, mArrowHeadWidth, height ) );
271}
272
274{
275 if ( !id().isEmpty() )
276 return id();
277
278 return tr( "<Polyline>" );
279}
280
281void QgsLayoutItemPolyline::_draw( QgsLayoutItemRenderContext &context, const QStyleOptionGraphicsItem * )
282{
283 QgsRenderContext renderContext = context.renderContext();
284 // symbol clipping messes with geometry generators used in the symbol for this item, and has no
285 // valid use here. See https://github.com/qgis/QGIS/issues/58909
287
288 const QgsScopedQPainterState painterState( renderContext.painter() );
289 //setup painter scaling to dots so that raster symbology is drawn to scale
290 const double scale = renderContext.convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
291 const QTransform t = QTransform::fromScale( scale, scale );
292
293 mPolylineStyleSymbol->startRender( renderContext );
294 mPolylineStyleSymbol->renderPolyline( t.map( mPolygon ), nullptr, renderContext );
295 mPolylineStyleSymbol->stopRender( renderContext );
296
297 // painter is scaled to dots, so scale back to layout units
298 renderContext.painter()->scale( renderContext.scaleFactor(), renderContext.scaleFactor() );
299
300 drawStartMarker( renderContext.painter() );
301 drawEndMarker( renderContext.painter() );
302}
303
304void QgsLayoutItemPolyline::_readXmlStyle( const QDomElement &elmt, const QgsReadWriteContext &context )
305{
306 mPolylineStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( elmt, context ) );
307}
308
310{
311 mPolylineStyleSymbol.reset( static_cast<QgsLineSymbol *>( symbol->clone() ) );
312 refreshSymbol();
313}
314
316{
317 mStartMarker = mode;
318 update();
319}
320
322{
323 mEndMarker = mode;
324 update();
325}
326
328{
329 mArrowHeadWidth = width;
330 updateMarkerSvgSizes();
331 update();
332}
333
335{
336 QPainterPath path;
337 path.addPolygon( mPolygon );
338
339 QPainterPathStroker ps;
340
341 ps.setWidth( 2 * mMaxSymbolBleed );
342 const QPainterPath strokedOutline = ps.createStroke( path );
343
344 return strokedOutline;
345}
346
348{
349 return mPolylineStyleSymbol.get();
350}
351
353{
354 QSvgRenderer r;
355 mStartMarkerFile = path;
356 if ( path.isEmpty() || !r.load( path ) )
357 {
358 mStartArrowHeadHeight = 0;
359 }
360 else
361 {
362 //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
363 const QRect viewBox = r.viewBox();
364 mStartArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
365 }
367}
368
370{
371 QSvgRenderer r;
372 mEndMarkerFile = path;
373 if ( path.isEmpty() || !r.load( path ) )
374 {
375 mEndArrowHeadHeight = 0;
376 }
377 else
378 {
379 //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
380 const QRect viewBox = r.viewBox();
381 mEndArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
382 }
384}
385
387{
388 mArrowHeadStrokeColor = color;
389 update();
390}
391
393{
394 mArrowHeadFillColor = color;
395 update();
396}
397
399{
400 mArrowHeadStrokeWidth = width;
402 update();
403}
404
406{
407 if ( mPolylineStyleSymbol )
408 {
409 QgsStyleSymbolEntity entity( mPolylineStyleSymbol.get() );
410 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
411 return false;
412 }
413
414 return true;
415}
416
417void QgsLayoutItemPolyline::_writeXmlStyle( QDomDocument &doc, QDomElement &elmt, const QgsReadWriteContext &context ) const
418{
419 const QDomElement pe = QgsSymbolLayerUtils::saveSymbol( QString(),
420 mPolylineStyleSymbol.get(),
421 doc,
422 context );
423 elmt.appendChild( pe );
424}
425
426bool QgsLayoutItemPolyline::writePropertiesToElement( QDomElement &elmt, QDomDocument &doc, const QgsReadWriteContext &context ) const
427{
429
430 // absolute paths to relative
431 const QString startMarkerPath = QgsSymbolLayerUtils::svgSymbolPathToName( mStartMarkerFile, context.pathResolver() );
432 const QString endMarkerPath = QgsSymbolLayerUtils::svgSymbolPathToName( mEndMarkerFile, context.pathResolver() );
433
434 elmt.setAttribute( QStringLiteral( "arrowHeadWidth" ), QString::number( mArrowHeadWidth ) );
435 elmt.setAttribute( QStringLiteral( "arrowHeadFillColor" ), QgsColorUtils::colorToString( mArrowHeadFillColor ) );
436 elmt.setAttribute( QStringLiteral( "arrowHeadOutlineColor" ), QgsColorUtils::colorToString( mArrowHeadStrokeColor ) );
437 elmt.setAttribute( QStringLiteral( "outlineWidth" ), QString::number( mArrowHeadStrokeWidth ) );
438 elmt.setAttribute( QStringLiteral( "markerMode" ), mEndMarker );
439 elmt.setAttribute( QStringLiteral( "startMarkerMode" ), mStartMarker );
440 elmt.setAttribute( QStringLiteral( "startMarkerFile" ), startMarkerPath );
441 elmt.setAttribute( QStringLiteral( "endMarkerFile" ), endMarkerPath );
442
443 return true;
444}
445
446bool QgsLayoutItemPolyline::readPropertiesFromElement( const QDomElement &elmt, const QDomDocument &doc, const QgsReadWriteContext &context )
447{
448 mArrowHeadWidth = elmt.attribute( QStringLiteral( "arrowHeadWidth" ), QStringLiteral( "2.0" ) ).toDouble();
449 mArrowHeadFillColor = QgsColorUtils::colorFromString( elmt.attribute( QStringLiteral( "arrowHeadFillColor" ), QStringLiteral( "0,0,0,255" ) ) );
450 mArrowHeadStrokeColor = QgsColorUtils::colorFromString( elmt.attribute( QStringLiteral( "arrowHeadOutlineColor" ), QStringLiteral( "0,0,0,255" ) ) );
451 mArrowHeadStrokeWidth = elmt.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "1.0" ) ).toDouble();
452 // relative paths to absolute
453 const QString startMarkerPath = elmt.attribute( QStringLiteral( "startMarkerFile" ), QString() );
454 const QString endMarkerPath = elmt.attribute( QStringLiteral( "endMarkerFile" ), QString() );
457 mEndMarker = static_cast< QgsLayoutItemPolyline::MarkerMode >( elmt.attribute( QStringLiteral( "markerMode" ), QStringLiteral( "0" ) ).toInt() );
458 mStartMarker = static_cast< QgsLayoutItemPolyline::MarkerMode >( elmt.attribute( QStringLiteral( "startMarkerMode" ), QStringLiteral( "0" ) ).toInt() );
459
461
463 return true;
464}
465
467{
468 QRectF br = rect();
469
470 double margin = std::max( mMaxSymbolBleed, computeMarkerMargin() );
471 if ( mEndMarker == ArrowHead )
472 {
473 margin += 0.5 * mArrowHeadWidth;
474 }
475 br.adjust( -margin, -margin, margin, margin );
476 prepareGeometryChange();
478
479 // update
480 update();
481}
482
483
484double QgsLayoutItemPolyline::computeMarkerMargin() const
485{
486 double margin = 0;
487
488 if ( mStartMarker == ArrowHead || mEndMarker == ArrowHead )
489 {
490 margin = mArrowHeadStrokeWidth / 2.0 + mArrowHeadWidth * M_SQRT2;
491 }
492
493 if ( mStartMarker == SvgMarker )
494 {
495 const double startMarkerMargin = std::sqrt( 0.25 * ( mStartArrowHeadHeight * mStartArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) );
496 margin = std::max( startMarkerMargin, margin );
497 }
498
499 if ( mEndMarker == SvgMarker )
500 {
501 const double endMarkerMargin = std::sqrt( 0.25 * ( mEndArrowHeadHeight * mEndArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) );
502 margin = std::max( endMarkerMargin, margin );
503 }
504
505 return margin;
506}
@ Millimeters
Millimeters.
@ DisableSymbolClippingToExtent
Force symbol clipping to map extent to be disabled in all situations. This will result in slower rend...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Layout item for node based polyline shapes.
void setArrowHeadWidth(double width)
Sets the width of line arrow heads in mm.
void setEndMarker(MarkerMode mode)
Sets the end marker mode, which controls what marker is drawn at the end of the line.
void setEndSvgMarkerPath(const QString &path)
Sets the path to a SVG marker to draw at the end of the line.
void _writeXmlStyle(QDomDocument &doc, QDomElement &elmt, const QgsReadWriteContext &context) const override
Method called in writeXml.
void setArrowHeadStrokeWidth(double width)
Sets the pen width in millimeters for the stroke of the arrow head.
void _readXmlStyle(const QDomElement &elmt, const QgsReadWriteContext &context) override
Method called in readXml.
void setArrowHeadFillColor(const QColor &color)
Sets the color used to fill the arrow head.
bool _removeNode(int nodeIndex) override
Method called in removeNode.
QgsLineSymbol * symbol()
Returns the line symbol used to draw the shape.
void setArrowHeadStrokeColor(const QColor &color)
Sets the color used to draw the stroke around the arrow head.
void setStartMarker(MarkerMode mode)
Sets the start marker mode, which controls what marker is drawn at the start of the line.
MarkerMode
Vertex marker mode.
@ ArrowHead
Show arrow marker.
@ NoMarker
Don't show marker.
QPainterPath shape() const override
~QgsLayoutItemPolyline() override
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
QString displayName() const override
Gets item display name.
static QgsLayoutItemPolyline * create(QgsLayout *layout)
Returns a new polyline item for the specified layout.
bool _addNode(int indexPoint, QPointF newPoint, double radius) override
Method called in addNode.
void setSymbol(QgsLineSymbol *symbol)
Sets the symbol used to draw the shape.
QIcon icon() const override
Returns the item's icon.
double arrowHeadWidth() const
Returns the width of line arrow heads in mm.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void _draw(QgsLayoutItemRenderContext &context, const QStyleOptionGraphicsItem *itemStyle=nullptr) override
Method called in paint.
void setStartSvgMarkerPath(const QString &path)
Sets the path to a SVG marker to draw at the start of the line.
QgsLayoutItemPolyline(QgsLayout *layout)
Constructor for QgsLayoutItemPolyline for the specified layout.
@ LayoutPolyline
Polyline shape item.
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
virtual QString uuid() const
Returns the item identification string.
QString id() const
Returns the item's ID name.
void frameChanged()
Emitted if the item's frame style changes.
An abstract layout item that provides generic methods for node based shapes such as polygon or polyli...
double mMaxSymbolBleed
Max symbol bleed.
QRectF mCurrentRectangle
Current bounding rectangle of shape.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
bool setSelectedNode(int index)
Selects a node by index.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void updateSceneRect()
Update the current scene rectangle for this item.
double computeDistance(QPointF pt1, QPointF pt2) const
Compute an euclidean distance between 2 nodes.
QPolygonF mPolygon
Shape's nodes.
const QgsLayout * layout() const
Returns the layout the object is attached to.
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:49
A line symbol type, for rendering LineString and MultiLineString geometries.
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
static QgsLineSymbol * createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
The class is used as a container of context for various read/write operations on other objects.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
Scoped object for saving and restoring a QPainter object's state.
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
Definition qgsstyle.h:1396
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol's name from its path.
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Contains information relating to the style entity currently being visited.