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