29 #include <QSvgRenderer>
31 #include <QGraphicsPathItem>
37 createDefaultPolylineStyleSymbol();
43 createDefaultPolylineStyleSymbol();
70 if ( indexPoint == (
mPolygon.size() - 1 ) )
72 if ( distEnd < radius )
74 else if ( distStart < radius )
78 mPolygon.insert( indexPoint + 1, newPoint );
85 if ( index < 0 || index >=
mPolygon.size() )
94 int newSelectNode = index;
103 void QgsLayoutItemPolyline::createDefaultPolylineStyleSymbol()
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" ) );
114 void QgsLayoutItemPolyline::refreshSymbol()
116 if (
auto *lLayout =
layout() )
127 void QgsLayoutItemPolyline::drawStartMarker( QPainter *painter )
132 switch ( mStartMarker )
134 case MarkerMode::NoMarker:
137 case MarkerMode::ArrowHead:
141 const double angle = startLine.angle();
146 case MarkerMode::SvgMarker:
150 const double angle = startLine.angle();
151 drawSvgMarker( painter,
mPolygon.at( 0 ),
angle, mStartMarkerFile, mStartArrowHeadHeight );
158 void QgsLayoutItemPolyline::drawEndMarker( QPainter *painter )
163 switch ( mEndMarker )
165 case MarkerMode::NoMarker:
168 case MarkerMode::ArrowHead:
172 const double angle = endLine.angle();
175 const QVector2D dir = QVector2D( endLine.dx(), endLine.dy() ).normalized();
176 QPointF endPoint = endLine.p2();
177 endPoint += ( dir * 0.5 * mArrowHeadWidth ).toPointF();
179 drawArrow( painter, endPoint,
angle );
182 case MarkerMode::SvgMarker:
186 const double angle = endLine.angle();
187 drawSvgMarker( painter, endLine.p2(),
angle, mEndMarkerFile, mEndArrowHeadHeight );
193 void QgsLayoutItemPolyline::drawArrow( QPainter *painter, QPointF center,
double angle )
198 p.setColor( mArrowHeadStrokeColor );
199 p.setWidthF( mArrowHeadStrokeWidth );
200 painter->setPen( p );
202 b.setColor( mArrowHeadFillColor );
203 painter->setBrush( b );
205 drawArrowHead( painter, center.x(), center.y(),
angle, mArrowHeadWidth );
208 void QgsLayoutItemPolyline::updateMarkerSvgSizes()
214 void QgsLayoutItemPolyline::drawArrowHead( QPainter *p,
const double x,
const double y,
const double angle,
const double arrowHeadWidth )
219 const double angleRad =
angle / 180.0 * M_PI;
220 const QPointF middlePoint( x, y );
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 ) );
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 );
246 void QgsLayoutItemPolyline::drawSvgMarker( QPainter *p, QPointF point,
double angle,
const QString &markerPath,
double height )
const
251 if ( mArrowHeadWidth <= 0 || height <= 0 )
257 if ( markerPath.isEmpty() )
261 const QByteArray &svgContent =
QgsApplication::svgCache()->
svgContent( markerPath, mArrowHeadWidth, mArrowHeadFillColor, mArrowHeadStrokeColor, mArrowHeadStrokeWidth,
263 r.load( svgContent );
266 p->translate( point.x(), point.y() );
268 p->translate( -mArrowHeadWidth / 2.0, -height / 2.0 );
269 r.render( p, QRectF( 0, 0, mArrowHeadWidth, height ) );
274 if ( !
id().isEmpty() )
277 return tr(
"<Polyline>" );
285 const QTransform t = QTransform::fromScale( scale, scale );
287 mPolylineStyleSymbol->startRender( context.
renderContext() );
300 mPolylineStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( elmt, context ) );
323 mArrowHeadWidth = width;
324 updateMarkerSvgSizes();
333 QPainterPathStroker ps;
336 const QPainterPath strokedOutline = ps.createStroke( path );
338 return strokedOutline;
343 return mPolylineStyleSymbol.get();
349 mStartMarkerFile = path;
350 if ( path.isEmpty() || !r.load( path ) )
352 mStartArrowHeadHeight = 0;
357 const QRect viewBox = r.viewBox();
358 mStartArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
366 mEndMarkerFile = path;
367 if ( path.isEmpty() || !r.load( path ) )
369 mEndArrowHeadHeight = 0;
374 const QRect viewBox = r.viewBox();
375 mEndArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
382 mArrowHeadStrokeColor = color;
388 mArrowHeadFillColor = color;
394 mArrowHeadStrokeWidth = width;
401 if ( mPolylineStyleSymbol )
414 mPolylineStyleSymbol.get(),
417 elmt.appendChild( pe );
428 elmt.setAttribute( QStringLiteral(
"arrowHeadWidth" ), QString::number( mArrowHeadWidth ) );
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 );
442 mArrowHeadWidth = elmt.attribute( QStringLiteral(
"arrowHeadWidth" ), QStringLiteral(
"2.0" ) ).toDouble();
445 mArrowHeadStrokeWidth = elmt.attribute( QStringLiteral(
"outlineWidth" ), QStringLiteral(
"1.0" ) ).toDouble();
447 const QString startMarkerPath = elmt.attribute( QStringLiteral(
"startMarkerFile" ), QString() );
448 const QString endMarkerPath = elmt.attribute( QStringLiteral(
"endMarkerFile" ), QString() );
467 margin += 0.5 * mArrowHeadWidth;
469 br.adjust( -margin, -margin, margin, margin );
473 prepareGeometryChange();
478 double QgsLayoutItemPolyline::computeMarkerMargin()
const
484 margin = mArrowHeadStrokeWidth / 2.0 + mArrowHeadWidth * M_SQRT2;
489 const double startMarkerMargin = std::sqrt( 0.25 * ( mStartArrowHeadHeight * mStartArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) );
490 margin = std::max( startMarkerMargin, margin );
495 const double endMarkerMargin = std::sqrt( 0.25 * ( mEndArrowHeadHeight * mEndArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) );
496 margin = std::max( endMarkerMargin, margin );