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