47 if ( props.contains( QStringLiteral(
"arrow_width" ) ) )
48 l->
setArrowWidth( props[QStringLiteral(
"arrow_width" )].toDouble() );
50 if ( props.contains( QStringLiteral(
"arrow_width_unit" ) ) )
53 if ( props.contains( QStringLiteral(
"arrow_width_unit_scale" ) ) )
56 if ( props.contains( QStringLiteral(
"arrow_start_width" ) ) )
59 if ( props.contains( QStringLiteral(
"arrow_start_width_unit" ) ) )
62 if ( props.contains( QStringLiteral(
"arrow_start_width_unit_scale" ) ) )
65 if ( props.contains( QStringLiteral(
"is_curved" ) ) )
66 l->
setIsCurved( props[QStringLiteral(
"is_curved" )].toInt() == 1 );
68 if ( props.contains( QStringLiteral(
"is_repeated" ) ) )
69 l->
setIsRepeated( props[QStringLiteral(
"is_repeated" )].toInt() == 1 );
71 if ( props.contains( QStringLiteral(
"head_length" ) ) )
72 l->
setHeadLength( props[QStringLiteral(
"head_length" )].toDouble() );
74 if ( props.contains( QStringLiteral(
"head_length_unit" ) ) )
77 if ( props.contains( QStringLiteral(
"head_length_unit_scale" ) ) )
80 if ( props.contains( QStringLiteral(
"head_thickness" ) ) )
83 if ( props.contains( QStringLiteral(
"head_thickness_unit" ) ) )
86 if ( props.contains( QStringLiteral(
"head_thickness_unit_scale" ) ) )
89 if ( props.contains( QStringLiteral(
"head_type" ) ) )
92 if ( props.contains( QStringLiteral(
"arrow_type" ) ) )
95 if ( props.contains( QStringLiteral(
"offset" ) ) )
96 l->
setOffset( props[QStringLiteral(
"offset" )].toDouble() );
98 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
101 if ( props.contains( QStringLiteral(
"offset_unit_scale" ) ) )
104 if ( props.contains( QStringLiteral(
"ring_filter" ) ) )
125 return mSymbol.get();
130 return QStringLiteral(
"ArrowLine" );
137 map[QStringLiteral(
"arrow_width" )] = QString::number(
arrowWidth() );
141 map[QStringLiteral(
"arrow_start_width" )] = QString::number(
arrowStartWidth() );
145 map[QStringLiteral(
"is_curved" )] = QString::number(
isCurved() ? 1 : 0 );
146 map[QStringLiteral(
"is_repeated" )] = QString::number(
isRepeated() ? 1 : 0 );
148 map[QStringLiteral(
"head_length" )] = QString::number(
headLength() );
152 map[QStringLiteral(
"head_thickness" )] = QString::number(
headThickness() );
156 map[QStringLiteral(
"head_type" )] = QString::number(
headType() );
157 map[QStringLiteral(
"arrow_type" )] = QString::number(
arrowType() );
159 map[QStringLiteral(
"offset" )] = QString::number(
offset() );
163 map[QStringLiteral(
"ring_filter" )] = QString::number(
static_cast< int >(
mRingFilter ) );
172 attributes.unite( mSymbol->usedAttributes( context ) );
181 if ( mSymbol && mSymbol->hasDataDefinedProperties() )
199 mArrowWidthUnit = unit;
200 mArrowStartWidthUnit = unit;
201 mHeadLengthUnit = unit;
202 mHeadThicknessUnit = unit;
226 return std::sqrt( ( po.x() - pd.x() ) * ( po.x() - pd.x() ) + ( po.y() - pd.y() ) * ( po.y() - pd.y() ) );
230 qreal startWidth, qreal width,
231 qreal headWidth, qreal headHeight,
242 po = pd - ( pd - po ) / length * headWidth;
247 pd = po + ( pd - po ) / length * headWidth;
252 const QPointF v = ( pd - po ) / length * headWidth;
253 const QPointF npo = ( po + pd ) / 2.0 - v;
254 const QPointF npd = ( po + pd ) / 2.0 + v;
257 length = 2 * headWidth;
260 const qreal bodyLength = length - headWidth;
263 const QPointF unitVec = ( pd - po ) / length;
265 const QPointF perpVec( -unitVec.y(), unitVec.x() );
268 po += perpVec * offset;
269 pd += perpVec * offset;
277 polygon << po + unitVec *headWidth + perpVec *headHeight;
278 polygon << po + unitVec *headWidth + perpVec * ( width * 0.5 );
280 polygon << po + unitVec *bodyLength + perpVec * ( width * 0.5 );
283 polygon << po + unitVec *bodyLength + perpVec *headHeight;
289 polygon << po + unitVec *bodyLength - perpVec *headHeight;
290 polygon << po + unitVec *bodyLength - perpVec * ( width * 0.5 );
293 polygon << po + unitVec *headWidth - perpVec * ( width * 0.5 );
294 polygon << po + unitVec *headWidth - perpVec *headHeight;
301 polygon << po + perpVec * ( startWidth * 0.5 );
302 polygon << po + unitVec *bodyLength + perpVec * ( width * 0.5 );
303 polygon << po + unitVec *bodyLength + perpVec *headHeight;
312 polygon << po + unitVec *bodyLength - perpVec *headHeight;
313 polygon << po + unitVec *bodyLength - perpVec * ( width * 0.5 );
314 polygon << po - perpVec * ( startWidth * 0.5 );
326 polygon << po + unitVec *headWidth + perpVec *headHeight;
327 polygon << po + unitVec *headWidth + perpVec * ( width * 0.5 );
329 polygon << pd + perpVec * ( startWidth * 0.5 );
337 polygon << pd - perpVec * ( startWidth * 0.5 );
339 polygon << po + unitVec *headWidth - perpVec * ( width * 0.5 );
340 polygon << po + unitVec *headWidth - perpVec *headHeight;
348 polygon << polygon.first();
367 bool pointsToCircle( QPointF a, QPointF b, QPointF
c, QPointF ¢er, qreal &radius )
372 const QPointF ab = b - a;
373 const QPointF bc =
c - b;
376 const QPointF ab2 = ( a + b ) / 2.0;
377 const QPointF bc2 = ( b +
c ) / 2.0;
380 if ( std::fabs( ab.x() * bc.y() - ab.y() * bc.x() ) < 0.001 )
387 cy = bc2.y() - ( cx - bc2.x() ) * bc.x() / bc.y();
390 else if ( bc.y() == 0 )
393 cy = ab2.y() - ( cx - ab2.x() ) * ab.x() / ab.y();
398 cx = ( bc2.y() - ab2.y() + bc.x() * bc2.x() / bc.y() - ab.x() * ab2.x() / ab.y() ) / ( bc.x() / bc.y() - ab.x() / ab.y() );
399 cy = bc2.y() - ( cx - bc2.x() ) * bc.x() / bc.y();
402 radius = std::sqrt( ( a.x() - cx ) * ( a.x() - cx ) + ( a.y() - cy ) * ( a.y() - cy ) );
412 return QPointF( std::cos( -
angle ) * radius + center.x(), std::sin( -
angle ) * radius + center.y() );
415 void pathArcTo( QPainterPath &path, QPointF circleCenter, qreal circleRadius, qreal angle_o, qreal angle_d,
int direction )
417 const QRectF circleRect( circleCenter - QPointF( circleRadius, circleRadius ), circleCenter + QPointF( circleRadius, circleRadius ) );
418 if ( direction == 1 )
420 if ( angle_o < angle_d )
421 path.arcTo( circleRect, angle_o / M_PI * 180.0, ( angle_d - angle_o ) / M_PI * 180.0 );
423 path.arcTo( circleRect, angle_o / M_PI * 180.0, 360.0 - ( angle_o - angle_d ) / M_PI * 180.0 );
427 if ( angle_o < angle_d )
428 path.arcTo( circleRect, angle_o / M_PI * 180.0, - ( 360.0 - ( angle_d - angle_o ) / M_PI * 180.0 ) );
430 path.arcTo( circleRect, angle_o / M_PI * 180.0, ( angle_d - angle_o ) / M_PI * 180.0 );
435 void spiralArcTo( QPainterPath &path, QPointF center, qreal startAngle, qreal startRadius, qreal endAngle, qreal endRadius,
int direction )
438 const QPointF A =
circlePoint( center, startRadius, startAngle );
440 const QPointF B =
circlePoint( center, endRadius, endAngle );
444 deltaAngle = endAngle - startAngle;
445 if ( direction * deltaAngle < 0.0 )
446 deltaAngle = deltaAngle + direction * 2 * M_PI;
448 const QPointF I1 =
circlePoint( center, 0.75 * startRadius + 0.25 * endRadius, startAngle + 0.25 * deltaAngle );
449 const QPointF I2 =
circlePoint( center, 0.50 * startRadius + 0.50 * endRadius, startAngle + 0.50 * deltaAngle );
450 const QPointF I3 =
circlePoint( center, 0.25 * startRadius + 0.75 * endRadius, startAngle + 0.75 * deltaAngle );
463 const qreal a1 = std::atan2( cCenter.y() - A.y(), A.x() - cCenter.x() );
464 const qreal a2 = std::atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() );
465 pathArcTo( path, cCenter, cRadius, a1, a2, direction );
477 const qreal a1 = std::atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() );
478 const qreal a2 = std::atan2( cCenter.y() - B.y(), B.x() - cCenter.x() );
479 pathArcTo( path, cCenter, cRadius, a1, a2, direction );
484 qreal startWidth, qreal width,
485 qreal headWidth, qreal headHeight,
490 QPointF circleCenter;
494 return straightArrow( po, pd, startWidth, width, headWidth, headHeight, headType, arrowType, offset );
498 const qreal angle_o =
clampAngle( std::atan2( circleCenter.y() - po.y(), po.x() - circleCenter.x() ) );
499 const qreal angle_m =
clampAngle( std::atan2( circleCenter.y() - pm.y(), pm.x() - circleCenter.x() ) );
500 const qreal angle_d =
clampAngle( std::atan2( circleCenter.y() - pd.y(), pd.x() - circleCenter.x() ) );
503 const int direction =
clampAngle( angle_m - angle_o ) <
clampAngle( angle_m - angle_d ) ? 1 : -1;
512 qreal deltaAngle = angle_d - angle_o;
513 if ( direction * deltaAngle < 0.0 )
514 deltaAngle = deltaAngle + direction * 2 * M_PI;
522 return straightArrow( po, pd, startWidth, width, headWidth, headHeight, headType, arrowType, offset );
526 circleRadius += offset;
527 po =
circlePoint( circleCenter, circleRadius, angle_o );
528 pm =
circlePoint( circleCenter, circleRadius, angle_m );
529 pd =
circlePoint( circleCenter, circleRadius, angle_d );
531 const qreal headAngle = direction * std::atan( headWidth / circleRadius );
541 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_o + headAngle ) );
543 pathArcTo( path, circleCenter, circleRadius + direction * width / 2, angle_o + headAngle, angle_d - headAngle, direction );
546 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_d - headAngle ) );
551 pathArcTo( path, circleCenter, circleRadius, angle_o, angle_d, direction );
555 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_d - headAngle ) );
557 pathArcTo( path, circleCenter, circleRadius - direction * width / 2, angle_d - headAngle, angle_o + headAngle, -direction );
560 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_o + headAngle ) );
565 pathArcTo( path, circleCenter, circleRadius, angle_d, angle_o, -direction );
572 path.moveTo(
circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
574 spiralArcTo( path, circleCenter, angle_o, circleRadius + direction * startWidth / 2, angle_d - headAngle, circleRadius + direction * width / 2, direction );
577 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_d - headAngle ) );
583 pathArcTo( path, circleCenter, circleRadius, angle_o, angle_d, direction );
587 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_d - headAngle ) );
589 spiralArcTo( path, circleCenter, angle_d - headAngle, circleRadius - direction * width / 2, angle_o, circleRadius - direction * startWidth / 2, -direction );
591 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
595 pathArcTo( path, circleCenter, circleRadius, angle_d, angle_o, -direction );
596 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
604 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_o + headAngle ) );
605 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * width / 2, angle_o + headAngle ) );
607 spiralArcTo( path, circleCenter, angle_o + headAngle, circleRadius + direction * width / 2, angle_d, circleRadius + direction * startWidth / 2, direction );
611 pathArcTo( path, circleCenter, circleRadius, angle_o, angle_d, direction );
615 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * startWidth / 2, angle_d ) );
617 spiralArcTo( path, circleCenter, angle_d, circleRadius - direction * startWidth / 2, angle_o + headAngle, circleRadius - direction * width / 2, - direction );
619 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_o + headAngle ) );
625 pathArcTo( path, circleCenter, circleRadius, angle_d, angle_o, -direction );
629 return path.toSubpathPolygons().at( 0 );
642 if ( !exprVal.isNull() )
644 const double w = exprVal.toDouble( &ok );
655 if ( !exprVal.isNull() )
657 const double w = exprVal.toDouble( &ok );
668 if ( !exprVal.isNull() )
670 const double w = exprVal.toDouble( &ok );
681 if ( !exprVal.isNull() )
683 const double w = exprVal.toDouble( &ok );
694 const double w = exprVal.toDouble( &ok );
705 if ( !exprVal.isNull() )
710 mComputedHeadType = h;
719 if ( !exprVal.isNull() )
724 mComputedArrowType = h;
746 const double prevOpacity = mSymbol->opacity();
747 mSymbol->setOpacity( prevOpacity * context.
opacity() );
751 _resolveDataDefined( context );
755 if ( points.size() >= 3 )
758 const QPointF po( points.at( 0 ) );
760 const QPointF pm( points.at( points.size() / 2 ) );
762 const QPointF pd( points.back() );
764 const QPolygonF poly =
curvedArrow( po, pm, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
768 else if ( points.size() == 2 )
771 const QPointF po( points.at( 0 ) );
773 const QPointF pd( points.at( 1 ) );
775 const QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
781 for (
int pIdx = 0; pIdx < points.size() - 1; pIdx += 2 )
787 _resolveDataDefined( context );
789 if ( points.size() - pIdx >= 3 )
792 const QPointF po( points.at( pIdx ) );
794 const QPointF pm( points.at( pIdx + 1 ) );
796 const QPointF pd( points.at( pIdx + 2 ) );
798 const QPolygonF poly =
curvedArrow( po, pm, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
802 else if ( points.size() - pIdx == 2 )
805 const QPointF po( points.at( pIdx ) );
807 const QPointF pd( points.at( pIdx + 1 ) );
809 const QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
819 _resolveDataDefined( context );
821 if ( !points.isEmpty() )
824 const QPointF po( points.at( 0 ) );
826 const QPointF pd( points.back() );
828 const QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
835 for (
int pIdx = 0; pIdx < points.size() - 1; pIdx++ )
841 _resolveDataDefined( context );
844 const QPointF po( points.at( pIdx ) );
846 const QPointF pd( points.at( pIdx + 1 ) );
848 const QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
857 mSymbol->setOpacity( prevOpacity );
864 mSymbol->setColor(
c );
871 return mSymbol.get() ? mSymbol->color() :
mColor;