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