43 if ( props.contains( QStringLiteral(
"arrow_width" ) ) )
44 l->
setArrowWidth( props[QStringLiteral(
"arrow_width" )].toDouble() );
46 if ( props.contains( QStringLiteral(
"arrow_width_unit" ) ) )
49 if ( props.contains( QStringLiteral(
"arrow_width_unit_scale" ) ) )
52 if ( props.contains( QStringLiteral(
"arrow_start_width" ) ) )
55 if ( props.contains( QStringLiteral(
"arrow_start_width_unit" ) ) )
58 if ( props.contains( QStringLiteral(
"arrow_start_width_unit_scale" ) ) )
61 if ( props.contains( QStringLiteral(
"is_curved" ) ) )
62 l->
setIsCurved( props[QStringLiteral(
"is_curved" )].toInt() == 1 );
64 if ( props.contains( QStringLiteral(
"is_repeated" ) ) )
65 l->
setIsRepeated( props[QStringLiteral(
"is_repeated" )].toInt() == 1 );
67 if ( props.contains( QStringLiteral(
"head_length" ) ) )
68 l->
setHeadLength( props[QStringLiteral(
"head_length" )].toDouble() );
70 if ( props.contains( QStringLiteral(
"head_length_unit" ) ) )
73 if ( props.contains( QStringLiteral(
"head_length_unit_scale" ) ) )
76 if ( props.contains( QStringLiteral(
"head_thickness" ) ) )
79 if ( props.contains( QStringLiteral(
"head_thickness_unit" ) ) )
82 if ( props.contains( QStringLiteral(
"head_thickness_unit_scale" ) ) )
85 if ( props.contains( QStringLiteral(
"head_type" ) ) )
88 if ( props.contains( QStringLiteral(
"arrow_type" ) ) )
91 if ( props.contains( QStringLiteral(
"offset" ) ) )
92 l->
setOffset( props[QStringLiteral(
"offset" )].toDouble() );
94 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
97 if ( props.contains( QStringLiteral(
"offset_unit_scale" ) ) )
100 if ( props.contains( QStringLiteral(
"ring_filter" ) ) )
121 return QStringLiteral(
"ArrowLine" );
128 map[QStringLiteral(
"arrow_width" )] = QString::number(
arrowWidth() );
132 map[QStringLiteral(
"arrow_start_width" )] = QString::number(
arrowStartWidth() );
136 map[QStringLiteral(
"is_curved" )] = QString::number(
isCurved() ? 1 : 0 );
137 map[QStringLiteral(
"is_repeated" )] = QString::number(
isRepeated() ? 1 : 0 );
139 map[QStringLiteral(
"head_length" )] = QString::number(
headLength() );
143 map[QStringLiteral(
"head_thickness" )] = QString::number(
headThickness() );
147 map[QStringLiteral(
"head_type" )] = QString::number(
headType() );
148 map[QStringLiteral(
"arrow_type" )] = QString::number(
arrowType() );
150 map[QStringLiteral(
"offset" )] = QString::number(
offset() );
154 map[QStringLiteral(
"ring_filter" )] = QString::number(
static_cast< int >(
mRingFilter ) );
163 attributes.unite( mSymbol->usedAttributes( context ) );
172 if ( mSymbol && mSymbol->hasDataDefinedProperties() )
198 return std::sqrt( ( po.x() - pd.x() ) * ( po.x() - pd.x() ) + ( po.y() - pd.y() ) * ( po.y() - pd.y() ) );
202 qreal startWidth, qreal width,
203 qreal headWidth, qreal headHeight,
214 po = pd - ( pd - po ) / length * headWidth;
219 pd = po + ( pd - po ) / length * headWidth;
224 QPointF v = ( pd - po ) / length * headWidth;
225 QPointF npo = ( po + pd ) / 2.0 - v;
226 QPointF npd = ( po + pd ) / 2.0 + v;
229 length = 2 * headWidth;
232 qreal bodyLength = length - headWidth;
235 QPointF unitVec = ( pd - po ) / length;
237 QPointF perpVec( -unitVec.y(), unitVec.x() );
240 po += perpVec * offset;
241 pd += perpVec * offset;
249 polygon << po + unitVec *headWidth + perpVec *headHeight;
250 polygon << po + unitVec *headWidth + perpVec * ( width * 0.5 );
252 polygon << po + unitVec *bodyLength + perpVec * ( width * 0.5 );
255 polygon << po + unitVec *bodyLength + perpVec *headHeight;
261 polygon << po + unitVec *bodyLength - perpVec *headHeight;
262 polygon << po + unitVec *bodyLength - perpVec * ( width * 0.5 );
265 polygon << po + unitVec *headWidth - perpVec * ( width * 0.5 );
266 polygon << po + unitVec *headWidth - perpVec *headHeight;
273 polygon << po + perpVec * ( startWidth * 0.5 );
274 polygon << po + unitVec *bodyLength + perpVec * ( width * 0.5 );
275 polygon << po + unitVec *bodyLength + perpVec *headHeight;
284 polygon << po + unitVec *bodyLength - perpVec *headHeight;
285 polygon << po + unitVec *bodyLength - perpVec * ( width * 0.5 );
286 polygon << po - perpVec * ( startWidth * 0.5 );
298 polygon << po + unitVec *headWidth + perpVec *headHeight;
299 polygon << po + unitVec *headWidth + perpVec * ( width * 0.5 );
301 polygon << pd + perpVec * ( startWidth * 0.5 );
309 polygon << pd - perpVec * ( startWidth * 0.5 );
311 polygon << po + unitVec *headWidth - perpVec * ( width * 0.5 );
312 polygon << po + unitVec *headWidth - perpVec *headHeight;
320 polygon << polygon.first();
339 bool pointsToCircle( QPointF a, QPointF b, QPointF
c, QPointF ¢er, qreal &radius )
348 QPointF ab2 = ( a + b ) / 2.0;
349 QPointF bc2 = ( b +
c ) / 2.0;
352 if ( std::fabs( ab.x() * bc.y() - ab.y() * bc.x() ) < 0.001 )
359 cy = bc2.y() - ( cx - bc2.x() ) * bc.x() / bc.y();
362 else if ( bc.y() == 0 )
365 cy = ab2.y() - ( cx - ab2.x() ) * ab.x() / ab.y();
370 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() );
371 cy = bc2.y() - ( cx - bc2.x() ) * bc.x() / bc.y();
374 radius = std::sqrt( ( a.x() - cx ) * ( a.x() - cx ) + ( a.y() - cy ) * ( a.y() - cy ) );
384 return QPointF( std::cos( -
angle ) * radius + center.x(), std::sin( -
angle ) * radius + center.y() );
387 void pathArcTo( QPainterPath &path, QPointF circleCenter, qreal circleRadius, qreal angle_o, qreal angle_d,
int direction )
389 QRectF circleRect( circleCenter - QPointF( circleRadius, circleRadius ), circleCenter + QPointF( circleRadius, circleRadius ) );
390 if ( direction == 1 )
392 if ( angle_o < angle_d )
393 path.arcTo( circleRect, angle_o / M_PI * 180.0, ( angle_d - angle_o ) / M_PI * 180.0 );
395 path.arcTo( circleRect, angle_o / M_PI * 180.0, 360.0 - ( angle_o - angle_d ) / M_PI * 180.0 );
399 if ( angle_o < angle_d )
400 path.arcTo( circleRect, angle_o / M_PI * 180.0, - ( 360.0 - ( angle_d - angle_o ) / M_PI * 180.0 ) );
402 path.arcTo( circleRect, angle_o / M_PI * 180.0, ( angle_d - angle_o ) / M_PI * 180.0 );
407 void spiralArcTo( QPainterPath &path, QPointF center, qreal startAngle, qreal startRadius, qreal endAngle, qreal endRadius,
int direction )
410 QPointF A =
circlePoint( center, startRadius, startAngle );
412 QPointF B =
circlePoint( center, endRadius, endAngle );
416 deltaAngle = endAngle - startAngle;
417 if ( direction * deltaAngle < 0.0 )
418 deltaAngle = deltaAngle + direction * 2 * M_PI;
420 QPointF I1 =
circlePoint( center, 0.75 * startRadius + 0.25 * endRadius, startAngle + 0.25 * deltaAngle );
421 QPointF I2 =
circlePoint( center, 0.50 * startRadius + 0.50 * endRadius, startAngle + 0.50 * deltaAngle );
422 QPointF I3 =
circlePoint( center, 0.25 * startRadius + 0.75 * endRadius, startAngle + 0.75 * deltaAngle );
435 qreal a1 = std::atan2( cCenter.y() - A.y(), A.x() - cCenter.x() );
436 qreal a2 = std::atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() );
437 pathArcTo( path, cCenter, cRadius, a1, a2, direction );
449 qreal a1 = std::atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() );
450 qreal a2 = std::atan2( cCenter.y() - B.y(), B.x() - cCenter.x() );
451 pathArcTo( path, cCenter, cRadius, a1, a2, direction );
456 qreal startWidth, qreal width,
457 qreal headWidth, qreal headHeight,
462 QPointF circleCenter;
466 return straightArrow( po, pd, startWidth, width, headWidth, headHeight, headType, arrowType, offset );
470 qreal angle_o =
clampAngle( std::atan2( circleCenter.y() - po.y(), po.x() - circleCenter.x() ) );
471 qreal angle_m =
clampAngle( std::atan2( circleCenter.y() - pm.y(), pm.x() - circleCenter.x() ) );
472 qreal angle_d =
clampAngle( std::atan2( circleCenter.y() - pd.y(), pd.x() - circleCenter.x() ) );
484 qreal deltaAngle = angle_d - angle_o;
485 if ( direction * deltaAngle < 0.0 )
486 deltaAngle = deltaAngle + direction * 2 * M_PI;
494 return straightArrow( po, pd, startWidth, width, headWidth, headHeight, headType, arrowType, offset );
498 circleRadius += offset;
499 po =
circlePoint( circleCenter, circleRadius, angle_o );
500 pm =
circlePoint( circleCenter, circleRadius, angle_m );
501 pd =
circlePoint( circleCenter, circleRadius, angle_d );
503 qreal headAngle = direction * std::atan( headWidth / circleRadius );
513 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_o + headAngle ) );
515 pathArcTo( path, circleCenter, circleRadius + direction * width / 2, angle_o + headAngle, angle_d - headAngle, direction );
518 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_d - headAngle ) );
523 pathArcTo( path, circleCenter, circleRadius, angle_o, angle_d, direction );
527 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_d - headAngle ) );
529 pathArcTo( path, circleCenter, circleRadius - direction * width / 2, angle_d - headAngle, angle_o + headAngle, -direction );
532 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_o + headAngle ) );
537 pathArcTo( path, circleCenter, circleRadius, angle_d, angle_o, -direction );
544 path.moveTo(
circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
546 spiralArcTo( path, circleCenter, angle_o, circleRadius + direction * startWidth / 2, angle_d - headAngle, circleRadius + direction * width / 2, direction );
549 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_d - headAngle ) );
555 pathArcTo( path, circleCenter, circleRadius, angle_o, angle_d, direction );
559 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_d - headAngle ) );
561 spiralArcTo( path, circleCenter, angle_d - headAngle, circleRadius - direction * width / 2, angle_o, circleRadius - direction * startWidth / 2, -direction );
563 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
567 pathArcTo( path, circleCenter, circleRadius, angle_d, angle_o, -direction );
568 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
576 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_o + headAngle ) );
577 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * width / 2, angle_o + headAngle ) );
579 spiralArcTo( path, circleCenter, angle_o + headAngle, circleRadius + direction * width / 2, angle_d, circleRadius + direction * startWidth / 2, direction );
583 pathArcTo( path, circleCenter, circleRadius, angle_o, angle_d, direction );
587 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * startWidth / 2, angle_d ) );
589 spiralArcTo( path, circleCenter, angle_d, circleRadius - direction * startWidth / 2, angle_o + headAngle, circleRadius - direction * width / 2, - direction );
591 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_o + headAngle ) );
597 pathArcTo( path, circleCenter, circleRadius, angle_d, angle_o, -direction );
601 return path.toSubpathPolygons().at( 0 );
614 double w = exprVal.toDouble( &ok );
624 double w = exprVal.toDouble( &ok );
634 double w = exprVal.toDouble( &ok );
644 double w = exprVal.toDouble( &ok );
654 double w = exprVal.toDouble( &ok );
668 mComputedHeadType = h;
679 mComputedArrowType = h;
698 _resolveDataDefined( context );
702 if ( points.size() >= 3 )
705 QPointF po( points.at( 0 ) );
707 QPointF pm( points.at( points.size() / 2 ) );
709 QPointF pd( points.back() );
711 QPolygonF poly =
curvedArrow( po, pm, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
715 else if ( points.size() == 2 )
718 QPointF po( points.at( 0 ) );
720 QPointF pd( points.at( 1 ) );
722 QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
728 for (
int pIdx = 0; pIdx < points.size() - 1; pIdx += 2 )
734 _resolveDataDefined( context );
736 if ( points.size() - pIdx >= 3 )
739 QPointF po( points.at( pIdx ) );
741 QPointF pm( points.at( pIdx + 1 ) );
743 QPointF pd( points.at( pIdx + 2 ) );
745 QPolygonF poly =
curvedArrow( po, pm, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
749 else if ( points.size() - pIdx == 2 )
752 QPointF po( points.at( pIdx ) );
754 QPointF pd( points.at( pIdx + 1 ) );
756 QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
766 _resolveDataDefined( context );
768 if ( !points.isEmpty() )
771 QPointF po( points.at( 0 ) );
773 QPointF pd( points.back() );
775 QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
782 for (
int pIdx = 0; pIdx < points.size() - 1; pIdx++ )
788 _resolveDataDefined( context );
791 QPointF po( points.at( pIdx ) );
793 QPointF pd( points.at( pIdx + 1 ) );
795 QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
806 mSymbol->setColor(
c );
813 return mSymbol.get() ? mSymbol->color() :
mColor;