1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickpath_p.h"
5#include "qquickpath_p_p.h"
6#include "qquicksvgparser_p.h"
7
8#include <QSet>
9#include <QTime>
10
11#include <private/qbezier_p.h>
12#include <QtCore/qmath.h>
13#include <QtCore/private/qnumeric_p.h>
14
15QT_BEGIN_NAMESPACE
16
17/*!
18 \qmltype PathElement
19 \instantiates QQuickPathElement
20 \inqmlmodule QtQuick
21 \ingroup qtquick-animation-paths
22 \brief PathElement is the base path type.
23
24 This type is the base for all path types. It cannot
25 be instantiated.
26
27 \sa Path, PathAttribute, PathPercent, PathLine, PathPolyline, PathQuad, PathCubic, PathArc,
28 PathAngleArc, PathCurve, PathSvg
29*/
30
31/*!
32 \qmltype Path
33 \instantiates QQuickPath
34 \inqmlmodule QtQuick
35 \ingroup qtquick-animation-paths
36 \brief Defines a path for use by \l PathView and \l Shape.
37
38 A Path is composed of one or more path segments - PathLine, PathPolyline, PathQuad,
39 PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg.
40
41 The spacing of the items along the Path can be adjusted via a
42 PathPercent object.
43
44 PathAttribute allows named attributes with values to be defined
45 along the path.
46
47 Path and the other types for specifying path elements are shared between
48 \l PathView and \l Shape. The following table provides an overview of the
49 applicability of the various path elements:
50
51 \table
52 \header
53 \li Element
54 \li PathView
55 \li Shape
56 \li Shape, software
57 \row
58 \li PathMove
59 \li N/A
60 \li Yes
61 \li Yes
62 \row
63 \li PathLine
64 \li Yes
65 \li Yes
66 \li Yes
67 \row
68 \li PathPolyline
69 \li Yes
70 \li Yes
71 \li Yes
72 \row
73 \li PathMultiLine
74 \li Yes
75 \li Yes
76 \li Yes
77 \row
78 \li PathQuad
79 \li Yes
80 \li Yes
81 \li Yes
82 \row
83 \li PathCubic
84 \li Yes
85 \li Yes
86 \li Yes
87 \row
88 \li PathArc
89 \li Yes
90 \li Yes
91 \li Yes
92 \row
93 \li PathAngleArc
94 \li Yes
95 \li Yes
96 \li Yes
97 \row
98 \li PathSvg
99 \li Yes
100 \li Yes
101 \li Yes
102 \row
103 \li PathAttribute
104 \li Yes
105 \li N/A
106 \li N/A
107 \row
108 \li PathPercent
109 \li Yes
110 \li N/A
111 \li N/A
112 \row
113 \li PathCurve
114 \li Yes
115 \li No
116 \li No
117 \endtable
118
119 \note Path is a non-visual type; it does not display anything on its own.
120 To draw a path, use \l Shape.
121
122 \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathPolyline, PathMove, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg
123*/
124QQuickPath::QQuickPath(QObject *parent)
125 : QObject(*(new QQuickPathPrivate), parent)
126{
127}
128
129QQuickPath::QQuickPath(QQuickPathPrivate &dd, QObject *parent)
130 : QObject(dd, parent)
131{
132}
133
134QQuickPath::~QQuickPath()
135{
136}
137
138/*!
139 \qmlproperty real QtQuick::Path::startX
140 \qmlproperty real QtQuick::Path::startY
141 These properties hold the starting position of the path.
142*/
143qreal QQuickPath::startX() const
144{
145 Q_D(const QQuickPath);
146 return d->startX.isNull ? 0 : d->startX.value;
147}
148
149void QQuickPath::setStartX(qreal x)
150{
151 Q_D(QQuickPath);
152 if (d->startX.isValid() && qFuzzyCompare(p1: x, p2: d->startX))
153 return;
154 d->startX = x;
155 emit startXChanged();
156 processPath();
157}
158
159bool QQuickPath::hasStartX() const
160{
161 Q_D(const QQuickPath);
162 return d->startX.isValid();
163}
164
165qreal QQuickPath::startY() const
166{
167 Q_D(const QQuickPath);
168 return d->startY.isNull ? 0 : d->startY.value;
169}
170
171void QQuickPath::setStartY(qreal y)
172{
173 Q_D(QQuickPath);
174 if (d->startY.isValid() && qFuzzyCompare(p1: y, p2: d->startY))
175 return;
176 d->startY = y;
177 emit startYChanged();
178 processPath();
179}
180
181bool QQuickPath::hasStartY() const
182{
183 Q_D(const QQuickPath);
184 return d->startY.isValid();
185}
186
187/*!
188 \qmlproperty bool QtQuick::Path::closed
189 This property holds whether the start and end of the path are identical.
190*/
191bool QQuickPath::isClosed() const
192{
193 Q_D(const QQuickPath);
194 return d->closed;
195}
196
197/*!
198 \qmlproperty list<PathElement> QtQuick::Path::pathElements
199 This property holds the objects composing the path.
200
201 \qmldefault
202
203 A path can contain the following path objects:
204 \list
205 \li \l PathLine - a straight line to a given position.
206 \li \l PathPolyline - a polyline specified as a list of coordinates.
207 \li \l PathMultiline - a list of polylines specified as a list of lists of coordinates.
208 \li \l PathQuad - a quadratic Bezier curve to a given position with a control point.
209 \li \l PathCubic - a cubic Bezier curve to a given position with two control points.
210 \li \l PathArc - an arc to a given position with a radius.
211 \li \l PathAngleArc - an arc specified by center point, radii, and angles.
212 \li \l PathSvg - a path specified as an SVG path data string.
213 \li \l PathCurve - a point on a Catmull-Rom curve.
214 \li \l PathAttribute - an attribute at a given position in the path.
215 \li \l PathPercent - a way to spread out items along various segments of the path.
216 \endlist
217
218 \snippet qml/pathview/pathattributes.qml 2
219*/
220
221QQmlListProperty<QQuickPathElement> QQuickPath::pathElements()
222{
223 return QQmlListProperty<QQuickPathElement>(this,
224 nullptr,
225 pathElements_append,
226 pathElements_count,
227 pathElements_at,
228 pathElements_clear);
229}
230
231static QQuickPathPrivate *privatePath(QObject *object)
232{
233 QQuickPath *path = static_cast<QQuickPath*>(object);
234
235 return QQuickPathPrivate::get(path);
236}
237
238QQuickPathElement *QQuickPath::pathElements_at(QQmlListProperty<QQuickPathElement> *property, qsizetype index)
239{
240 QQuickPathPrivate *d = privatePath(object: property->object);
241
242 return d->_pathElements.at(i: index);
243}
244
245void QQuickPath::pathElements_append(QQmlListProperty<QQuickPathElement> *property, QQuickPathElement *pathElement)
246{
247 QQuickPathPrivate *d = privatePath(object: property->object);
248 QQuickPath *path = static_cast<QQuickPath*>(property->object);
249
250 d->_pathElements.append(t: pathElement);
251
252 if (d->componentComplete) {
253 QQuickCurve *curve = qobject_cast<QQuickCurve *>(object: pathElement);
254 if (curve)
255 d->_pathCurves.append(t: curve);
256 else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(object: pathElement))
257 d->_pathTexts.append(t: text);
258 else {
259 QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(object: pathElement);
260 if (attribute && !d->_attributes.contains(str: attribute->name()))
261 d->_attributes.append(t: attribute->name());
262 }
263
264 path->processPath();
265
266 connect(sender: pathElement, SIGNAL(changed()), receiver: path, SLOT(processPath()));
267 }
268}
269
270qsizetype QQuickPath::pathElements_count(QQmlListProperty<QQuickPathElement> *property)
271{
272 QQuickPathPrivate *d = privatePath(object: property->object);
273
274 return d->_pathElements.size();
275}
276
277void QQuickPath::pathElements_clear(QQmlListProperty<QQuickPathElement> *property)
278{
279 QQuickPathPrivate *d = privatePath(object: property->object);
280 QQuickPath *path = static_cast<QQuickPath*>(property->object);
281
282 path->disconnectPathElements();
283 d->_pathElements.clear();
284 d->_pathCurves.clear();
285 d->_pointCache.clear();
286 d->_pathTexts.clear();
287}
288
289void QQuickPath::interpolate(int idx, const QString &name, qreal value)
290{
291 Q_D(QQuickPath);
292 interpolate(points&: d->_attributePoints, idx, name, value);
293}
294
295void QQuickPath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value)
296{
297 if (!idx)
298 return;
299
300 qreal lastValue = 0;
301 qreal lastPercent = 0;
302 int search = idx - 1;
303 while(search >= 0) {
304 const AttributePoint &point = attributePoints.at(i: search);
305 if (point.values.contains(key: name)) {
306 lastValue = point.values.value(key: name);
307 lastPercent = point.origpercent;
308 break;
309 }
310 --search;
311 }
312
313 ++search;
314
315 const AttributePoint &curPoint = attributePoints.at(i: idx);
316
317 for (int ii = search; ii < idx; ++ii) {
318 AttributePoint &point = attributePoints[ii];
319
320 qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
321 point.values.insert(key: name, value: val);
322 }
323}
324
325void QQuickPath::endpoint(const QString &name)
326{
327 Q_D(QQuickPath);
328 const AttributePoint &first = d->_attributePoints.first();
329 qreal val = first.values.value(key: name);
330 for (int ii = d->_attributePoints.size() - 1; ii >= 0; ii--) {
331 const AttributePoint &point = d->_attributePoints.at(i: ii);
332 if (point.values.contains(key: name)) {
333 for (int jj = ii + 1; jj < d->_attributePoints.size(); ++jj) {
334 AttributePoint &setPoint = d->_attributePoints[jj];
335 setPoint.values.insert(key: name, value: val);
336 }
337 return;
338 }
339 }
340}
341
342void QQuickPath::endpoint(QList<AttributePoint> &attributePoints, const QString &name)
343{
344 const AttributePoint &first = attributePoints.first();
345 qreal val = first.values.value(key: name);
346 for (int ii = attributePoints.size() - 1; ii >= 0; ii--) {
347 const AttributePoint &point = attributePoints.at(i: ii);
348 if (point.values.contains(key: name)) {
349 for (int jj = ii + 1; jj < attributePoints.size(); ++jj) {
350 AttributePoint &setPoint = attributePoints[jj];
351 setPoint.values.insert(key: name, value: val);
352 }
353 return;
354 }
355 }
356}
357
358void QQuickPath::processPath()
359{
360 Q_D(QQuickPath);
361
362 if (!d->componentComplete)
363 return;
364
365 d->_pointCache.clear();
366 d->prevBez.isValid = false;
367
368 if (d->isShapePath) {
369 // This path is a ShapePath, so avoid extra overhead
370 d->_path = createShapePath(startPoint: QPointF(), endPoint: QPointF(), pathLength&: d->pathLength, closed: &d->closed);
371 } else {
372 d->_path = createPath(startPoint: QPointF(), endPoint: QPointF(), attributes: d->_attributes, pathLength&: d->pathLength, attributePoints&: d->_attributePoints, closed: &d->closed);
373 }
374
375 if (d->simplify)
376 d->_path = d->_path.simplified();
377
378 emit changed();
379}
380
381inline static void scalePath(QPainterPath &path, const QSizeF &scale)
382{
383 const qreal xscale = scale.width();
384 const qreal yscale = scale.height();
385 if (xscale == 1 && yscale == 1)
386 return;
387
388 for (int i = 0; i < path.elementCount(); ++i) {
389 const QPainterPath::Element &element = path.elementAt(i);
390 path.setElementPositionAt(i, x: element.x * xscale, y: element.y * yscale);
391 }
392}
393
394QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
395{
396 Q_D(QQuickPath);
397
398 pathLength = 0;
399 attributePoints.clear();
400
401 if (!d->componentComplete)
402 return QPainterPath();
403
404 QPainterPath path;
405
406 AttributePoint first;
407 for (int ii = 0; ii < attributes.size(); ++ii)
408 first.values[attributes.at(i: ii)] = 0;
409 attributePoints << first;
410
411 qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
412 qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
413 path.moveTo(x: startX, y: startY);
414
415 const QString percentString = QStringLiteral("_qfx_percent");
416
417 bool usesPercent = false;
418 int index = 0;
419 for (QQuickPathElement *pathElement : std::as_const(t&: d->_pathElements)) {
420 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(object: pathElement)) {
421 QQuickPathData data;
422 data.index = index;
423 data.endPoint = endPoint;
424 data.curves = d->_pathCurves;
425 curve->addToPath(path, data);
426 AttributePoint p;
427 p.origpercent = path.length();
428 attributePoints << p;
429 ++index;
430 } else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(object: pathElement)) {
431 AttributePoint &point = attributePoints.last();
432 point.values[attribute->name()] = attribute->value();
433 interpolate(attributePoints, idx: attributePoints.size() - 1, name: attribute->name(), value: attribute->value());
434 } else if (QQuickPathPercent *percent = qobject_cast<QQuickPathPercent *>(object: pathElement)) {
435 AttributePoint &point = attributePoints.last();
436 point.values[percentString] = percent->value();
437 interpolate(attributePoints, idx: attributePoints.size() - 1, name: percentString, value: percent->value());
438 usesPercent = true;
439 } else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(object: pathElement)) {
440 text->addToPath(path);
441 }
442 }
443
444 // Fixup end points
445 const AttributePoint &last = attributePoints.constLast();
446 for (int ii = 0; ii < attributes.size(); ++ii) {
447 if (!last.values.contains(key: attributes.at(i: ii)))
448 endpoint(attributePoints, name: attributes.at(i: ii));
449 }
450 if (usesPercent && !last.values.contains(key: percentString)) {
451 d->_attributePoints.last().values[percentString] = 1;
452 interpolate(idx: d->_attributePoints.size() - 1, name: percentString, value: 1);
453 }
454 scalePath(path, scale: d->scale);
455
456 // Adjust percent
457 qreal length = path.length();
458 qreal prevpercent = 0;
459 qreal prevorigpercent = 0;
460 for (int ii = 0; ii < attributePoints.size(); ++ii) {
461 const AttributePoint &point = attributePoints.at(i: ii);
462 if (point.values.contains(key: percentString)) { //special string for QQuickPathPercent
463 if ( ii > 0) {
464 qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
465 (point.values.value(key: percentString)-prevpercent);
466 attributePoints[ii].scale = scale;
467 }
468 attributePoints[ii].origpercent /= length;
469 attributePoints[ii].percent = point.values.value(key: percentString);
470 prevorigpercent = attributePoints.at(i: ii).origpercent;
471 prevpercent = attributePoints.at(i: ii).percent;
472 } else {
473 attributePoints[ii].origpercent /= length;
474 attributePoints[ii].percent = attributePoints.at(i: ii).origpercent;
475 }
476 }
477
478 if (closed) {
479 QPointF end = path.currentPosition();
480 *closed = length > 0 && startX * d->scale.width() == end.x() && startY * d->scale.height() == end.y();
481 }
482 pathLength = length;
483
484 return path;
485}
486
487QPainterPath QQuickPath::createShapePath(const QPointF &startPoint, const QPointF &endPoint, qreal &pathLength, bool *closed)
488{
489 Q_D(QQuickPath);
490
491 if (!d->componentComplete)
492 return QPainterPath();
493
494 QPainterPath path;
495
496 qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
497 qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
498 path.moveTo(x: startX, y: startY);
499
500 int index = 0;
501 for (QQuickCurve *curve : std::as_const(t&: d->_pathCurves)) {
502 QQuickPathData data;
503 data.index = index;
504 data.endPoint = endPoint;
505 data.curves = d->_pathCurves;
506 curve->addToPath(path, data);
507 ++index;
508 }
509
510 for (QQuickPathText *text : std::as_const(t&: d->_pathTexts))
511 text->addToPath(path);
512
513 if (closed) {
514 QPointF end = path.currentPosition();
515 *closed = startX == end.x() && startY == end.y();
516 }
517 scalePath(path, scale: d->scale);
518
519 // Note: Length of paths inside ShapePath is not used, so currently
520 // length is always 0. This avoids potentially heavy path.length()
521 //pathLength = path.length();
522 pathLength = 0;
523
524 return path;
525}
526
527void QQuickPath::classBegin()
528{
529 Q_D(QQuickPath);
530 d->componentComplete = false;
531}
532
533void QQuickPath::disconnectPathElements()
534{
535 Q_D(const QQuickPath);
536
537 for (QQuickPathElement *pathElement : d->_pathElements)
538 disconnect(sender: pathElement, SIGNAL(changed()), receiver: this, SLOT(processPath()));
539}
540
541void QQuickPath::connectPathElements()
542{
543 Q_D(const QQuickPath);
544
545 for (QQuickPathElement *pathElement : d->_pathElements)
546 connect(sender: pathElement, SIGNAL(changed()), receiver: this, SLOT(processPath()));
547}
548
549void QQuickPath::gatherAttributes()
550{
551 Q_D(QQuickPath);
552
553 QSet<QString> attributes;
554
555 // First gather up all the attributes
556 for (QQuickPathElement *pathElement : std::as_const(t&: d->_pathElements)) {
557 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(object: pathElement))
558 d->_pathCurves.append(t: curve);
559 else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(object: pathElement))
560 d->_pathTexts.append(t: text);
561 else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(object: pathElement))
562 attributes.insert(value: attribute->name());
563 }
564
565 d->_attributes = attributes.values();
566}
567
568void QQuickPath::componentComplete()
569{
570 Q_D(QQuickPath);
571 d->componentComplete = true;
572
573 gatherAttributes();
574
575 processPath();
576
577 connectPathElements();
578}
579
580QPainterPath QQuickPath::path() const
581{
582 Q_D(const QQuickPath);
583 return d->_path;
584}
585
586QStringList QQuickPath::attributes() const
587{
588 Q_D(const QQuickPath);
589 if (!d->componentComplete) {
590 QSet<QString> attrs;
591
592 // First gather up all the attributes
593 for (QQuickPathElement *pathElement : d->_pathElements) {
594 if (QQuickPathAttribute *attribute =
595 qobject_cast<QQuickPathAttribute *>(object: pathElement))
596 attrs.insert(value: attribute->name());
597 }
598 return attrs.values();
599 }
600 return d->_attributes;
601}
602
603static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false)
604{
605 const int lastElement = reverse ? 0 : path.elementCount() - 1;
606 const int start = reverse ? *current - 1 : *current + 1;
607 for (int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
608 const QPainterPath::Element &e = path.elementAt(i);
609
610 switch (e.type) {
611 case QPainterPath::MoveToElement:
612 break;
613 case QPainterPath::LineToElement:
614 {
615 QLineF line(path.elementAt(i: i-1), e);
616 *bezLength = line.length();
617 QPointF a = path.elementAt(i: i-1);
618 QPointF delta = e - a;
619 *current = i;
620 return QBezier::fromPoints(p1: a, p2: a + delta / 3, p3: a + 2 * delta / 3, p4: e);
621 }
622 case QPainterPath::CurveToElement:
623 {
624 QBezier b = QBezier::fromPoints(p1: path.elementAt(i: i-1),
625 p2: e,
626 p3: path.elementAt(i: i+1),
627 p4: path.elementAt(i: i+2));
628 *bezLength = b.length();
629 *current = i;
630 return b;
631 }
632 default:
633 break;
634 }
635 }
636 *current = lastElement;
637 *bezLength = 0;
638 return QBezier();
639}
640
641static inline int segmentCount(const QPainterPath &path, qreal pathLength)
642{
643 // In the really simple case of a single straight line we can interpolate without jitter
644 // between just two points.
645 if (path.elementCount() == 2
646 && path.elementAt(i: 0).type == QPainterPath::MoveToElement
647 && path.elementAt(i: 1).type == QPainterPath::LineToElement) {
648 return 1;
649 }
650 // more points means less jitter between items as they move along the
651 // path, but takes longer to generate
652 return qCeil(v: pathLength*5);
653}
654
655//derivative of the equation
656static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
657{
658 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
659}
660
661void QQuickPath::createPointCache() const
662{
663 Q_D(const QQuickPath);
664 qreal pathLength = d->pathLength;
665 if (pathLength <= 0 || qt_is_nan(d: pathLength))
666 return;
667
668 const int segments = segmentCount(path: d->_path, pathLength);
669 const int lastElement = d->_path.elementCount() - 1;
670 d->_pointCache.resize(size: segments+1);
671
672 int currElement = -1;
673 qreal bezLength = 0;
674 QBezier currBez = nextBezier(path: d->_path, current: &currElement, bezLength: &bezLength);
675 qreal currLength = bezLength;
676 qreal epc = currLength / pathLength;
677
678 for (int i = 0; i < d->_pointCache.size(); i++) {
679 //find which set we are in
680 qreal prevPercent = 0;
681 qreal prevOrigPercent = 0;
682 for (int ii = 0; ii < d->_attributePoints.size(); ++ii) {
683 qreal percent = qreal(i)/segments;
684 const AttributePoint &point = d->_attributePoints.at(i: ii);
685 if (percent < point.percent || ii == d->_attributePoints.size() - 1) { //### || is special case for very last item
686 qreal elementPercent = (percent - prevPercent);
687
688 qreal spc = prevOrigPercent + elementPercent * point.scale;
689
690 while (spc > epc) {
691 if (currElement > lastElement)
692 break;
693 currBez = nextBezier(path: d->_path, current: &currElement, bezLength: &bezLength);
694 if (bezLength == 0.0) {
695 currLength = pathLength;
696 epc = 1.0;
697 break;
698 }
699 currLength += bezLength;
700 epc = currLength / pathLength;
701 }
702 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
703 d->_pointCache[i] = currBez.pointAt(t: qBound(min: qreal(0), val: realT, max: qreal(1)));
704 break;
705 }
706 prevOrigPercent = point.origpercent;
707 prevPercent = point.percent;
708 }
709 }
710}
711
712void QQuickPath::invalidateSequentialHistory() const
713{
714 Q_D(const QQuickPath);
715 d->prevBez.isValid = false;
716}
717
718/*! \qmlproperty bool QtQuick::Path::simplify
719 \since 6.6
720
721 When set to true, the path will be simplified. This implies merging all subpaths that intersect,
722 creating a path where there are no self-intersections. Consecutive parallel lines will also be
723 merged. The simplified path is intended to be used with ShapePath.OddEvenFill. Bezier curves may
724 be flattened to line segments due to numerical instability of doing bezier curve intersections.
725*/
726void QQuickPath::setSimplify(bool s)
727{
728 Q_D(QQuickPath);
729 if (d->simplify == s)
730 return;
731
732 d->simplify = s;
733 processPath();
734
735 emit simplifyChanged();
736}
737
738bool QQuickPath::simplify() const
739{
740 Q_D(const QQuickPath);
741 return d->simplify;
742}
743
744/*!
745 \qmlproperty size QtQuick::Path::scale
746
747 This property holds the scale factor for the path.
748 The width and height of \a scale can be different, to
749 achieve anisotropic scaling.
750
751 \note Setting this property will not affect the border width.
752
753 \since QtQuick 2.14
754*/
755QSizeF QQuickPath::scale() const
756{
757 Q_D(const QQuickPath);
758 return d->scale;
759}
760
761void QQuickPath::setScale(const QSizeF &scale)
762{
763 Q_D(QQuickPath);
764 if (scale == d->scale)
765 return;
766 d->scale = scale;
767 emit scaleChanged();
768 processPath();
769}
770
771QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
772{
773 Q_D(const QQuickPath);
774 return sequentialPointAt(path: d->_path, pathLength: d->pathLength, attributePoints: d->_attributePoints, prevBez&: d->prevBez, p, angle);
775}
776
777QPointF QQuickPath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
778{
779 Q_ASSERT(p >= 0.0 && p <= 1.0);
780
781 if (!prevBez.isValid)
782 return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
783 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
784
785 return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
786 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
787}
788
789QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
790{
791 if (pathLength <= 0 || qt_is_nan(d: pathLength))
792 return path.pointAtPercent(t: 0); //expensive?
793
794 const int lastElement = path.elementCount() - 1;
795 bool haveCachedBez = prevBez.isValid;
796 int currElement = haveCachedBez ? prevBez.element : -1;
797 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
798 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, current: &currElement, bezLength: &bezLength);
799 qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
800 qreal epc = currLength / pathLength;
801
802 //find which set we are in
803 qreal prevPercent = 0;
804 qreal prevOrigPercent = 0;
805 for (int ii = 0; ii < attributePoints.size(); ++ii) {
806 qreal percent = p;
807 const AttributePoint &point = attributePoints.at(i: ii);
808 if (percent < point.percent || ii == attributePoints.size() - 1) {
809 qreal elementPercent = (percent - prevPercent);
810
811 qreal spc = prevOrigPercent + elementPercent * point.scale;
812
813 while (spc > epc) {
814 Q_ASSERT(!(currElement > lastElement));
815 Q_UNUSED(lastElement);
816 currBez = nextBezier(path, current: &currElement, bezLength: &bezLength);
817 currLength += bezLength;
818 epc = currLength / pathLength;
819 }
820 prevBez.element = currElement;
821 prevBez.bezLength = bezLength;
822 prevBez.currLength = currLength;
823 prevBez.bezier = currBez;
824 prevBez.p = p;
825 prevBez.isValid = true;
826
827 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
828
829 if (angle) {
830 qreal m1 = slopeAt(t: realT, a: currBez.x1, b: currBez.x2, c: currBez.x3, d: currBez.x4);
831 qreal m2 = slopeAt(t: realT, a: currBez.y1, b: currBez.y2, c: currBez.y3, d: currBez.y4);
832 *angle = QLineF(0, 0, m1, m2).angle();
833 }
834
835 return currBez.pointAt(t: qBound(min: qreal(0), val: realT, max: qreal(1)));
836 }
837 prevOrigPercent = point.origpercent;
838 prevPercent = point.percent;
839 }
840
841 return QPointF(0,0);
842}
843
844//ideally this should be merged with forwardsPointAt
845QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
846{
847 if (pathLength <= 0 || qt_is_nan(d: pathLength))
848 return path.pointAtPercent(t: 0);
849
850 const int firstElement = 1; //element 0 is always a MoveTo, which we ignore
851 bool haveCachedBez = prevBez.isValid;
852 int currElement = haveCachedBez ? prevBez.element : path.elementCount();
853 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
854 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, current: &currElement, bezLength: &bezLength, reverse: true /*reverse*/);
855 qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
856 qreal prevLength = currLength - bezLength;
857 qreal epc = prevLength / pathLength;
858
859 for (int ii = attributePoints.size() - 1; ii > 0; --ii) {
860 qreal percent = p;
861 const AttributePoint &point = attributePoints.at(i: ii);
862 const AttributePoint &prevPoint = attributePoints.at(i: ii-1);
863 if (percent > prevPoint.percent || ii == 1) {
864 qreal elementPercent = (percent - prevPoint.percent);
865
866 qreal spc = prevPoint.origpercent + elementPercent * point.scale;
867
868 while (spc < epc) {
869 Q_ASSERT(!(currElement < firstElement));
870 Q_UNUSED(firstElement);
871 currBez = nextBezier(path, current: &currElement, bezLength: &bezLength, reverse: true /*reverse*/);
872 //special case for first element is to avoid floating point math
873 //causing an epc that never hits 0.
874 currLength = (currElement == firstElement) ? bezLength : prevLength;
875 prevLength = currLength - bezLength;
876 epc = prevLength / pathLength;
877 }
878 prevBez.element = currElement;
879 prevBez.bezLength = bezLength;
880 prevBez.currLength = currLength;
881 prevBez.bezier = currBez;
882 prevBez.p = p;
883 prevBez.isValid = true;
884
885 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
886
887 if (angle) {
888 qreal m1 = slopeAt(t: realT, a: currBez.x1, b: currBez.x2, c: currBez.x3, d: currBez.x4);
889 qreal m2 = slopeAt(t: realT, a: currBez.y1, b: currBez.y2, c: currBez.y3, d: currBez.y4);
890 *angle = QLineF(0, 0, m1, m2).angle();
891 }
892
893 return currBez.pointAt(t: qBound(min: qreal(0), val: realT, max: qreal(1)));
894 }
895 }
896
897 return QPointF(0,0);
898}
899
900/*!
901 \qmlmethod point Path::pointAtPercent(real t)
902
903 Returns the point at the percentage \a t of the current path.
904 The argument \a t has to be between 0 and 1.
905
906 \note Similarly to other percent methods in \l QPainterPath,
907 the percentage measurement is not linear with regards to the length,
908 if curves are present in the path.
909 When curves are present, the percentage argument is mapped to the \c t
910 parameter of the Bezier equations.
911
912 \sa QPainterPath::pointAtPercent()
913
914 \since QtQuick 2.14
915*/
916QPointF QQuickPath::pointAtPercent(qreal t) const
917{
918 Q_D(const QQuickPath);
919 if (d->isShapePath) // this since ShapePath does not calculate the length at all,
920 return d->_path.pointAtPercent(t); // in order to be faster.
921
922 if (d->_pointCache.isEmpty()) {
923 createPointCache();
924 if (d->_pointCache.isEmpty())
925 return QPointF();
926 }
927
928 const int segmentCount = d->_pointCache.size() - 1;
929 qreal idxf = t*segmentCount;
930 int idx1 = qFloor(v: idxf);
931 qreal delta = idxf - idx1;
932 if (idx1 > segmentCount)
933 idx1 = segmentCount;
934 else if (idx1 < 0)
935 idx1 = 0;
936
937 if (delta == 0.0)
938 return d->_pointCache.at(i: idx1);
939
940 // interpolate between the two points.
941 int idx2 = qCeil(v: idxf);
942 if (idx2 > segmentCount)
943 idx2 = segmentCount;
944 else if (idx2 < 0)
945 idx2 = 0;
946
947 QPointF p1 = d->_pointCache.at(i: idx1);
948 QPointF p2 = d->_pointCache.at(i: idx2);
949 QPointF pos = p1 * (1.0-delta) + p2 * delta;
950
951 return pos;
952}
953
954qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
955{
956 Q_D(const QQuickPath);
957 if (percent < 0 || percent > 1)
958 return 0;
959
960 for (int ii = 0; ii < d->_attributePoints.size(); ++ii) {
961 const AttributePoint &point = d->_attributePoints.at(i: ii);
962
963 if (point.percent == percent) {
964 return point.values.value(key: name);
965 } else if (point.percent > percent) {
966 qreal lastValue =
967 ii?(d->_attributePoints.at(i: ii - 1).values.value(key: name)):0;
968 qreal lastPercent =
969 ii?(d->_attributePoints.at(i: ii - 1).percent):0;
970 qreal curValue = point.values.value(key: name);
971 qreal curPercent = point.percent;
972
973 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
974 }
975 }
976
977 return 0;
978}
979
980/****************************************************************************/
981
982qreal QQuickCurve::x() const
983{
984 return _x.isNull ? 0 : _x.value;
985}
986
987void QQuickCurve::setX(qreal x)
988{
989 if (_x.isNull || _x != x) {
990 _x = x;
991 emit xChanged();
992 emit changed();
993 }
994}
995
996bool QQuickCurve::hasX()
997{
998 return _x.isValid();
999}
1000
1001qreal QQuickCurve::y() const
1002{
1003 return _y.isNull ? 0 : _y.value;
1004}
1005
1006void QQuickCurve::setY(qreal y)
1007{
1008 if (_y.isNull || _y != y) {
1009 _y = y;
1010 emit yChanged();
1011 emit changed();
1012 }
1013}
1014
1015bool QQuickCurve::hasY()
1016{
1017 return _y.isValid();
1018}
1019
1020qreal QQuickCurve::relativeX() const
1021{
1022 return _relativeX;
1023}
1024
1025void QQuickCurve::setRelativeX(qreal x)
1026{
1027 if (_relativeX.isNull || _relativeX != x) {
1028 _relativeX = x;
1029 emit relativeXChanged();
1030 emit changed();
1031 }
1032}
1033
1034bool QQuickCurve::hasRelativeX()
1035{
1036 return _relativeX.isValid();
1037}
1038
1039qreal QQuickCurve::relativeY() const
1040{
1041 return _relativeY;
1042}
1043
1044void QQuickCurve::setRelativeY(qreal y)
1045{
1046 if (_relativeY.isNull || _relativeY != y) {
1047 _relativeY = y;
1048 emit relativeYChanged();
1049 emit changed();
1050 }
1051}
1052
1053bool QQuickCurve::hasRelativeY()
1054{
1055 return _relativeY.isValid();
1056}
1057
1058/****************************************************************************/
1059
1060/*!
1061 \qmltype PathAttribute
1062 \instantiates QQuickPathAttribute
1063 \inqmlmodule QtQuick
1064 \ingroup qtquick-animation-paths
1065 \brief Specifies how to set an attribute at a given position in a Path.
1066
1067 The PathAttribute object allows attributes consisting of a name and
1068 a value to be specified for various points along a path. The
1069 attributes are exposed to the delegate as
1070 \l{Attached Properties and Attached Signal Handlers} {Attached Properties}.
1071 The value of an attribute at any particular point along the path is interpolated
1072 from the PathAttributes bounding that point.
1073
1074 The example below shows a path with the items scaled to 30% with
1075 opacity 50% at the top of the path and scaled 100% with opacity
1076 100% at the bottom. Note the use of the PathView.iconScale and
1077 PathView.iconOpacity attached properties to set the scale and opacity
1078 of the delegate.
1079
1080 \table
1081 \row
1082 \li \image declarative-pathattribute.png
1083 \li
1084 \snippet qml/pathview/pathattributes.qml 0
1085 (see the PathView documentation for the specification of ContactModel.qml
1086 used for ContactModel above.)
1087 \endtable
1088
1089
1090 \sa Path
1091*/
1092
1093/*!
1094 \qmlproperty string QtQuick::PathAttribute::name
1095 This property holds the name of the attribute to change.
1096
1097 This attribute will be available to the delegate as PathView.<name>
1098
1099 Note that using an existing Item property name such as "opacity" as an
1100 attribute is allowed. This is because path attributes add a new
1101 \l{Attached Properties and Attached Signal Handlers} {Attached Property}
1102 which in no way clashes with existing properties.
1103*/
1104
1105/*!
1106 the name of the attribute to change.
1107*/
1108
1109QString QQuickPathAttribute::name() const
1110{
1111 return _name;
1112}
1113
1114void QQuickPathAttribute::setName(const QString &name)
1115{
1116 if (_name == name)
1117 return;
1118 _name = name;
1119 emit nameChanged();
1120}
1121
1122/*!
1123 \qmlproperty real QtQuick::PathAttribute::value
1124 This property holds the value for the attribute.
1125
1126 The value specified can be used to influence the visual appearance
1127 of an item along the path. For example, the following Path specifies
1128 an attribute named \e itemRotation, which has the value \e 0 at the
1129 beginning of the path, and the value 90 at the end of the path.
1130
1131 \qml
1132 Path {
1133 startX: 0
1134 startY: 0
1135 PathAttribute { name: "itemRotation"; value: 0 }
1136 PathLine { x: 100; y: 100 }
1137 PathAttribute { name: "itemRotation"; value: 90 }
1138 }
1139 \endqml
1140
1141 In our delegate, we can then bind the \e rotation property to the
1142 \l{Attached Properties and Attached Signal Handlers} {Attached Property}
1143 \e PathView.itemRotation created for this attribute.
1144
1145 \qml
1146 Rectangle {
1147 width: 10; height: 10
1148 rotation: PathView.itemRotation
1149 }
1150 \endqml
1151
1152 As each item is positioned along the path, it will be rotated accordingly:
1153 an item at the beginning of the path with be not be rotated, an item at
1154 the end of the path will be rotated 90 degrees, and an item mid-way along
1155 the path will be rotated 45 degrees.
1156*/
1157
1158/*!
1159 the new value of the attribute.
1160*/
1161qreal QQuickPathAttribute::value() const
1162{
1163 return _value;
1164}
1165
1166void QQuickPathAttribute::setValue(qreal value)
1167{
1168 if (_value != value) {
1169 _value = value;
1170 emit valueChanged();
1171 emit changed();
1172 }
1173}
1174
1175/****************************************************************************/
1176
1177/*!
1178 \qmltype PathLine
1179 \instantiates QQuickPathLine
1180 \inqmlmodule QtQuick
1181 \ingroup qtquick-animation-paths
1182 \brief Defines a straight line.
1183
1184 The example below creates a path consisting of a straight line from
1185 0,100 to 200,100:
1186
1187 \qml
1188 Path {
1189 startX: 0; startY: 100
1190 PathLine { x: 200; y: 100 }
1191 }
1192 \endqml
1193
1194 \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline
1195*/
1196
1197/*!
1198 \qmlproperty real QtQuick::PathLine::x
1199 \qmlproperty real QtQuick::PathLine::y
1200
1201 Defines the end point of the line.
1202
1203 \sa relativeX, relativeY
1204*/
1205
1206/*!
1207 \qmlproperty real QtQuick::PathLine::relativeX
1208 \qmlproperty real QtQuick::PathLine::relativeY
1209
1210 Defines the end point of the line relative to its start.
1211
1212 If both a relative and absolute end position are specified for a single axis, the relative
1213 position will be used.
1214
1215 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1216 and an absolute y.
1217
1218 \sa x, y
1219*/
1220
1221inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
1222{
1223 QQuickCurve *curve = data.curves.at(i: data.index);
1224 bool isEnd = data.index == data.curves.size() - 1;
1225 return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
1226 curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
1227}
1228
1229void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
1230{
1231 path.lineTo(p: positionForCurve(data, prevPoint: path.currentPosition()));
1232}
1233
1234/****************************************************************************/
1235
1236/*!
1237 \qmltype PathMove
1238 \instantiates QQuickPathMove
1239 \inqmlmodule QtQuick
1240 \ingroup qtquick-animation-paths
1241 \brief Moves the Path's position.
1242
1243 The example below creates a path consisting of two horizontal lines with
1244 some empty space between them. All three segments have a width of 100:
1245
1246 \qml
1247 Path {
1248 startX: 0; startY: 100
1249 PathLine { relativeX: 100; y: 100 }
1250 PathMove { relativeX: 100; y: 100 }
1251 PathLine { relativeX: 100; y: 100 }
1252 }
1253 \endqml
1254
1255 \note PathMove should not be used in a Path associated with a PathView. Use
1256 PathLine instead. For ShapePath however it is important to distinguish
1257 between the operations of drawing a straight line and moving the path
1258 position without drawing anything.
1259
1260 \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathLine
1261*/
1262
1263/*!
1264 \qmlproperty real QtQuick::PathMove::x
1265 \qmlproperty real QtQuick::PathMove::y
1266
1267 Defines the position to move to.
1268
1269 \sa relativeX, relativeY
1270*/
1271
1272/*!
1273 \qmlproperty real QtQuick::PathMove::relativeX
1274 \qmlproperty real QtQuick::PathMove::relativeY
1275
1276 Defines the position to move to relative to its start.
1277
1278 If both a relative and absolute end position are specified for a single axis, the relative
1279 position will be used.
1280
1281 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1282 and an absolute y.
1283
1284 \sa x, y
1285*/
1286
1287void QQuickPathMove::addToPath(QPainterPath &path, const QQuickPathData &data)
1288{
1289 path.moveTo(p: positionForCurve(data, prevPoint: path.currentPosition()));
1290}
1291
1292/****************************************************************************/
1293
1294/*!
1295 \qmltype PathQuad
1296 \instantiates QQuickPathQuad
1297 \inqmlmodule QtQuick
1298 \ingroup qtquick-animation-paths
1299 \brief Defines a quadratic Bezier curve with a control point.
1300
1301 The following QML produces the path shown below:
1302 \table
1303 \row
1304 \li \image declarative-pathquad.png
1305 \li
1306 \qml
1307 Path {
1308 startX: 0; startY: 0
1309 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
1310 }
1311 \endqml
1312 \endtable
1313
1314 \sa Path, PathCubic, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg
1315*/
1316
1317/*!
1318 \qmlproperty real QtQuick::PathQuad::x
1319 \qmlproperty real QtQuick::PathQuad::y
1320
1321 Defines the end point of the curve.
1322
1323 \sa relativeX, relativeY
1324*/
1325
1326/*!
1327 \qmlproperty real QtQuick::PathQuad::relativeX
1328 \qmlproperty real QtQuick::PathQuad::relativeY
1329
1330 Defines the end point of the curve relative to its start.
1331
1332 If both a relative and absolute end position are specified for a single axis, the relative
1333 position will be used.
1334
1335 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1336 and an absolute y.
1337
1338 \sa x, y
1339*/
1340
1341/*!
1342 \qmlproperty real QtQuick::PathQuad::controlX
1343 \qmlproperty real QtQuick::PathQuad::controlY
1344
1345 Defines the position of the control point.
1346*/
1347
1348/*!
1349 the x position of the control point.
1350*/
1351qreal QQuickPathQuad::controlX() const
1352{
1353 return _controlX;
1354}
1355
1356void QQuickPathQuad::setControlX(qreal x)
1357{
1358 if (_controlX != x) {
1359 _controlX = x;
1360 emit controlXChanged();
1361 emit changed();
1362 }
1363}
1364
1365
1366/*!
1367 the y position of the control point.
1368*/
1369qreal QQuickPathQuad::controlY() const
1370{
1371 return _controlY;
1372}
1373
1374void QQuickPathQuad::setControlY(qreal y)
1375{
1376 if (_controlY != y) {
1377 _controlY = y;
1378 emit controlYChanged();
1379 emit changed();
1380 }
1381}
1382
1383/*!
1384 \qmlproperty real QtQuick::PathQuad::relativeControlX
1385 \qmlproperty real QtQuick::PathQuad::relativeControlY
1386
1387 Defines the position of the control point relative to the curve's start.
1388
1389 If both a relative and absolute control position are specified for a single axis, the relative
1390 position will be used.
1391
1392 Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1393 and an absolute control y.
1394
1395 \sa controlX, controlY
1396*/
1397
1398qreal QQuickPathQuad::relativeControlX() const
1399{
1400 return _relativeControlX;
1401}
1402
1403void QQuickPathQuad::setRelativeControlX(qreal x)
1404{
1405 if (_relativeControlX.isNull || _relativeControlX != x) {
1406 _relativeControlX = x;
1407 emit relativeControlXChanged();
1408 emit changed();
1409 }
1410}
1411
1412bool QQuickPathQuad::hasRelativeControlX()
1413{
1414 return _relativeControlX.isValid();
1415}
1416
1417qreal QQuickPathQuad::relativeControlY() const
1418{
1419 return _relativeControlY;
1420}
1421
1422void QQuickPathQuad::setRelativeControlY(qreal y)
1423{
1424 if (_relativeControlY.isNull || _relativeControlY != y) {
1425 _relativeControlY = y;
1426 emit relativeControlYChanged();
1427 emit changed();
1428 }
1429}
1430
1431bool QQuickPathQuad::hasRelativeControlY()
1432{
1433 return _relativeControlY.isValid();
1434}
1435
1436void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
1437{
1438 const QPointF &prevPoint = path.currentPosition();
1439 QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1440 hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1441 path.quadTo(ctrlPt: controlPoint, endPt: positionForCurve(data, prevPoint: path.currentPosition()));
1442}
1443
1444/****************************************************************************/
1445
1446/*!
1447 \qmltype PathCubic
1448 \instantiates QQuickPathCubic
1449 \inqmlmodule QtQuick
1450 \ingroup qtquick-animation-paths
1451 \brief Defines a cubic Bezier curve with two control points.
1452
1453 The following QML produces the path shown below:
1454 \table
1455 \row
1456 \li \image declarative-pathcubic.png
1457 \li
1458 \qml
1459 Path {
1460 startX: 20; startY: 0
1461 PathCubic {
1462 x: 180; y: 0
1463 control1X: -10; control1Y: 90
1464 control2X: 210; control2Y: 90
1465 }
1466 }
1467 \endqml
1468 \endtable
1469
1470 \sa Path, PathQuad, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg
1471*/
1472
1473/*!
1474 \qmlproperty real QtQuick::PathCubic::x
1475 \qmlproperty real QtQuick::PathCubic::y
1476
1477 Defines the end point of the curve.
1478
1479 \sa relativeX, relativeY
1480*/
1481
1482/*!
1483 \qmlproperty real QtQuick::PathCubic::relativeX
1484 \qmlproperty real QtQuick::PathCubic::relativeY
1485
1486 Defines the end point of the curve relative to its start.
1487
1488 If both a relative and absolute end position are specified for a single axis, the relative
1489 position will be used.
1490
1491 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1492 and an absolute y.
1493
1494 \sa x, y
1495*/
1496
1497/*!
1498 \qmlproperty real QtQuick::PathCubic::control1X
1499 \qmlproperty real QtQuick::PathCubic::control1Y
1500
1501 Defines the position of the first control point.
1502*/
1503qreal QQuickPathCubic::control1X() const
1504{
1505 return _control1X;
1506}
1507
1508void QQuickPathCubic::setControl1X(qreal x)
1509{
1510 if (_control1X != x) {
1511 _control1X = x;
1512 emit control1XChanged();
1513 emit changed();
1514 }
1515}
1516
1517qreal QQuickPathCubic::control1Y() const
1518{
1519 return _control1Y;
1520}
1521
1522void QQuickPathCubic::setControl1Y(qreal y)
1523{
1524 if (_control1Y != y) {
1525 _control1Y = y;
1526 emit control1YChanged();
1527 emit changed();
1528 }
1529}
1530
1531/*!
1532 \qmlproperty real QtQuick::PathCubic::control2X
1533 \qmlproperty real QtQuick::PathCubic::control2Y
1534
1535 Defines the position of the second control point.
1536*/
1537qreal QQuickPathCubic::control2X() const
1538{
1539 return _control2X;
1540}
1541
1542void QQuickPathCubic::setControl2X(qreal x)
1543{
1544 if (_control2X != x) {
1545 _control2X = x;
1546 emit control2XChanged();
1547 emit changed();
1548 }
1549}
1550
1551qreal QQuickPathCubic::control2Y() const
1552{
1553 return _control2Y;
1554}
1555
1556void QQuickPathCubic::setControl2Y(qreal y)
1557{
1558 if (_control2Y != y) {
1559 _control2Y = y;
1560 emit control2YChanged();
1561 emit changed();
1562 }
1563}
1564
1565/*!
1566 \qmlproperty real QtQuick::PathCubic::relativeControl1X
1567 \qmlproperty real QtQuick::PathCubic::relativeControl1Y
1568 \qmlproperty real QtQuick::PathCubic::relativeControl2X
1569 \qmlproperty real QtQuick::PathCubic::relativeControl2Y
1570
1571 Defines the positions of the control points relative to the curve's start.
1572
1573 If both a relative and absolute control position are specified for a control point's axis, the relative
1574 position will be used.
1575
1576 Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1577 and an absolute control1 y.
1578
1579 \sa control1X, control1Y, control2X, control2Y
1580*/
1581
1582qreal QQuickPathCubic::relativeControl1X() const
1583{
1584 return _relativeControl1X;
1585}
1586
1587void QQuickPathCubic::setRelativeControl1X(qreal x)
1588{
1589 if (_relativeControl1X.isNull || _relativeControl1X != x) {
1590 _relativeControl1X = x;
1591 emit relativeControl1XChanged();
1592 emit changed();
1593 }
1594}
1595
1596bool QQuickPathCubic::hasRelativeControl1X()
1597{
1598 return _relativeControl1X.isValid();
1599}
1600
1601qreal QQuickPathCubic::relativeControl1Y() const
1602{
1603 return _relativeControl1Y;
1604}
1605
1606void QQuickPathCubic::setRelativeControl1Y(qreal y)
1607{
1608 if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1609 _relativeControl1Y = y;
1610 emit relativeControl1YChanged();
1611 emit changed();
1612 }
1613}
1614
1615bool QQuickPathCubic::hasRelativeControl1Y()
1616{
1617 return _relativeControl1Y.isValid();
1618}
1619
1620qreal QQuickPathCubic::relativeControl2X() const
1621{
1622 return _relativeControl2X;
1623}
1624
1625void QQuickPathCubic::setRelativeControl2X(qreal x)
1626{
1627 if (_relativeControl2X.isNull || _relativeControl2X != x) {
1628 _relativeControl2X = x;
1629 emit relativeControl2XChanged();
1630 emit changed();
1631 }
1632}
1633
1634bool QQuickPathCubic::hasRelativeControl2X()
1635{
1636 return _relativeControl2X.isValid();
1637}
1638
1639qreal QQuickPathCubic::relativeControl2Y() const
1640{
1641 return _relativeControl2Y;
1642}
1643
1644void QQuickPathCubic::setRelativeControl2Y(qreal y)
1645{
1646 if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1647 _relativeControl2Y = y;
1648 emit relativeControl2YChanged();
1649 emit changed();
1650 }
1651}
1652
1653bool QQuickPathCubic::hasRelativeControl2Y()
1654{
1655 return _relativeControl2Y.isValid();
1656}
1657
1658void QQuickPathCubic::addToPath(QPainterPath &path, const QQuickPathData &data)
1659{
1660 const QPointF &prevPoint = path.currentPosition();
1661 QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1662 hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1663 QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1664 hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1665 path.cubicTo(ctrlPt1: controlPoint1, ctrlPt2: controlPoint2, endPt: positionForCurve(data, prevPoint: path.currentPosition()));
1666}
1667
1668/****************************************************************************/
1669
1670/*!
1671 \qmltype PathCurve
1672 \instantiates QQuickPathCatmullRomCurve
1673 \inqmlmodule QtQuick
1674 \ingroup qtquick-animation-paths
1675 \brief Defines a point on a Catmull-Rom curve.
1676
1677 PathCurve provides an easy way to specify a curve passing directly through a set of points.
1678 Typically multiple PathCurves are used in a series, as the following example demonstrates:
1679
1680 \snippet qml/path/basiccurve.qml 0
1681
1682 This example produces the following path (with the starting point and PathCurve points
1683 highlighted in red):
1684
1685 \image declarative-pathcurve.png
1686
1687 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1688*/
1689
1690/*!
1691 \qmlproperty real QtQuick::PathCurve::x
1692 \qmlproperty real QtQuick::PathCurve::y
1693
1694 Defines the end point of the curve.
1695
1696 \sa relativeX, relativeY
1697*/
1698
1699/*!
1700 \qmlproperty real QtQuick::PathCurve::relativeX
1701 \qmlproperty real QtQuick::PathCurve::relativeY
1702
1703 Defines the end point of the curve relative to its start.
1704
1705 If both a relative and absolute end position are specified for a single axis, the relative
1706 position will be used.
1707
1708 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1709 and an absolute y.
1710
1711 \sa x, y
1712*/
1713
1714inline QPointF previousPathPosition(const QPainterPath &path)
1715{
1716 int count = path.elementCount();
1717 if (count < 1)
1718 return QPointF();
1719
1720 int index = path.elementAt(i: count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1721 return index > -1 ? QPointF(path.elementAt(i: index)) : path.pointAtPercent(t: 0);
1722}
1723
1724void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathData &data)
1725{
1726 //here we convert catmull-rom spline to bezier for use in QPainterPath.
1727 //basic conversion algorithm:
1728 // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1729 //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1730 //calculations for each point use a moving window of 4 points
1731 // (previous 2 points + current point + next point)
1732 QPointF prevFar, prev, point, next;
1733
1734 //get previous points
1735 int index = data.index - 1;
1736 QQuickCurve *curve = index == -1 ? 0 : data.curves.at(i: index);
1737 if (qobject_cast<QQuickPathCatmullRomCurve*>(object: curve)) {
1738 prev = path.currentPosition();
1739 prevFar = previousPathPosition(path);
1740 } else {
1741 prev = path.currentPosition();
1742 bool prevFarSet = false;
1743 if (index == -1 && data.curves.size() > 1) {
1744 if (qobject_cast<QQuickPathCatmullRomCurve*>(object: data.curves.at(i: data.curves.size()-1))) {
1745 //TODO: profile and optimize
1746 QPointF pos = prev;
1747 QQuickPathData loopData;
1748 loopData.endPoint = data.endPoint;
1749 loopData.curves = data.curves;
1750 for (int i = data.index; i < data.curves.size(); ++i) {
1751 loopData.index = i;
1752 pos = positionForCurve(data: loopData, prevPoint: pos);
1753 if (i == data.curves.size()-2)
1754 prevFar = pos;
1755 }
1756 if (pos == QPointF(path.elementAt(i: 0))) {
1757 //this is a closed path starting and ending with catmull-rom segments.
1758 //we try to smooth the join point
1759 prevFarSet = true;
1760 }
1761 }
1762 }
1763 if (!prevFarSet)
1764 prevFar = prev;
1765 }
1766
1767 //get current point
1768 point = positionForCurve(data, prevPoint: path.currentPosition());
1769
1770 //get next point
1771 index = data.index + 1;
1772 if (index < data.curves.size() && qobject_cast<QQuickPathCatmullRomCurve*>(object: data.curves.at(i: index))) {
1773 QQuickPathData nextData;
1774 nextData.index = index;
1775 nextData.endPoint = data.endPoint;
1776 nextData.curves = data.curves;
1777 next = positionForCurve(data: nextData, prevPoint: point);
1778 } else {
1779 if (point == QPointF(path.elementAt(i: 0)) && qobject_cast<QQuickPathCatmullRomCurve*>(object: data.curves.at(i: 0)) && path.elementCount() >= 3) {
1780 //this is a closed path starting and ending with catmull-rom segments.
1781 //we try to smooth the join point
1782 next = QPointF(path.elementAt(i: 3)); //the first catmull-rom point
1783 } else
1784 next = point;
1785 }
1786
1787 /*
1788 full conversion matrix (inverse bezier * catmull-rom):
1789 0.000, 1.000, 0.000, 0.000,
1790 -0.167, 1.000, 0.167, 0.000,
1791 0.000, 0.167, 1.000, -0.167,
1792 0.000, 0.000, 1.000, 0.000
1793
1794 conversion doesn't require full matrix multiplication,
1795 so below we simplify
1796 */
1797 QPointF control1(prevFar.x() * qreal(-0.167) +
1798 prev.x() +
1799 point.x() * qreal(0.167),
1800 prevFar.y() * qreal(-0.167) +
1801 prev.y() +
1802 point.y() * qreal(0.167));
1803
1804 QPointF control2(prev.x() * qreal(0.167) +
1805 point.x() +
1806 next.x() * qreal(-0.167),
1807 prev.y() * qreal(0.167) +
1808 point.y() +
1809 next.y() * qreal(-0.167));
1810
1811 path.cubicTo(ctrlPt1: control1, ctrlPt2: control2, endPt: point);
1812}
1813
1814/****************************************************************************/
1815
1816/*!
1817 \qmltype PathArc
1818 \instantiates QQuickPathArc
1819 \inqmlmodule QtQuick
1820 \ingroup qtquick-animation-paths
1821 \brief Defines an arc with the given radius.
1822
1823 PathArc provides a simple way of specifying an arc that ends at a given position
1824 and uses the specified radius. It is modeled after the SVG elliptical arc command.
1825
1826 The following QML produces the path shown below:
1827 \table
1828 \row
1829 \li \image declarative-patharc.png
1830 \li \snippet qml/path/basicarc.qml 0
1831 \endtable
1832
1833 Note that a single PathArc cannot be used to specify a circle. Instead, you can
1834 use two PathArc elements, each specifying half of the circle.
1835
1836 \sa Path, PathLine, PathQuad, PathCubic, PathAngleArc, PathCurve, PathSvg
1837*/
1838
1839/*!
1840 \qmlproperty real QtQuick::PathArc::x
1841 \qmlproperty real QtQuick::PathArc::y
1842
1843 Defines the end point of the arc.
1844
1845 \sa relativeX, relativeY
1846*/
1847
1848/*!
1849 \qmlproperty real QtQuick::PathArc::relativeX
1850 \qmlproperty real QtQuick::PathArc::relativeY
1851
1852 Defines the end point of the arc relative to its start.
1853
1854 If both a relative and absolute end position are specified for a single axis, the relative
1855 position will be used.
1856
1857 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1858 and an absolute y.
1859
1860 \sa x, y
1861*/
1862
1863/*!
1864 \qmlproperty real QtQuick::PathArc::radiusX
1865 \qmlproperty real QtQuick::PathArc::radiusY
1866
1867 Defines the radius of the arc.
1868
1869 The following QML demonstrates how different radius values can be used to change
1870 the shape of the arc:
1871 \table
1872 \row
1873 \li \image declarative-arcradius.png
1874 \li \snippet qml/path/arcradius.qml 0
1875 \endtable
1876*/
1877
1878qreal QQuickPathArc::radiusX() const
1879{
1880 return _radiusX;
1881}
1882
1883void QQuickPathArc::setRadiusX(qreal radius)
1884{
1885 if (_radiusX == radius)
1886 return;
1887
1888 _radiusX = radius;
1889 emit radiusXChanged();
1890 emit changed();
1891}
1892
1893qreal QQuickPathArc::radiusY() const
1894{
1895 return _radiusY;
1896}
1897
1898void QQuickPathArc::setRadiusY(qreal radius)
1899{
1900 if (_radiusY == radius)
1901 return;
1902
1903 _radiusY = radius;
1904 emit radiusYChanged();
1905 emit changed();
1906}
1907
1908/*!
1909 \qmlproperty bool QtQuick::PathArc::useLargeArc
1910 Whether to use a large arc as defined by the arc points.
1911
1912 Given fixed start and end positions, radius, and direction,
1913 there are two possible arcs that can fit the data. useLargeArc
1914 is used to distinguish between these. For example, the following
1915 QML can produce either of the two illustrated arcs below by
1916 changing the value of useLargeArc.
1917
1918 \table
1919 \row
1920 \li \image declarative-largearc.png
1921 \li \snippet qml/path/largearc.qml 0
1922 \endtable
1923
1924 The default value is false.
1925*/
1926
1927bool QQuickPathArc::useLargeArc() const
1928{
1929 return _useLargeArc;
1930}
1931
1932void QQuickPathArc::setUseLargeArc(bool largeArc)
1933{
1934 if (_useLargeArc == largeArc)
1935 return;
1936
1937 _useLargeArc = largeArc;
1938 emit useLargeArcChanged();
1939 emit changed();
1940}
1941
1942/*!
1943 \qmlproperty enumeration QtQuick::PathArc::direction
1944
1945 Defines the direction of the arc. Possible values are
1946 PathArc.Clockwise (default) and PathArc.Counterclockwise.
1947
1948 The following QML can produce either of the two illustrated arcs below
1949 by changing the value of direction.
1950 \table
1951 \row
1952 \li \image declarative-arcdirection.png
1953 \li \snippet qml/path/arcdirection.qml 0
1954 \endtable
1955
1956 \sa useLargeArc
1957*/
1958
1959QQuickPathArc::ArcDirection QQuickPathArc::direction() const
1960{
1961 return _direction;
1962}
1963
1964void QQuickPathArc::setDirection(ArcDirection direction)
1965{
1966 if (_direction == direction)
1967 return;
1968
1969 _direction = direction;
1970 emit directionChanged();
1971 emit changed();
1972}
1973
1974/*!
1975 \qmlproperty real QtQuick::PathArc::xAxisRotation
1976
1977 Defines the rotation of the arc, in degrees. The default value is 0.
1978
1979 An arc is a section of circles or ellipses. Given the radius and the start
1980 and end points, there are two ellipses that connect the points. This
1981 property defines the rotation of the X axis of these ellipses.
1982
1983 \note The value is only useful when the x and y radius differ, meaning the
1984 arc is a section of ellipses.
1985
1986 The following QML demonstrates how different radius values can be used to change
1987 the shape of the arc:
1988 \table
1989 \row
1990 \li \image declarative-arcrotation.png
1991 \li \snippet qml/path/arcrotation.qml 0
1992 \endtable
1993*/
1994
1995qreal QQuickPathArc::xAxisRotation() const
1996{
1997 return _xAxisRotation;
1998}
1999
2000void QQuickPathArc::setXAxisRotation(qreal rotation)
2001{
2002 if (_xAxisRotation == rotation)
2003 return;
2004
2005 _xAxisRotation = rotation;
2006 emit xAxisRotationChanged();
2007 emit changed();
2008}
2009
2010void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
2011{
2012 const QPointF &startPoint = path.currentPosition();
2013 const QPointF &endPoint = positionForCurve(data, prevPoint: startPoint);
2014 QQuickSvgParser::pathArc(path,
2015 rx: _radiusX,
2016 ry: _radiusY,
2017 x_axis_rotation: _xAxisRotation,
2018 large_arc_flag: _useLargeArc,
2019 sweep_flag: _direction == Clockwise ? 1 : 0,
2020 x: endPoint.x(),
2021 y: endPoint.y(),
2022 curx: startPoint.x(), cury: startPoint.y());
2023}
2024
2025/****************************************************************************/
2026
2027/*!
2028 \qmltype PathAngleArc
2029 \instantiates QQuickPathAngleArc
2030 \inqmlmodule QtQuick
2031 \ingroup qtquick-animation-paths
2032 \brief Defines an arc with the given radii and center.
2033
2034 PathAngleArc provides a simple way of specifying an arc. While PathArc is designed
2035 to work as part of a larger path (specifying start and end), PathAngleArc is designed
2036 to make a path where the arc is primary (such as a circular progress indicator) more intuitive.
2037
2038 \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg, PathArc
2039*/
2040
2041/*!
2042 \qmlproperty real QtQuick::PathAngleArc::centerX
2043 \qmlproperty real QtQuick::PathAngleArc::centerY
2044
2045 Defines the center of the arc.
2046*/
2047
2048qreal QQuickPathAngleArc::centerX() const
2049{
2050 return _centerX;
2051}
2052
2053void QQuickPathAngleArc::setCenterX(qreal centerX)
2054{
2055 if (_centerX == centerX)
2056 return;
2057
2058 _centerX = centerX;
2059 emit centerXChanged();
2060 emit changed();
2061}
2062
2063qreal QQuickPathAngleArc::centerY() const
2064{
2065 return _centerY;
2066}
2067
2068void QQuickPathAngleArc::setCenterY(qreal centerY)
2069{
2070 if (_centerY == centerY)
2071 return;
2072
2073 _centerY = centerY;
2074 emit centerYChanged();
2075 emit changed();
2076}
2077
2078/*!
2079 \qmlproperty real QtQuick::PathAngleArc::radiusX
2080 \qmlproperty real QtQuick::PathAngleArc::radiusY
2081
2082 Defines the radii of the ellipse of which the arc is part.
2083*/
2084
2085qreal QQuickPathAngleArc::radiusX() const
2086{
2087 return _radiusX;
2088}
2089
2090void QQuickPathAngleArc::setRadiusX(qreal radius)
2091{
2092 if (_radiusX == radius)
2093 return;
2094
2095 _radiusX = radius;
2096 emit radiusXChanged();
2097 emit changed();
2098}
2099
2100qreal QQuickPathAngleArc::radiusY() const
2101{
2102 return _radiusY;
2103}
2104
2105void QQuickPathAngleArc::setRadiusY(qreal radius)
2106{
2107 if (_radiusY == radius)
2108 return;
2109
2110 _radiusY = radius;
2111 emit radiusYChanged();
2112 emit changed();
2113}
2114
2115/*!
2116 \qmlproperty real QtQuick::PathAngleArc::startAngle
2117
2118 Defines the start angle of the arc.
2119
2120 The start angle is reported clockwise, with zero degrees at the 3 o'clock position.
2121*/
2122
2123qreal QQuickPathAngleArc::startAngle() const
2124{
2125 return _startAngle;
2126}
2127
2128void QQuickPathAngleArc::setStartAngle(qreal angle)
2129{
2130 if (_startAngle == angle)
2131 return;
2132
2133 _startAngle = angle;
2134 emit startAngleChanged();
2135 emit changed();
2136}
2137
2138/*!
2139 \qmlproperty real QtQuick::PathAngleArc::sweepAngle
2140
2141 Defines the sweep angle of the arc.
2142
2143 The arc will begin at startAngle and continue sweepAngle degrees, with a value of 360
2144 resulting in a full circle. Positive numbers are clockwise and negative numbers are counterclockwise.
2145*/
2146
2147qreal QQuickPathAngleArc::sweepAngle() const
2148{
2149 return _sweepAngle;
2150}
2151
2152void QQuickPathAngleArc::setSweepAngle(qreal angle)
2153{
2154 if (_sweepAngle == angle)
2155 return;
2156
2157 _sweepAngle = angle;
2158 emit sweepAngleChanged();
2159 emit changed();
2160}
2161
2162/*!
2163 \qmlproperty bool QtQuick::PathAngleArc::moveToStart
2164
2165 Whether this element should be disconnected from the previous Path element (or startX/Y).
2166
2167 The default value is true. If set to false, the previous element's end-point
2168 (or startX/Y if PathAngleArc is the first element) will be connected to the arc's
2169 start-point with a straight line.
2170*/
2171
2172bool QQuickPathAngleArc::moveToStart() const
2173{
2174 return _moveToStart;
2175}
2176
2177void QQuickPathAngleArc::setMoveToStart(bool move)
2178{
2179 if (_moveToStart == move)
2180 return;
2181
2182 _moveToStart = move;
2183 emit moveToStartChanged();
2184 emit changed();
2185}
2186
2187void QQuickPathAngleArc::addToPath(QPainterPath &path, const QQuickPathData &)
2188{
2189 qreal x = _centerX - _radiusX;
2190 qreal y = _centerY - _radiusY;
2191 qreal width = _radiusX * 2;
2192 qreal height = _radiusY * 2;
2193 if (_moveToStart)
2194 path.arcMoveTo(x, y, w: width, h: height, angle: -_startAngle);
2195 path.arcTo(x, y, w: width, h: height, startAngle: -_startAngle, arcLength: -_sweepAngle);
2196}
2197
2198/****************************************************************************/
2199
2200/*!
2201 \qmltype PathSvg
2202 \instantiates QQuickPathSvg
2203 \inqmlmodule QtQuick
2204 \ingroup qtquick-animation-paths
2205 \brief Defines a path using an SVG path data string.
2206
2207 The following QML produces the path shown below:
2208 \table
2209 \row
2210 \li \image declarative-pathsvg.png
2211 \li
2212 \qml
2213 Path {
2214 startX: 50; startY: 50
2215 PathSvg { path: "L 150 50 L 100 150 z" }
2216 }
2217 \endqml
2218 \endtable
2219
2220 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve
2221*/
2222
2223/*!
2224 \qmlproperty string QtQuick::PathSvg::path
2225
2226 The SVG path data string specifying the path.
2227
2228 See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
2229 for more details on this format.
2230*/
2231
2232QString QQuickPathSvg::path() const
2233{
2234 return _path;
2235}
2236
2237void QQuickPathSvg::setPath(const QString &path)
2238{
2239 if (_path == path)
2240 return;
2241
2242 _path = path;
2243 emit pathChanged();
2244 emit changed();
2245}
2246
2247void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
2248{
2249 QQuickSvgParser::parsePathDataFast(dataStr: _path, path);
2250}
2251
2252/****************************************************************************/
2253
2254/*!
2255 \qmltype PathPercent
2256 \instantiates QQuickPathPercent
2257 \inqmlmodule QtQuick
2258 \ingroup qtquick-animation-paths
2259 \brief Manipulates the way a path is interpreted.
2260
2261 PathPercent allows you to manipulate the spacing between items on a
2262 PathView's path. You can use it to bunch together items on part of
2263 the path, and spread them out on other parts of the path.
2264
2265 The examples below show the normal distribution of items along a path
2266 compared to a distribution which places 50% of the items along the
2267 PathLine section of the path.
2268 \table
2269 \row
2270 \li \image declarative-nopercent.png
2271 \li
2272 \qml
2273 PathView {
2274 // ...
2275 Path {
2276 startX: 20; startY: 0
2277 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
2278 PathLine { x: 150; y: 80 }
2279 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
2280 }
2281 }
2282 \endqml
2283 \row
2284 \li \image declarative-percent.png
2285 \li
2286 \qml
2287 PathView {
2288 // ...
2289 Path {
2290 startX: 20; startY: 0
2291 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
2292 PathPercent { value: 0.25 }
2293 PathLine { x: 150; y: 80 }
2294 PathPercent { value: 0.75 }
2295 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
2296 PathPercent { value: 1 }
2297 }
2298 }
2299 \endqml
2300 \endtable
2301
2302 \sa Path
2303*/
2304
2305/*!
2306 \qmlproperty real QtQuick::PathPercent::value
2307 The proportion of items that should be laid out up to this point.
2308
2309 This value should always be higher than the last value specified
2310 by a PathPercent at a previous position in the Path.
2311
2312 In the following example we have a Path made up of three PathLines.
2313 Normally, the items of the PathView would be laid out equally along
2314 this path, with an equal number of items per line segment. PathPercent
2315 allows us to specify that the first and third lines should each hold
2316 10% of the laid out items, while the second line should hold the remaining
2317 80%.
2318
2319 \qml
2320 PathView {
2321 // ...
2322 Path {
2323 startX: 0; startY: 0
2324 PathLine { x:100; y: 0; }
2325 PathPercent { value: 0.1 }
2326 PathLine { x: 100; y: 100 }
2327 PathPercent { value: 0.9 }
2328 PathLine { x: 100; y: 0 }
2329 PathPercent { value: 1 }
2330 }
2331 }
2332 \endqml
2333*/
2334
2335qreal QQuickPathPercent::value() const
2336{
2337 return _value;
2338}
2339
2340void QQuickPathPercent::setValue(qreal value)
2341{
2342 if (_value != value) {
2343 _value = value;
2344 emit valueChanged();
2345 emit changed();
2346 }
2347}
2348
2349/*!
2350 \qmltype PathPolyline
2351 \instantiates QQuickPathPolyline
2352 \inqmlmodule QtQuick
2353 \ingroup qtquick-animation-paths
2354 \brief Defines a polyline through a list of coordinates.
2355 \since QtQuick 2.14
2356
2357 The example below creates a triangular path consisting of four vertices
2358 on the edge of the containing Shape's bounding box.
2359 Through the containing shape's \l {QtQuick::Path::}{scale} property,
2360 the path will be rescaled together with its containing shape.
2361
2362 \qml
2363 PathPolyline {
2364 id: ppl
2365 path: [ Qt.point(0.0, 0.0),
2366 Qt.point(1.0, 0.0),
2367 Qt.point(0.5, 1.0),
2368 Qt.point(0.0, 0.0)
2369 ]
2370 }
2371 \endqml
2372
2373 \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline
2374*/
2375
2376/*!
2377 \qmlproperty point QtQuick::PathPolyline::start
2378
2379 This read-only property contains the beginning of the polyline.
2380*/
2381
2382/*!
2383 \qmlproperty list<point> QtQuick::PathPolyline::path
2384
2385 This property defines the vertices of the polyline.
2386
2387 It can be a JS array of points constructed with \c Qt.point(),
2388 a QList or QVector of QPointF, or QPolygonF.
2389 If you are binding this to a custom property in some C++ object,
2390 QPolygonF is the most appropriate type to use.
2391*/
2392
2393QQuickPathPolyline::QQuickPathPolyline(QObject *parent) : QQuickCurve(parent)
2394{
2395}
2396
2397QVariant QQuickPathPolyline::path() const
2398{
2399 return QVariant::fromValue(value: m_path);
2400}
2401
2402void QQuickPathPolyline::setPath(const QVariant &path)
2403{
2404 if (path.userType() == QMetaType::QPolygonF) {
2405 setPath(path.value<QPolygonF>());
2406 } else if (path.canConvert<QVector<QPointF>>()) {
2407 setPath(path.value<QVector<QPointF>>());
2408 } else if (path.canConvert<QVariantList>()) {
2409 // This handles cases other than QPolygonF or QVector<QPointF>, such as
2410 // QList<QPointF>, QVector<QPoint>, QVariantList of QPointF, QVariantList of QPoint.
2411 QVector<QPointF> pathList;
2412 QVariantList vl = path.value<QVariantList>();
2413 // If path is a QJSValue, e.g. coming from a JS array of Qt.point() in QML,
2414 // then path.value<QVariantList>() is inefficient.
2415 // TODO We should be able to iterate over path.value<QSequentialIterable>() eventually
2416 for (const QVariant &v : vl)
2417 pathList.append(t: v.toPointF());
2418 setPath(pathList);
2419 } else {
2420 qWarning() << "PathPolyline: path of type" << path.userType() << "not supported";
2421 }
2422}
2423
2424void QQuickPathPolyline::setPath(const QVector<QPointF> &path)
2425{
2426 if (m_path != path) {
2427 const QPointF &oldStart = start();
2428 m_path = path;
2429 const QPointF &newStart = start();
2430 emit pathChanged();
2431 if (oldStart != newStart)
2432 emit startChanged();
2433 emit changed();
2434 }
2435}
2436
2437QPointF QQuickPathPolyline::start() const
2438{
2439 if (m_path.size()) {
2440 const QPointF &p = m_path.first();
2441 return p;
2442 }
2443 return QPointF();
2444}
2445
2446void QQuickPathPolyline::addToPath(QPainterPath &path, const QQuickPathData &/*data*/)
2447{
2448 if (m_path.size() < 2)
2449 return;
2450
2451 path.moveTo(p: m_path.first());
2452 for (int i = 1; i < m_path.size(); ++i)
2453 path.lineTo(p: m_path.at(i));
2454}
2455
2456
2457/*!
2458 \qmltype PathMultiline
2459 \instantiates QQuickPathMultiline
2460 \inqmlmodule QtQuick
2461 \ingroup qtquick-animation-paths
2462 \brief Defines a set of polylines through a list of lists of coordinates.
2463 \since QtQuick 2.14
2464
2465 This element allows to define a list of polylines at once.
2466 Each polyline in the list will be preceded by a \l{QPainterPath::moveTo}{moveTo}
2467 command, effectively making each polyline a separate one.
2468 The polylines in this list are supposed to be non-intersecting with each other.
2469 In any case, when used in conjunction with a \l ShapePath, the containing ShapePath's
2470 \l ShapePath::fillRule applies.
2471 That is, with the default \c OddEvenFill and non intersecting shapes, the largest shape in the list defines an area to be filled;
2472 areas where two shapes overlap are holes; areas where three shapes overlap are filled areas inside holes, etc.
2473
2474 The example below creates a high voltage symbol by adding each path
2475 of the symbol to the list of paths.
2476 The coordinates of the vertices are normalized, and through the containing shape's
2477 \l {QtQuick::Path::}{scale} property, the path will be rescaled together with its containing shape.
2478
2479 \qml
2480 PathMultiline {
2481 paths: [
2482 [Qt.point(0.5, 0.06698),
2483 Qt.point(1, 0.93301),
2484 Qt.point(0, 0.93301),
2485 Qt.point(0.5, 0.06698)],
2486
2487 [Qt.point(0.5, 0.12472),
2488 Qt.point(0.95, 0.90414),
2489 Qt.point(0.05, 0.90414),
2490 Qt.point(0.5, 0.12472)],
2491
2492 [Qt.point(0.47131, 0.32986),
2493 Qt.point(0.36229, 0.64789),
2494 Qt.point(0.51492, 0.58590),
2495 Qt.point(0.47563, 0.76014),
2496 Qt.point(0.44950, 0.73590),
2497 Qt.point(0.46292, 0.83392),
2498 Qt.point(0.52162, 0.75190),
2499 Qt.point(0.48531, 0.76230),
2500 Qt.point(0.57529, 0.53189),
2501 Qt.point(0.41261, 0.59189),
2502 Qt.point(0.53001, 0.32786),
2503 Qt.point(0.47131, 0.32986)]
2504 ]
2505 }
2506 \endqml
2507
2508 \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
2509*/
2510
2511/*!
2512 \qmlproperty point QtQuick::PathMultiline::start
2513
2514 This read-only property contains the beginning of the polylines.
2515*/
2516
2517/*!
2518 \qmlproperty list<list<point>> QtQuick::PathMultiline::paths
2519
2520 This property defines the vertices of the polylines.
2521
2522 It can be a JS array of JS arrays of points constructed with \c Qt.point(),
2523 a QList or QVector of QPolygonF, or QVector<QVector<QPointF>>.
2524 If you are binding this to a custom property in some C++ object,
2525 QVector<QPolygonF> or QVector<QVector<QPointF>> is the most
2526 appropriate type to use.
2527*/
2528
2529QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent)
2530{
2531}
2532
2533QVariant QQuickPathMultiline::paths() const
2534{
2535 return QVariant::fromValue(value: m_paths);
2536}
2537
2538void QQuickPathMultiline::setPaths(const QVariant &paths)
2539{
2540 if (paths.canConvert<QVector<QPolygonF>>()) {
2541 const QVector<QPolygonF> pathPolygons = paths.value<QVector<QPolygonF>>();
2542 QVector<QVector<QPointF>> pathVectors;
2543 for (const QPolygonF &p : pathPolygons)
2544 pathVectors << p;
2545 setPaths(pathVectors);
2546 } else if (paths.canConvert<QVector<QVector<QPointF>>>()) {
2547 setPaths(paths.value<QVector<QVector<QPointF>>>());
2548 } else if (paths.canConvert<QVariantList>()) {
2549 // This handles cases other than QVector<QPolygonF> or QVector<QVector<QPointF>>, such as
2550 // QList<QVector<QPointF>>, QList<QList<QPointF>>, QVariantList of QVector<QPointF>,
2551 // QVariantList of QVariantList of QPointF, QVector<QList<QPoint>> etc.
2552 QVector<QVector<QPointF>> pathsList;
2553 QVariantList vll = paths.value<QVariantList>();
2554 for (const QVariant &v : vll) {
2555 // If we bind a QVector<QPolygonF> property directly, rather than via QVariant,
2556 // it will come through as QJSValue that can be converted to QVariantList of QPolygonF.
2557 if (v.canConvert<QPolygonF>()) {
2558 pathsList.append(t: v.value<QPolygonF>());
2559 } else {
2560 QVariantList vl = v.value<QVariantList>();
2561 QVector<QPointF> l;
2562 for (const QVariant &point : vl) {
2563 if (point.canConvert<QPointF>())
2564 l.append(t: point.toPointF());
2565 }
2566 if (l.size() >= 2)
2567 pathsList.append(t: l);
2568 }
2569 }
2570 setPaths(pathsList);
2571 } else {
2572 qWarning() << "PathMultiline: paths of type" << paths.userType() << "not supported";
2573 setPaths(QVector<QVector<QPointF>>());
2574 }
2575}
2576
2577void QQuickPathMultiline::setPaths(const QVector<QVector<QPointF>> &paths)
2578{
2579 if (m_paths != paths) {
2580 const QPointF &oldStart = start();
2581 m_paths = paths;
2582 const QPointF &newStart = start();
2583 emit pathsChanged();
2584 if (oldStart != newStart)
2585 emit startChanged();
2586 emit changed();
2587 }
2588}
2589
2590QPointF QQuickPathMultiline::start() const
2591{
2592 if (m_paths.size())
2593 return m_paths.first().first();
2594 return QPointF();
2595}
2596
2597void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
2598{
2599 if (!m_paths.size())
2600 return;
2601 for (const QVector<QPointF> &p: m_paths) {
2602 path.moveTo(p: p.first());
2603 for (int i = 1; i < p.size(); ++i)
2604 path.lineTo(p: p.at(i));
2605 }
2606}
2607
2608/*!
2609 \qmltype PathText
2610 \instantiates QQuickPathText
2611 \inqmlmodule QtQuick
2612 \ingroup qtquick-animation-paths
2613 \brief Defines a string in a specified font.
2614 \since QtQuick 2.15
2615
2616 This element defines the shape of a specified string in a specified font. The text's
2617 baseline will be translated to the x and y coordinates, and the outlines from the font
2618 will be added to the path accordingly.
2619
2620 \qml
2621 PathText {
2622 x: 0
2623 y: font.pixelSize
2624 font.family: "Arial"
2625 font.pixelSize: 100
2626 text: "Foobar"
2627 }
2628 \endqml
2629
2630 \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
2631*/
2632
2633/*!
2634 \qmlproperty real QtQuick::PathText::x
2635
2636 The horizontal position of the PathText's baseline.
2637*/
2638
2639/*!
2640 \qmlproperty real QtQuick::PathText::y
2641
2642 The vertical position of the PathText's baseline.
2643
2644 \note This property refers to the position of the baseline of the text, not the top of its bounding box. This may
2645 cause some confusion, e.g. when using the PathText with Qt Quick Shapes. See \l FontMetrics for information on how to
2646 get the ascent of a font, which can be used to translate the text into the expected position.
2647*/
2648
2649/*!
2650 \qmlproperty string QtQuick::PathText::text
2651
2652 The text for which this PathText should contain the outlines.
2653*/
2654
2655/*!
2656 \qmlproperty string QtQuick::PathText::font.family
2657
2658 Sets the family name of the font.
2659
2660 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
2661 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
2662 If the family isn't available a family will be set using the font matching algorithm.
2663*/
2664
2665/*!
2666 \qmlproperty string QtQuick::PathText::font.styleName
2667
2668 Sets the style name of the font.
2669
2670 The style name is case insensitive. If set, the font will be matched against style name instead
2671 of the font properties \l font.weight, \l font.bold and \l font.italic.
2672*/
2673
2674/*!
2675 \qmlproperty bool QtQuick::PathText::font.bold
2676
2677 Sets whether the font weight is bold.
2678*/
2679
2680/*!
2681 \qmlproperty int QtQuick::PathText::font.weight
2682
2683 Sets the font's weight.
2684
2685 The weight can be one of:
2686
2687 \value Font.Thin 100
2688 \value Font.ExtraLight 200
2689 \value Font.Light 300
2690 \value Font.Normal 400 (default)
2691 \value Font.Medium 500
2692 \value Font.DemiBold 600
2693 \value Font.Bold 700
2694 \value Font.ExtraBold 800
2695 \value Font.Black 900
2696
2697 \qml
2698 PathText { text: "Hello"; font.weight: Font.DemiBold }
2699 \endqml
2700*/
2701
2702/*!
2703 \qmlproperty bool QtQuick::PathText::font.italic
2704
2705 Sets whether the font has an italic style.
2706*/
2707
2708/*!
2709 \qmlproperty bool QtQuick::PathText::font.underline
2710
2711 Sets whether the text is underlined.
2712*/
2713
2714/*!
2715 \qmlproperty bool QtQuick::PathText::font.strikeout
2716
2717 Sets whether the font has a strikeout style.
2718*/
2719
2720/*!
2721 \qmlproperty real QtQuick::PathText::font.pointSize
2722
2723 Sets the font size in points. The point size must be greater than zero.
2724*/
2725
2726/*!
2727 \qmlproperty int QtQuick::PathText::font.pixelSize
2728
2729 Sets the font size in pixels.
2730
2731 Using this function makes the font device dependent.
2732 Use \c pointSize to set the size of the font in a device independent manner.
2733*/
2734
2735/*!
2736 \qmlproperty real QtQuick::PathText::font.letterSpacing
2737
2738 Sets the letter spacing for the font.
2739
2740 Letter spacing changes the default spacing between individual letters in the font.
2741 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
2742*/
2743
2744/*!
2745 \qmlproperty real QtQuick::PathText::font.wordSpacing
2746
2747 Sets the word spacing for the font.
2748
2749 Word spacing changes the default spacing between individual words.
2750 A positive value increases the word spacing by a corresponding amount of pixels,
2751 while a negative value decreases the inter-word spacing accordingly.
2752*/
2753
2754/*!
2755 \qmlproperty enumeration QtQuick::PathText::font.capitalization
2756
2757 Sets the capitalization for the text.
2758
2759 \value Font.MixedCase no capitalization change is applied
2760 \value Font.AllUppercase alters the text to be rendered in all uppercase type
2761 \value Font.AllLowercase alters the text to be rendered in all lowercase type
2762 \value Font.SmallCaps alters the text to be rendered in small-caps type
2763 \value Font.Capitalize alters the text to be rendered with the first character of
2764 each word as an uppercase character
2765
2766 \qml
2767 PathText { text: "Hello"; font.capitalization: Font.AllLowercase }
2768 \endqml
2769*/
2770
2771/*!
2772 \qmlproperty bool QtQuick::PathText::font.kerning
2773
2774 Enables or disables the kerning OpenType feature when shaping the text. Disabling this may
2775 improve performance when creating or changing the text, at the expense of some cosmetic
2776 features. The default value is true.
2777
2778 \qml
2779 PathText { text: "OATS FLAVOUR WAY"; font.kerning: false }
2780 \endqml
2781*/
2782
2783/*!
2784 \qmlproperty bool QtQuick::PathText::font.preferShaping
2785
2786 Sometimes, a font will apply complex rules to a set of characters in order to
2787 display them correctly. In some writing systems, such as Brahmic scripts, this is
2788 required in order for the text to be legible, but in e.g. Latin script, it is merely
2789 a cosmetic feature. Setting the \c preferShaping property to false will disable all
2790 such features when they are not required, which will improve performance in most cases.
2791
2792 The default value is true.
2793
2794 \qml
2795 PathText { text: "Some text"; font.preferShaping: false }
2796 \endqml
2797*/
2798
2799/*!
2800 \qmlproperty object QtQuick::PathText::font.features
2801 \since 6.6
2802
2803 \include qquicktext.cpp qml-font-features
2804*/
2805void QQuickPathText::updatePath() const
2806{
2807 if (!_path.isEmpty())
2808 return;
2809
2810 _path.addText(x: 0.0, y: 0.0, f: _font, text: _text);
2811
2812 // Account for distance from baseline to top, since addText() takes baseline position
2813 QRectF brect = _path.boundingRect();
2814 _path.translate(dx: _x, dy: _y - brect.y());
2815}
2816
2817void QQuickPathText::addToPath(QPainterPath &path)
2818{
2819 if (_text.isEmpty())
2820 return;
2821 updatePath();
2822 path.addPath(path: _path);
2823}
2824
2825QT_END_NAMESPACE
2826
2827#include "moc_qquickpath_p.cpp"
2828

source code of qtdeclarative/src/quick/util/qquickpath.cpp