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