QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 "qgssymbol.h"
21#include "qgslayout.h"
22#include "qgsmapsettings.h"
23#include "qgslayoututils.h"
24#include "qgsreadwritecontext.h"
25#include "qgssvgcache.h"
27#include "qgslinesymbol.h"
28
29#include <QSvgRenderer>
30#include <limits>
31#include <QGraphicsPathItem>
32#include <QVector2D>
33
35 : QgsLayoutNodesItem( layout )
36{
37 createDefaultPolylineStyleSymbol();
38}
39
40QgsLayoutItemPolyline::QgsLayoutItemPolyline( const QPolygonF &polyline, QgsLayout *layout )
41 : QgsLayoutNodesItem( polyline, layout )
42{
43 createDefaultPolylineStyleSymbol();
44}
45
47
49{
50 return new QgsLayoutItemPolyline( layout );
51}
52
54{
56}
57
59{
60 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemPolyline.svg" ) );
61}
62
63bool QgsLayoutItemPolyline::_addNode( const int indexPoint,
64 QPointF newPoint,
65 const double radius )
66{
67 const double distStart = computeDistance( newPoint, mPolygon[0] );
68 const double distEnd = computeDistance( newPoint, mPolygon[mPolygon.size() - 1] );
69
70 if ( indexPoint == ( mPolygon.size() - 1 ) )
71 {
72 if ( distEnd < radius )
73 mPolygon.append( newPoint );
74 else if ( distStart < radius )
75 mPolygon.insert( 0, newPoint );
76 }
77 else
78 mPolygon.insert( indexPoint + 1, newPoint );
79
80 return true;
81}
82
84{
85 if ( index < 0 || index >= mPolygon.size() )
86 return false;
87
88 mPolygon.remove( index );
89
90 if ( mPolygon.size() < 2 )
91 mPolygon.clear();
92 else
93 {
94 int newSelectNode = index;
95 if ( index >= mPolygon.size() )
96 newSelectNode = mPolygon.size() - 1;
97 setSelectedNode( newSelectNode );
98 }
99
100 return true;
101}
102
103void QgsLayoutItemPolyline::createDefaultPolylineStyleSymbol()
104{
105 QVariantMap properties;
106 properties.insert( QStringLiteral( "color" ), QStringLiteral( "0,0,0,255" ) );
107 properties.insert( QStringLiteral( "width" ), QStringLiteral( "0.3" ) );
108 properties.insert( QStringLiteral( "capstyle" ), QStringLiteral( "square" ) );
109
110 mPolylineStyleSymbol.reset( QgsLineSymbol::createSimple( properties ) );
111 refreshSymbol();
112}
113
114void QgsLayoutItemPolyline::refreshSymbol()
115{
116 if ( auto *lLayout = layout() )
117 {
118 const QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( lLayout, nullptr, lLayout->renderContext().dpi() );
119 mMaxSymbolBleed = ( 25.4 / lLayout->renderContext().dpi() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mPolylineStyleSymbol.get(), rc );
120 }
121
123
124 emit frameChanged();
125}
126
127void QgsLayoutItemPolyline::drawStartMarker( QPainter *painter )
128{
129 if ( mPolygon.size() < 2 )
130 return;
131
132 switch ( mStartMarker )
133 {
134 case MarkerMode::NoMarker:
135 break;
136
137 case MarkerMode::ArrowHead:
138 {
139 // calculate angle at start of line
140 const QLineF startLine( mPolygon.at( 0 ), mPolygon.at( 1 ) );
141 const double angle = startLine.angle();
142 drawArrow( painter, mPolygon.at( 0 ), angle );
143 break;
144 }
145
146 case MarkerMode::SvgMarker:
147 {
148 // calculate angle at start of line
149 const QLineF startLine( mPolygon.at( 0 ), mPolygon.at( 1 ) );
150 const double angle = startLine.angle();
151 drawSvgMarker( painter, mPolygon.at( 0 ), angle, mStartMarkerFile, mStartArrowHeadHeight );
152 break;
153 }
154 }
155
156}
157
158void QgsLayoutItemPolyline::drawEndMarker( QPainter *painter )
159{
160 if ( mPolygon.size() < 2 )
161 return;
162
163 switch ( mEndMarker )
164 {
165 case MarkerMode::NoMarker:
166 break;
167
168 case MarkerMode::ArrowHead:
169 {
170 // calculate angle at end of line
171 const QLineF endLine( mPolygon.at( mPolygon.count() - 2 ), mPolygon.at( mPolygon.count() - 1 ) );
172 const double angle = endLine.angle();
173
174 //move end point depending on arrow width
175 const QVector2D dir = QVector2D( endLine.dx(), endLine.dy() ).normalized();
176 QPointF endPoint = endLine.p2();
177 endPoint += ( dir * 0.5 * mArrowHeadWidth ).toPointF();
178
179 drawArrow( painter, endPoint, angle );
180 break;
181 }
182 case MarkerMode::SvgMarker:
183 {
184 // calculate angle at end of line
185 const QLineF endLine( mPolygon.at( mPolygon.count() - 2 ), mPolygon.at( mPolygon.count() - 1 ) );
186 const double angle = endLine.angle();
187 drawSvgMarker( painter, endLine.p2(), angle, mEndMarkerFile, mEndArrowHeadHeight );
188 break;
189 }
190 }
191}
192
193void QgsLayoutItemPolyline::drawArrow( QPainter *painter, QPointF center, double angle )
194{
195 // translate angle from ccw from axis to cw from north
196 angle = 90 - angle;
197 QPen p;
198 p.setColor( mArrowHeadStrokeColor );
199 p.setWidthF( mArrowHeadStrokeWidth );
200 painter->setPen( p );
201 QBrush b;
202 b.setColor( mArrowHeadFillColor );
203 painter->setBrush( b );
204
205 drawArrowHead( painter, center.x(), center.y(), angle, mArrowHeadWidth );
206}
207
208void QgsLayoutItemPolyline::updateMarkerSvgSizes()
209{
210 setStartSvgMarkerPath( mStartMarkerFile );
211 setEndSvgMarkerPath( mEndMarkerFile );
212}
213
214void QgsLayoutItemPolyline::drawArrowHead( QPainter *p, const double x, const double y, const double angle, const double arrowHeadWidth )
215{
216 if ( !p )
217 return;
218
219 const double angleRad = angle / 180.0 * M_PI;
220 const QPointF middlePoint( x, y );
221
222 //rotate both arrow points
223 const QPointF p1 = QPointF( -arrowHeadWidth / 2.0, arrowHeadWidth );
224 const QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth );
225
226 QPointF p1Rotated, p2Rotated;
227 p1Rotated.setX( p1.x() * std::cos( angleRad ) + p1.y() * -std::sin( angleRad ) );
228 p1Rotated.setY( p1.x() * std::sin( angleRad ) + p1.y() * std::cos( angleRad ) );
229 p2Rotated.setX( p2.x() * std::cos( angleRad ) + p2.y() * -std::sin( angleRad ) );
230 p2Rotated.setY( p2.x() * std::sin( angleRad ) + p2.y() * std::cos( angleRad ) );
231
232 QPolygonF arrowHeadPoly;
233 arrowHeadPoly << middlePoint;
234 arrowHeadPoly << QPointF( middlePoint.x() + p1Rotated.x(), middlePoint.y() + p1Rotated.y() );
235 arrowHeadPoly << QPointF( middlePoint.x() + p2Rotated.x(), middlePoint.y() + p2Rotated.y() );
236 QPen arrowPen = p->pen();
237 arrowPen.setJoinStyle( Qt::RoundJoin );
238 QBrush arrowBrush = p->brush();
239 arrowBrush.setStyle( Qt::SolidPattern );
240 p->setPen( arrowPen );
241 p->setBrush( arrowBrush );
242 arrowBrush.setStyle( Qt::SolidPattern );
243 p->drawPolygon( arrowHeadPoly );
244}
245
246void QgsLayoutItemPolyline::drawSvgMarker( QPainter *p, QPointF point, double angle, const QString &markerPath, double height ) const
247{
248 // translate angle from ccw from axis to cw from north
249 angle = 90 - angle;
250
251 if ( mArrowHeadWidth <= 0 || height <= 0 )
252 {
253 //bad image size
254 return;
255 }
256
257 if ( markerPath.isEmpty() )
258 return;
259
260 QSvgRenderer r;
261 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( markerPath, mArrowHeadWidth, mArrowHeadFillColor, mArrowHeadStrokeColor, mArrowHeadStrokeWidth,
262 1.0 );
263 r.load( svgContent );
264
265 const QgsScopedQPainterState painterState( p );
266 p->translate( point.x(), point.y() );
267 p->rotate( angle );
268 p->translate( -mArrowHeadWidth / 2.0, -height / 2.0 );
269 r.render( p, QRectF( 0, 0, mArrowHeadWidth, height ) );
270}
271
273{
274 if ( !id().isEmpty() )
275 return id();
276
277 return tr( "<Polyline>" );
278}
279
280void QgsLayoutItemPolyline::_draw( QgsLayoutItemRenderContext &context, const QStyleOptionGraphicsItem * )
281{
282 const QgsScopedQPainterState painterState( context.renderContext().painter() );
283 //setup painter scaling to dots so that raster symbology is drawn to scale
284 const double scale = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
285 const QTransform t = QTransform::fromScale( scale, scale );
286
287 mPolylineStyleSymbol->startRender( context.renderContext() );
288 mPolylineStyleSymbol->renderPolyline( t.map( mPolygon ), nullptr, context.renderContext() );
289 mPolylineStyleSymbol->stopRender( context.renderContext() );
290
291 // painter is scaled to dots, so scale back to layout units
292 context.renderContext().painter()->scale( context.renderContext().scaleFactor(), context.renderContext().scaleFactor() );
293
294 drawStartMarker( context.renderContext().painter() );
295 drawEndMarker( context.renderContext().painter() );
296}
297
298void QgsLayoutItemPolyline::_readXmlStyle( const QDomElement &elmt, const QgsReadWriteContext &context )
299{
300 mPolylineStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( elmt, context ) );
301}
302
304{
305 mPolylineStyleSymbol.reset( static_cast<QgsLineSymbol *>( symbol->clone() ) );
306 refreshSymbol();
307}
308
310{
311 mStartMarker = mode;
312 update();
313}
314
316{
317 mEndMarker = mode;
318 update();
319}
320
322{
323 mArrowHeadWidth = width;
324 updateMarkerSvgSizes();
325 update();
326}
327
329{
330 QPainterPath path;
331 path.addPolygon( mPolygon );
332
333 QPainterPathStroker ps;
334
335 ps.setWidth( 2 * mMaxSymbolBleed );
336 const QPainterPath strokedOutline = ps.createStroke( path );
337
338 return strokedOutline;
339}
340
342{
343 return mPolylineStyleSymbol.get();
344}
345
347{
348 QSvgRenderer r;
349 mStartMarkerFile = path;
350 if ( path.isEmpty() || !r.load( path ) )
351 {
352 mStartArrowHeadHeight = 0;
353 }
354 else
355 {
356 //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
357 const QRect viewBox = r.viewBox();
358 mStartArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
359 }
361}
362
364{
365 QSvgRenderer r;
366 mEndMarkerFile = path;
367 if ( path.isEmpty() || !r.load( path ) )
368 {
369 mEndArrowHeadHeight = 0;
370 }
371 else
372 {
373 //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
374 const QRect viewBox = r.viewBox();
375 mEndArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
376 }
378}
379
381{
382 mArrowHeadStrokeColor = color;
383 update();
384}
385
387{
388 mArrowHeadFillColor = color;
389 update();
390}
391
393{
394 mArrowHeadStrokeWidth = width;
396 update();
397}
398
400{
401 if ( mPolylineStyleSymbol )
402 {
403 QgsStyleSymbolEntity entity( mPolylineStyleSymbol.get() );
404 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
405 return false;
406 }
407
408 return true;
409}
410
411void QgsLayoutItemPolyline::_writeXmlStyle( QDomDocument &doc, QDomElement &elmt, const QgsReadWriteContext &context ) const
412{
413 const QDomElement pe = QgsSymbolLayerUtils::saveSymbol( QString(),
414 mPolylineStyleSymbol.get(),
415 doc,
416 context );
417 elmt.appendChild( pe );
418}
419
420bool QgsLayoutItemPolyline::writePropertiesToElement( QDomElement &elmt, QDomDocument &doc, const QgsReadWriteContext &context ) const
421{
423
424 // absolute paths to relative
425 const QString startMarkerPath = QgsSymbolLayerUtils::svgSymbolPathToName( mStartMarkerFile, context.pathResolver() );
426 const QString endMarkerPath = QgsSymbolLayerUtils::svgSymbolPathToName( mEndMarkerFile, context.pathResolver() );
427
428 elmt.setAttribute( QStringLiteral( "arrowHeadWidth" ), QString::number( mArrowHeadWidth ) );
429 elmt.setAttribute( QStringLiteral( "arrowHeadFillColor" ), QgsSymbolLayerUtils::encodeColor( mArrowHeadFillColor ) );
430 elmt.setAttribute( QStringLiteral( "arrowHeadOutlineColor" ), QgsSymbolLayerUtils::encodeColor( mArrowHeadStrokeColor ) );
431 elmt.setAttribute( QStringLiteral( "outlineWidth" ), QString::number( mArrowHeadStrokeWidth ) );
432 elmt.setAttribute( QStringLiteral( "markerMode" ), mEndMarker );
433 elmt.setAttribute( QStringLiteral( "startMarkerMode" ), mStartMarker );
434 elmt.setAttribute( QStringLiteral( "startMarkerFile" ), startMarkerPath );
435 elmt.setAttribute( QStringLiteral( "endMarkerFile" ), endMarkerPath );
436
437 return true;
438}
439
440bool QgsLayoutItemPolyline::readPropertiesFromElement( const QDomElement &elmt, const QDomDocument &doc, const QgsReadWriteContext &context )
441{
442 mArrowHeadWidth = elmt.attribute( QStringLiteral( "arrowHeadWidth" ), QStringLiteral( "2.0" ) ).toDouble();
443 mArrowHeadFillColor = QgsSymbolLayerUtils::decodeColor( elmt.attribute( QStringLiteral( "arrowHeadFillColor" ), QStringLiteral( "0,0,0,255" ) ) );
444 mArrowHeadStrokeColor = QgsSymbolLayerUtils::decodeColor( elmt.attribute( QStringLiteral( "arrowHeadOutlineColor" ), QStringLiteral( "0,0,0,255" ) ) );
445 mArrowHeadStrokeWidth = elmt.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "1.0" ) ).toDouble();
446 // relative paths to absolute
447 const QString startMarkerPath = elmt.attribute( QStringLiteral( "startMarkerFile" ), QString() );
448 const QString endMarkerPath = elmt.attribute( QStringLiteral( "endMarkerFile" ), QString() );
451 mEndMarker = static_cast< QgsLayoutItemPolyline::MarkerMode >( elmt.attribute( QStringLiteral( "markerMode" ), QStringLiteral( "0" ) ).toInt() );
452 mStartMarker = static_cast< QgsLayoutItemPolyline::MarkerMode >( elmt.attribute( QStringLiteral( "startMarkerMode" ), QStringLiteral( "0" ) ).toInt() );
453
455
457 return true;
458}
459
461{
462 QRectF br = rect();
463
464 double margin = std::max( mMaxSymbolBleed, computeMarkerMargin() );
465 if ( mEndMarker == ArrowHead )
466 {
467 margin += 0.5 * mArrowHeadWidth;
468 }
469 br.adjust( -margin, -margin, margin, margin );
471
472 // update
473 prepareGeometryChange();
474 update();
475}
476
477
478double QgsLayoutItemPolyline::computeMarkerMargin() const
479{
480 double margin = 0;
481
482 if ( mStartMarker == ArrowHead || mEndMarker == ArrowHead )
483 {
484 margin = mArrowHeadStrokeWidth / 2.0 + mArrowHeadWidth * M_SQRT2;
485 }
486
487 if ( mStartMarker == SvgMarker )
488 {
489 const double startMarkerMargin = std::sqrt( 0.25 * ( mStartArrowHeadHeight * mStartArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) );
490 margin = std::max( startMarkerMargin, margin );
491 }
492
493 if ( mEndMarker == SvgMarker )
494 {
495 const double endMarkerMargin = std::sqrt( 0.25 * ( mEndArrowHeadHeight * mEndArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) );
496 margin = std::max( endMarkerMargin, margin );
497 }
498
499 return margin;
500}
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...
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.
@ SvgMarker
Show SVG marker.
int type() const override
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.
Definition: qgslayoutitem.h:45
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:72
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:51
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
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.
QPainter * painter()
Returns the destination QPainter for the render operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
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:1342
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 QColor decodeColor(const QString &str)
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 QString encodeColor(const QColor &color)
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.
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
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)
Definition: MathUtils.cpp:786
Contains information relating to the style entity currently being visited.