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 {PathView showing three Qt
1219 logo items with names: John Smith and Jane Doe small and faded
1220 at top, Bill Jones large and opaque at center}
1221 \li
1222 \snippet qml/pathview/pathattributes.qml 0
1223 (see the PathView documentation for the specification of ContactModel.qml
1224 used for ContactModel above.)
1225 \endtable
1226
1227
1228 \sa Path
1229*/
1230
1231/*!
1232 \class QQuickPathAttribute
1233 \inmodule QtQuick
1234 \internal
1235*/
1236
1237/*!
1238 \qmlproperty string QtQuick::PathAttribute::name
1239 This property holds the name of the attribute to change.
1240
1241 This attribute will be available to the delegate as PathView.<name>
1242
1243 Note that using an existing Item property name such as "opacity" as an
1244 attribute is allowed. This is because path attributes add a new
1245 \l{Attached Properties and Attached Signal Handlers} {Attached Property}
1246 which in no way clashes with existing properties.
1247*/
1248
1249/*!
1250 the name of the attribute to change.
1251*/
1252
1253QString QQuickPathAttribute::name() const
1254{
1255 return _name;
1256}
1257
1258void QQuickPathAttribute::setName(const QString &name)
1259{
1260 if (_name == name)
1261 return;
1262 _name = name;
1263 emit nameChanged();
1264}
1265
1266/*!
1267 \qmlproperty real QtQuick::PathAttribute::value
1268 This property holds the value for the attribute.
1269
1270 The value specified can be used to influence the visual appearance
1271 of an item along the path. For example, the following Path specifies
1272 an attribute named \e itemRotation, which has the value \e 0 at the
1273 beginning of the path, and the value 90 at the end of the path.
1274
1275 \qml
1276 Path {
1277 startX: 0
1278 startY: 0
1279 PathAttribute { name: "itemRotation"; value: 0 }
1280 PathLine { x: 100; y: 100 }
1281 PathAttribute { name: "itemRotation"; value: 90 }
1282 }
1283 \endqml
1284
1285 In our delegate, we can then bind the \e rotation property to the
1286 \l{Attached Properties and Attached Signal Handlers} {Attached Property}
1287 \e PathView.itemRotation created for this attribute.
1288
1289 \qml
1290 Rectangle {
1291 width: 10; height: 10
1292 rotation: PathView.itemRotation
1293 }
1294 \endqml
1295
1296 As each item is positioned along the path, it will be rotated accordingly:
1297 an item at the beginning of the path with be not be rotated, an item at
1298 the end of the path will be rotated 90 degrees, and an item mid-way along
1299 the path will be rotated 45 degrees.
1300*/
1301
1302/*!
1303 the new value of the attribute.
1304*/
1305qreal QQuickPathAttribute::value() const
1306{
1307 return _value;
1308}
1309
1310void QQuickPathAttribute::setValue(qreal value)
1311{
1312 if (_value != value) {
1313 _value = value;
1314 emit valueChanged();
1315 emit changed();
1316 }
1317}
1318
1319/****************************************************************************/
1320
1321/*!
1322 \qmltype PathLine
1323 \nativetype QQuickPathLine
1324 \inqmlmodule QtQuick
1325 \ingroup qtquick-animation-paths
1326 \brief Defines a straight line.
1327
1328 The example below creates a path consisting of a straight line from
1329 0,100 to 200,100:
1330
1331 \qml
1332 Path {
1333 startX: 0; startY: 100
1334 PathLine { x: 200; y: 100 }
1335 }
1336 \endqml
1337
1338 \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline, PathRectangle
1339*/
1340
1341/*!
1342 \qmlproperty real QtQuick::PathLine::x
1343 \qmlproperty real QtQuick::PathLine::y
1344
1345 Defines the end point of the line.
1346
1347 \sa relativeX, relativeY
1348*/
1349
1350/*!
1351 \qmlproperty real QtQuick::PathLine::relativeX
1352 \qmlproperty real QtQuick::PathLine::relativeY
1353
1354 Defines the end point of the line relative to its start.
1355
1356 If both a relative and absolute end position are specified for a single axis, the relative
1357 position will be used.
1358
1359 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1360 and an absolute y.
1361
1362 \sa x, y
1363*/
1364
1365inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
1366{
1367 QQuickCurve *curve = data.curves.at(data.index);
1368 bool isEnd = data.index == data.curves.size() - 1;
1369 return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
1370 curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
1371}
1372
1373void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
1374{
1375 path.lineTo(positionForCurve(data, path.currentPosition()));
1376}
1377
1378/****************************************************************************/
1379
1380/*!
1381 \qmltype PathMove
1382 \nativetype QQuickPathMove
1383 \inqmlmodule QtQuick
1384 \ingroup qtquick-animation-paths
1385 \brief Moves the Path's position.
1386
1387 The example below creates a path consisting of two horizontal lines with
1388 some empty space between them. All three segments have a width of 100:
1389
1390 \qml
1391 Path {
1392 startX: 0; startY: 100
1393 PathLine { relativeX: 100; y: 100 }
1394 PathMove { relativeX: 100; y: 100 }
1395 PathLine { relativeX: 100; y: 100 }
1396 }
1397 \endqml
1398
1399 \note PathMove should not be used in a Path associated with a PathView. Use
1400 PathLine instead. For ShapePath however it is important to distinguish
1401 between the operations of drawing a straight line and moving the path
1402 position without drawing anything.
1403
1404 \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathLine
1405*/
1406
1407/*!
1408 \qmlproperty real QtQuick::PathMove::x
1409 \qmlproperty real QtQuick::PathMove::y
1410
1411 Defines the position to move to.
1412
1413 \sa relativeX, relativeY
1414*/
1415
1416/*!
1417 \qmlproperty real QtQuick::PathMove::relativeX
1418 \qmlproperty real QtQuick::PathMove::relativeY
1419
1420 Defines the position to move to relative to its start.
1421
1422 If both a relative and absolute end position are specified for a single axis, the relative
1423 position will be used.
1424
1425 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1426 and an absolute y.
1427
1428 \sa x, y
1429*/
1430
1431void QQuickPathMove::addToPath(QPainterPath &path, const QQuickPathData &data)
1432{
1433 path.moveTo(positionForCurve(data, path.currentPosition()));
1434}
1435
1436/****************************************************************************/
1437
1438/*!
1439 \qmltype PathQuad
1440 \nativetype QQuickPathQuad
1441 \inqmlmodule QtQuick
1442 \ingroup qtquick-animation-paths
1443 \brief Defines a quadratic Bezier curve with a control point.
1444
1445 The following QML produces the path shown below:
1446 \table
1447 \row
1448 \li \image declarative-pathquad.png {Quadratic bezier curve forming
1449 a parabola}
1450 \li
1451 \qml
1452 Path {
1453 startX: 0; startY: 0
1454 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
1455 }
1456 \endqml
1457 \endtable
1458
1459 \sa Path, PathCubic, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg
1460*/
1461
1462/*!
1463 \class QQuickPathQuad
1464 \inmodule QtQuick
1465 \internal
1466*/
1467
1468/*!
1469 \qmlproperty real QtQuick::PathQuad::x
1470 \qmlproperty real QtQuick::PathQuad::y
1471
1472 Defines the end point of the curve.
1473
1474 \sa relativeX, relativeY
1475*/
1476
1477/*!
1478 \qmlproperty real QtQuick::PathQuad::relativeX
1479 \qmlproperty real QtQuick::PathQuad::relativeY
1480
1481 Defines the end point of the curve relative to its start.
1482
1483 If both a relative and absolute end position are specified for a single axis, the relative
1484 position will be used.
1485
1486 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1487 and an absolute y.
1488
1489 \sa x, y
1490*/
1491
1492/*!
1493 \qmlproperty real QtQuick::PathQuad::controlX
1494 \qmlproperty real QtQuick::PathQuad::controlY
1495
1496 Defines the position of the control point.
1497*/
1498
1499/*!
1500 the x position of the control point.
1501*/
1502qreal QQuickPathQuad::controlX() const
1503{
1504 return _controlX;
1505}
1506
1507void QQuickPathQuad::setControlX(qreal x)
1508{
1509 if (_controlX != x) {
1510 _controlX = x;
1511 emit controlXChanged();
1512 emit changed();
1513 }
1514}
1515
1516
1517/*!
1518 the y position of the control point.
1519*/
1520qreal QQuickPathQuad::controlY() const
1521{
1522 return _controlY;
1523}
1524
1525void QQuickPathQuad::setControlY(qreal y)
1526{
1527 if (_controlY != y) {
1528 _controlY = y;
1529 emit controlYChanged();
1530 emit changed();
1531 }
1532}
1533
1534/*!
1535 \qmlproperty real QtQuick::PathQuad::relativeControlX
1536 \qmlproperty real QtQuick::PathQuad::relativeControlY
1537
1538 Defines the position of the control point relative to the curve's start.
1539
1540 If both a relative and absolute control position are specified for a single axis, the relative
1541 position will be used.
1542
1543 Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1544 and an absolute control y.
1545
1546 \sa controlX, controlY
1547*/
1548
1549qreal QQuickPathQuad::relativeControlX() const
1550{
1551 return _relativeControlX;
1552}
1553
1554void QQuickPathQuad::setRelativeControlX(qreal x)
1555{
1556 if (!_relativeControlX.isValid() || _relativeControlX != x) {
1557 _relativeControlX = x;
1558 emit relativeControlXChanged();
1559 emit changed();
1560 }
1561}
1562
1563bool QQuickPathQuad::hasRelativeControlX()
1564{
1565 return _relativeControlX.isValid();
1566}
1567
1568qreal QQuickPathQuad::relativeControlY() const
1569{
1570 return _relativeControlY;
1571}
1572
1573void QQuickPathQuad::setRelativeControlY(qreal y)
1574{
1575 if (!_relativeControlY.isValid() || _relativeControlY != y) {
1576 _relativeControlY = y;
1577 emit relativeControlYChanged();
1578 emit changed();
1579 }
1580}
1581
1582bool QQuickPathQuad::hasRelativeControlY()
1583{
1584 return _relativeControlY.isValid();
1585}
1586
1587void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
1588{
1589 const QPointF &prevPoint = path.currentPosition();
1590 QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1591 hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1592 path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1593}
1594
1595/****************************************************************************/
1596
1597/*!
1598 \qmltype PathCubic
1599 \nativetype QQuickPathCubic
1600 \inqmlmodule QtQuick
1601 \ingroup qtquick-animation-paths
1602 \brief Defines a cubic Bezier curve with two control points.
1603
1604 The following QML produces the path shown below:
1605 \table
1606 \row
1607 \li \image declarative-pathcubic.png {Cubic curve}
1608 \li
1609 \qml
1610 Path {
1611 startX: 20; startY: 0
1612 PathCubic {
1613 x: 180; y: 0
1614 control1X: -10; control1Y: 90
1615 control2X: 210; control2Y: 90
1616 }
1617 }
1618 \endqml
1619 \endtable
1620
1621 \sa Path, PathQuad, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg, PathRectangle
1622*/
1623
1624/*!
1625 \qmlproperty real QtQuick::PathCubic::x
1626 \qmlproperty real QtQuick::PathCubic::y
1627
1628 Defines the end point of the curve.
1629
1630 \sa relativeX, relativeY
1631*/
1632
1633/*!
1634 \qmlproperty real QtQuick::PathCubic::relativeX
1635 \qmlproperty real QtQuick::PathCubic::relativeY
1636
1637 Defines the end point of the curve relative to its start.
1638
1639 If both a relative and absolute end position are specified for a single axis, the relative
1640 position will be used.
1641
1642 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1643 and an absolute y.
1644
1645 \sa x, y
1646*/
1647
1648/*!
1649 \qmlproperty real QtQuick::PathCubic::control1X
1650 \qmlproperty real QtQuick::PathCubic::control1Y
1651
1652 Defines the position of the first control point.
1653*/
1654qreal QQuickPathCubic::control1X() const
1655{
1656 return _control1X;
1657}
1658
1659void QQuickPathCubic::setControl1X(qreal x)
1660{
1661 if (_control1X != x) {
1662 _control1X = x;
1663 emit control1XChanged();
1664 emit changed();
1665 }
1666}
1667
1668qreal QQuickPathCubic::control1Y() const
1669{
1670 return _control1Y;
1671}
1672
1673void QQuickPathCubic::setControl1Y(qreal y)
1674{
1675 if (_control1Y != y) {
1676 _control1Y = y;
1677 emit control1YChanged();
1678 emit changed();
1679 }
1680}
1681
1682/*!
1683 \qmlproperty real QtQuick::PathCubic::control2X
1684 \qmlproperty real QtQuick::PathCubic::control2Y
1685
1686 Defines the position of the second control point.
1687*/
1688qreal QQuickPathCubic::control2X() const
1689{
1690 return _control2X;
1691}
1692
1693void QQuickPathCubic::setControl2X(qreal x)
1694{
1695 if (_control2X != x) {
1696 _control2X = x;
1697 emit control2XChanged();
1698 emit changed();
1699 }
1700}
1701
1702qreal QQuickPathCubic::control2Y() const
1703{
1704 return _control2Y;
1705}
1706
1707void QQuickPathCubic::setControl2Y(qreal y)
1708{
1709 if (_control2Y != y) {
1710 _control2Y = y;
1711 emit control2YChanged();
1712 emit changed();
1713 }
1714}
1715
1716/*!
1717 \qmlproperty real QtQuick::PathCubic::relativeControl1X
1718 \qmlproperty real QtQuick::PathCubic::relativeControl1Y
1719 \qmlproperty real QtQuick::PathCubic::relativeControl2X
1720 \qmlproperty real QtQuick::PathCubic::relativeControl2Y
1721
1722 Defines the positions of the control points relative to the curve's start.
1723
1724 If both a relative and absolute control position are specified for a control point's axis, the relative
1725 position will be used.
1726
1727 Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1728 and an absolute control1 y.
1729
1730 \sa control1X, control1Y, control2X, control2Y
1731*/
1732
1733qreal QQuickPathCubic::relativeControl1X() const
1734{
1735 return _relativeControl1X;
1736}
1737
1738void QQuickPathCubic::setRelativeControl1X(qreal x)
1739{
1740 if (!_relativeControl1X.isValid() || _relativeControl1X != x) {
1741 _relativeControl1X = x;
1742 emit relativeControl1XChanged();
1743 emit changed();
1744 }
1745}
1746
1747bool QQuickPathCubic::hasRelativeControl1X()
1748{
1749 return _relativeControl1X.isValid();
1750}
1751
1752qreal QQuickPathCubic::relativeControl1Y() const
1753{
1754 return _relativeControl1Y;
1755}
1756
1757void QQuickPathCubic::setRelativeControl1Y(qreal y)
1758{
1759 if (!_relativeControl1Y.isValid() || _relativeControl1Y != y) {
1760 _relativeControl1Y = y;
1761 emit relativeControl1YChanged();
1762 emit changed();
1763 }
1764}
1765
1766bool QQuickPathCubic::hasRelativeControl1Y()
1767{
1768 return _relativeControl1Y.isValid();
1769}
1770
1771qreal QQuickPathCubic::relativeControl2X() const
1772{
1773 return _relativeControl2X;
1774}
1775
1776void QQuickPathCubic::setRelativeControl2X(qreal x)
1777{
1778 if (!_relativeControl2X.isValid() || _relativeControl2X != x) {
1779 _relativeControl2X = x;
1780 emit relativeControl2XChanged();
1781 emit changed();
1782 }
1783}
1784
1785bool QQuickPathCubic::hasRelativeControl2X()
1786{
1787 return _relativeControl2X.isValid();
1788}
1789
1790qreal QQuickPathCubic::relativeControl2Y() const
1791{
1792 return _relativeControl2Y;
1793}
1794
1795void QQuickPathCubic::setRelativeControl2Y(qreal y)
1796{
1797 if (!_relativeControl2Y.isValid() || _relativeControl2Y != y) {
1798 _relativeControl2Y = y;
1799 emit relativeControl2YChanged();
1800 emit changed();
1801 }
1802}
1803
1804bool QQuickPathCubic::hasRelativeControl2Y()
1805{
1806 return _relativeControl2Y.isValid();
1807}
1808
1809void QQuickPathCubic::addToPath(QPainterPath &path, const QQuickPathData &data)
1810{
1811 const QPointF &prevPoint = path.currentPosition();
1812 QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1813 hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1814 QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1815 hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1816 path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1817}
1818
1819/****************************************************************************/
1820
1821/*!
1822 \qmltype PathCurve
1823 \nativetype QQuickPathCatmullRomCurve
1824 \inqmlmodule QtQuick
1825 \ingroup qtquick-animation-paths
1826 \brief Defines a point on a Catmull-Rom curve.
1827
1828 PathCurve provides an easy way to specify a curve passing directly through a set of points.
1829 Typically multiple PathCurves are used in a series, as the following example demonstrates:
1830
1831 \snippet qml/path/basiccurve.qml 0
1832
1833 This example produces the following path (with the starting point and PathCurve points
1834 highlighted in red):
1835
1836 \image declarative-pathcurve.png {Catmull-Rom curve passing through
1837 five red highlighted points forming a wave pattern}
1838
1839 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1840*/
1841
1842/*!
1843 \qmlproperty real QtQuick::PathCurve::x
1844 \qmlproperty real QtQuick::PathCurve::y
1845
1846 Defines the end point of the curve.
1847
1848 \sa relativeX, relativeY
1849*/
1850
1851/*!
1852 \qmlproperty real QtQuick::PathCurve::relativeX
1853 \qmlproperty real QtQuick::PathCurve::relativeY
1854
1855 Defines the end point of the curve relative to its start.
1856
1857 If both a relative and absolute end position are specified for a single axis, the relative
1858 position will be used.
1859
1860 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1861 and an absolute y.
1862
1863 \sa x, y
1864*/
1865
1866inline QPointF previousPathPosition(const QPainterPath &path)
1867{
1868 int count = path.elementCount();
1869 if (count < 1)
1870 return QPointF();
1871
1872 int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1873 return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1874}
1875
1876void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathData &data)
1877{
1878 //here we convert catmull-rom spline to bezier for use in QPainterPath.
1879 //basic conversion algorithm:
1880 // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1881 //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1882 //calculations for each point use a moving window of 4 points
1883 // (previous 2 points + current point + next point)
1884 QPointF prevFar, prev, point, next;
1885
1886 //get previous points
1887 int index = data.index - 1;
1888 QQuickCurve *curve = index == -1 ? 0 : data.curves.at(index);
1889 if (qobject_cast<QQuickPathCatmullRomCurve*>(curve)) {
1890 prev = path.currentPosition();
1891 prevFar = previousPathPosition(path);
1892 } else {
1893 prev = path.currentPosition();
1894 bool prevFarSet = false;
1895 if (index == -1 && data.curves.size() > 1) {
1896 if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.size()-1))) {
1897 //TODO: profile and optimize
1898 QPointF pos = prev;
1899 QQuickPathData loopData;
1900 loopData.endPoint = data.endPoint;
1901 loopData.curves = data.curves;
1902 for (int i = data.index; i < data.curves.size(); ++i) {
1903 loopData.index = i;
1904 pos = positionForCurve(loopData, pos);
1905 if (i == data.curves.size()-2)
1906 prevFar = pos;
1907 }
1908 if (pos == QPointF(path.elementAt(0))) {
1909 //this is a closed path starting and ending with catmull-rom segments.
1910 //we try to smooth the join point
1911 prevFarSet = true;
1912 }
1913 }
1914 }
1915 if (!prevFarSet)
1916 prevFar = prev;
1917 }
1918
1919 //get current point
1920 point = positionForCurve(data, path.currentPosition());
1921
1922 //get next point
1923 index = data.index + 1;
1924 if (index < data.curves.size() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
1925 QQuickPathData nextData;
1926 nextData.index = index;
1927 nextData.endPoint = data.endPoint;
1928 nextData.curves = data.curves;
1929 next = positionForCurve(nextData, point);
1930 } else {
1931 if (point == QPointF(path.elementAt(0)) && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(0)) && path.elementCount() >= 3) {
1932 //this is a closed path starting and ending with catmull-rom segments.
1933 //we try to smooth the join point
1934 next = QPointF(path.elementAt(3)); //the first catmull-rom point
1935 } else
1936 next = point;
1937 }
1938
1939 /*
1940 full conversion matrix (inverse bezier * catmull-rom):
1941 0.000, 1.000, 0.000, 0.000,
1942 -0.167, 1.000, 0.167, 0.000,
1943 0.000, 0.167, 1.000, -0.167,
1944 0.000, 0.000, 1.000, 0.000
1945
1946 conversion doesn't require full matrix multiplication,
1947 so below we simplify
1948 */
1949 QPointF control1(prevFar.x() * qreal(-0.167) +
1950 prev.x() +
1951 point.x() * qreal(0.167),
1952 prevFar.y() * qreal(-0.167) +
1953 prev.y() +
1954 point.y() * qreal(0.167));
1955
1956 QPointF control2(prev.x() * qreal(0.167) +
1957 point.x() +
1958 next.x() * qreal(-0.167),
1959 prev.y() * qreal(0.167) +
1960 point.y() +
1961 next.y() * qreal(-0.167));
1962
1963 path.cubicTo(control1, control2, point);
1964}
1965
1966/****************************************************************************/
1967
1968/*!
1969 \qmltype PathArc
1970 \nativetype QQuickPathArc
1971 \inqmlmodule QtQuick
1972 \ingroup qtquick-animation-paths
1973 \brief Defines an arc with the given radius.
1974
1975 PathArc provides a simple way of specifying an arc that ends at a given position
1976 and uses the specified radius. It is modeled after the SVG elliptical arc command.
1977
1978 The following QML produces the path shown below:
1979 \table
1980 \row
1981 \li \image declarative-patharc.png {Nearly complete circular arc
1982 drawn from start point to end point}
1983 \li \snippet qml/path/basicarc.qml 0
1984 \endtable
1985
1986 Note that a single PathArc cannot be used to specify a circle. Instead, you can
1987 use two PathArc elements, each specifying half of the circle.
1988
1989 \sa Path, PathLine, PathQuad, PathCubic, PathAngleArc, PathCurve, PathSvg
1990*/
1991
1992/*!
1993 \qmlproperty real QtQuick::PathArc::x
1994 \qmlproperty real QtQuick::PathArc::y
1995
1996 Defines the end point of the arc.
1997
1998 \sa relativeX, relativeY
1999*/
2000
2001/*!
2002 \qmlproperty real QtQuick::PathArc::relativeX
2003 \qmlproperty real QtQuick::PathArc::relativeY
2004
2005 Defines the end point of the arc relative to its start.
2006
2007 If both a relative and absolute end position are specified for a single axis, the relative
2008 position will be used.
2009
2010 Relative and absolute positions can be mixed, for example it is valid to set a relative x
2011 and an absolute y.
2012
2013 \sa x, y
2014*/
2015
2016/*!
2017 \qmlproperty real QtQuick::PathArc::radiusX
2018 \qmlproperty real QtQuick::PathArc::radiusY
2019
2020 Defines the radius of the arc.
2021
2022 The following QML demonstrates how different radius values can be used to change
2023 the shape of the arc:
2024 \table
2025 \row
2026 \li \image declarative-arcradius.png {Four arc varieties with different
2027 radii arranged sequentially}
2028 \li \snippet qml/path/arcradius.qml 0
2029 \endtable
2030*/
2031
2032qreal QQuickPathArc::radiusX() const
2033{
2034 return _radiusX;
2035}
2036
2037void QQuickPathArc::setRadiusX(qreal radius)
2038{
2039 if (_radiusX == radius)
2040 return;
2041
2042 _radiusX = radius;
2043 emit radiusXChanged();
2044 emit changed();
2045}
2046
2047qreal QQuickPathArc::radiusY() const
2048{
2049 return _radiusY;
2050}
2051
2052void QQuickPathArc::setRadiusY(qreal radius)
2053{
2054 if (_radiusY == radius)
2055 return;
2056
2057 _radiusY = radius;
2058 emit radiusYChanged();
2059 emit changed();
2060}
2061
2062/*!
2063 \qmlproperty bool QtQuick::PathArc::useLargeArc
2064 Whether to use a large arc as defined by the arc points.
2065
2066 Given fixed start and end positions, radius, and direction,
2067 there are two possible arcs that can fit the data. useLargeArc
2068 is used to distinguish between these. For example, the following
2069 QML can produce either of the two illustrated arcs below by
2070 changing the value of useLargeArc.
2071
2072 \table
2073 \row
2074 \li \image declarative-largearc.png {Two arcs with same endpoints:
2075 small arc when useLargeArc is false, large arc when true}
2076 \li \snippet qml/path/largearc.qml 0
2077 \endtable
2078
2079 The default value is false.
2080*/
2081
2082bool QQuickPathArc::useLargeArc() const
2083{
2084 return _useLargeArc;
2085}
2086
2087void QQuickPathArc::setUseLargeArc(bool largeArc)
2088{
2089 if (_useLargeArc == largeArc)
2090 return;
2091
2092 _useLargeArc = largeArc;
2093 emit useLargeArcChanged();
2094 emit changed();
2095}
2096
2097/*!
2098 \qmlproperty enumeration QtQuick::PathArc::direction
2099
2100 Defines the direction of the arc. Possible values are
2101 PathArc.Clockwise (default) and PathArc.Counterclockwise.
2102
2103 The following QML can produce either of the two illustrated arcs below
2104 by changing the value of direction.
2105 \table
2106 \row
2107 \li \image declarative-arcdirection.png {Two arcs with same endpoints:
2108 one curving clockwise, one curving counterclockwise}
2109 \li \snippet qml/path/arcdirection.qml 0
2110 \endtable
2111
2112 \sa useLargeArc
2113*/
2114
2115QQuickPathArc::ArcDirection QQuickPathArc::direction() const
2116{
2117 return _direction;
2118}
2119
2120void QQuickPathArc::setDirection(ArcDirection direction)
2121{
2122 if (_direction == direction)
2123 return;
2124
2125 _direction = direction;
2126 emit directionChanged();
2127 emit changed();
2128}
2129
2130/*!
2131 \qmlproperty real QtQuick::PathArc::xAxisRotation
2132
2133 Defines the rotation of the arc, in degrees. The default value is 0.
2134
2135 An arc is a section of circles or ellipses. Given the radius and the start
2136 and end points, there are two ellipses that connect the points. This
2137 property defines the rotation of the X axis of these ellipses.
2138
2139 \note The value is only useful when the x and y radius differ, meaning the
2140 arc is a section of ellipses.
2141
2142 The following QML demonstrates how different radius values can be used to change
2143 the shape of the arc:
2144 \table
2145 \row
2146 \li \image declarative-arcrotation.png {Two elliptical arcs: one with
2147 0 degree rotation, one rotated 45 degrees}
2148 \li \snippet qml/path/arcrotation.qml 0
2149 \endtable
2150*/
2151
2152qreal QQuickPathArc::xAxisRotation() const
2153{
2154 return _xAxisRotation;
2155}
2156
2157void QQuickPathArc::setXAxisRotation(qreal rotation)
2158{
2159 if (_xAxisRotation == rotation)
2160 return;
2161
2162 _xAxisRotation = rotation;
2163 emit xAxisRotationChanged();
2164 emit changed();
2165}
2166
2167void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
2168{
2169 const QPointF &startPoint = path.currentPosition();
2170 const QPointF &endPoint = positionForCurve(data, startPoint);
2171 QQuickSvgParser::pathArc(path,
2172 _radiusX,
2173 _radiusY,
2174 _xAxisRotation,
2175 _useLargeArc,
2176 _direction == Clockwise ? 1 : 0,
2177 endPoint.x(),
2178 endPoint.y(),
2179 startPoint.x(), startPoint.y());
2180}
2181
2182/****************************************************************************/
2183
2184/*!
2185 \qmltype PathAngleArc
2186 \nativetype QQuickPathAngleArc
2187 \inqmlmodule QtQuick
2188 \ingroup qtquick-animation-paths
2189 \brief Defines an arc with the given radii and center.
2190
2191 PathAngleArc provides a simple way of specifying an arc. While PathArc is designed
2192 to work as part of a larger path (specifying start and end), PathAngleArc is designed
2193 to make a path where the arc is primary (such as a circular progress indicator) more intuitive.
2194
2195 \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg, PathArc, PathRectangle
2196*/
2197
2198/*!
2199 \qmlproperty real QtQuick::PathAngleArc::centerX
2200 \qmlproperty real QtQuick::PathAngleArc::centerY
2201
2202 Defines the center of the arc.
2203*/
2204
2205qreal QQuickPathAngleArc::centerX() const
2206{
2207 return _centerX;
2208}
2209
2210void QQuickPathAngleArc::setCenterX(qreal centerX)
2211{
2212 if (_centerX == centerX)
2213 return;
2214
2215 _centerX = centerX;
2216 emit centerXChanged();
2217 emit changed();
2218}
2219
2220qreal QQuickPathAngleArc::centerY() const
2221{
2222 return _centerY;
2223}
2224
2225void QQuickPathAngleArc::setCenterY(qreal centerY)
2226{
2227 if (_centerY == centerY)
2228 return;
2229
2230 _centerY = centerY;
2231 emit centerYChanged();
2232 emit changed();
2233}
2234
2235/*!
2236 \qmlproperty real QtQuick::PathAngleArc::radiusX
2237 \qmlproperty real QtQuick::PathAngleArc::radiusY
2238
2239 Defines the radii of the ellipse of which the arc is part.
2240*/
2241
2242qreal QQuickPathAngleArc::radiusX() const
2243{
2244 return _radiusX;
2245}
2246
2247void QQuickPathAngleArc::setRadiusX(qreal radius)
2248{
2249 if (_radiusX == radius)
2250 return;
2251
2252 _radiusX = radius;
2253 emit radiusXChanged();
2254 emit changed();
2255}
2256
2257qreal QQuickPathAngleArc::radiusY() const
2258{
2259 return _radiusY;
2260}
2261
2262void QQuickPathAngleArc::setRadiusY(qreal radius)
2263{
2264 if (_radiusY == radius)
2265 return;
2266
2267 _radiusY = radius;
2268 emit radiusYChanged();
2269 emit changed();
2270}
2271
2272/*!
2273 \qmlproperty real QtQuick::PathAngleArc::startAngle
2274
2275 Defines the start angle of the arc.
2276
2277 The start angle is reported clockwise, with zero degrees at the 3 o'clock position.
2278*/
2279
2280qreal QQuickPathAngleArc::startAngle() const
2281{
2282 return _startAngle;
2283}
2284
2285void QQuickPathAngleArc::setStartAngle(qreal angle)
2286{
2287 if (_startAngle == angle)
2288 return;
2289
2290 _startAngle = angle;
2291 emit startAngleChanged();
2292 emit changed();
2293}
2294
2295/*!
2296 \qmlproperty real QtQuick::PathAngleArc::sweepAngle
2297
2298 Defines the sweep angle of the arc.
2299
2300 The arc will begin at startAngle and continue sweepAngle degrees, with a value of 360
2301 resulting in a full circle. Positive numbers are clockwise and negative numbers are counterclockwise.
2302*/
2303
2304qreal QQuickPathAngleArc::sweepAngle() const
2305{
2306 return _sweepAngle;
2307}
2308
2309void QQuickPathAngleArc::setSweepAngle(qreal angle)
2310{
2311 if (_sweepAngle == angle)
2312 return;
2313
2314 _sweepAngle = angle;
2315 emit sweepAngleChanged();
2316 emit changed();
2317}
2318
2319/*!
2320 \qmlproperty bool QtQuick::PathAngleArc::moveToStart
2321
2322 Whether this element should be disconnected from the previous Path element (or startX/Y).
2323
2324 The default value is true. If set to false, the previous element's end-point
2325 (or startX/Y if PathAngleArc is the first element) will be connected to the arc's
2326 start-point with a straight line.
2327*/
2328
2329bool QQuickPathAngleArc::moveToStart() const
2330{
2331 return _moveToStart;
2332}
2333
2334void QQuickPathAngleArc::setMoveToStart(bool move)
2335{
2336 if (_moveToStart == move)
2337 return;
2338
2339 _moveToStart = move;
2340 emit moveToStartChanged();
2341 emit changed();
2342}
2343
2344void QQuickPathAngleArc::addToPath(QPainterPath &path, const QQuickPathData &)
2345{
2346 qreal x = _centerX - _radiusX;
2347 qreal y = _centerY - _radiusY;
2348 qreal width = _radiusX * 2;
2349 qreal height = _radiusY * 2;
2350 if (_moveToStart)
2351 path.arcMoveTo(x, y, width, height, -_startAngle);
2352 path.arcTo(x, y, width, height, -_startAngle, -_sweepAngle);
2353}
2354
2355/****************************************************************************/
2356
2357/*!
2358 \qmltype PathSvg
2359 \nativetype QQuickPathSvg
2360 \inqmlmodule QtQuick
2361 \ingroup qtquick-animation-paths
2362 \brief Defines a path using an SVG path data string.
2363
2364 The following QML produces the path shown below:
2365 \table
2366 \row
2367 \li \image declarative-pathsvg.png {Triangle drawn using SVG path
2368 commands}
2369 \li
2370 \qml
2371 Path {
2372 startX: 50; startY: 50
2373 PathSvg { path: "L 150 50 L 100 150 z" }
2374 }
2375 \endqml
2376 \endtable
2377
2378 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve
2379*/
2380
2381/*!
2382 \qmlproperty string QtQuick::PathSvg::path
2383
2384 The SVG path data string specifying the path.
2385
2386 See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
2387 for more details on this format.
2388*/
2389
2390QString QQuickPathSvg::path() const
2391{
2392 return _path;
2393}
2394
2395void QQuickPathSvg::setPath(const QString &path)
2396{
2397 if (_path == path)
2398 return;
2399
2400 _path = path;
2401 emit pathChanged();
2402 emit changed();
2403}
2404
2405void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
2406{
2407 QQuickSvgParser::parsePathDataFast(_path, path);
2408}
2409
2410/****************************************************************************/
2411
2412/*!
2413 \qmltype PathRectangle
2414 \nativetype QQuickPathRectangle
2415 \inqmlmodule QtQuick
2416 \ingroup qtquick-animation-paths
2417 \brief Defines a rectangle with optionally rounded corners.
2418 \since QtQuick 6.8
2419
2420 PathRectangle provides an easy way to specify a rectangle, optionally with
2421 rounded or beveled corners. The API corresponds to that of the \l Rectangle
2422 item.
2423
2424 \image pathrectangle-bevel.png {Rectangle with beveled corners}
2425
2426 \snippet qml/pathrectangle/pathrectangle-bevel.qml shape
2427
2428 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg
2429*/
2430
2431/*!
2432 \qmlproperty real QtQuick::PathRectangle::x
2433 \qmlproperty real QtQuick::PathRectangle::y
2434
2435 Defines the top left corner of the rectangle.
2436
2437 Unless that corner is rounded, this will also be the start and end point of the path.
2438
2439 \sa relativeX, relativeY
2440*/
2441
2442/*!
2443 \qmlproperty real QtQuick::PathRectangle::relativeX
2444 \qmlproperty real QtQuick::PathRectangle::relativeY
2445
2446 Defines the top left corner of the rectangle relative to the path's start point.
2447
2448 If both a relative and absolute end position are specified for a single axis, the relative
2449 position will be used.
2450
2451 Relative and absolute positions can be mixed, for example it is valid to set a relative x
2452 and an absolute y.
2453
2454 \sa x, y
2455*/
2456
2457/*!
2458 \qmlproperty real QtQuick::PathRectangle::width
2459 \qmlproperty real QtQuick::PathRectangle::height
2460
2461 Defines the width and height of the rectangle.
2462
2463 \sa x, y
2464*/
2465
2466qreal QQuickPathRectangle::width() const
2467{
2468 return _width;
2469}
2470
2471void QQuickPathRectangle::setWidth(qreal width)
2472{
2473 if (_width == width)
2474 return;
2475
2476 _width = width;
2477 emit widthChanged();
2478 emit changed();
2479}
2480
2481qreal QQuickPathRectangle::height() const
2482{
2483 return _height;
2484}
2485
2486void QQuickPathRectangle::setHeight(qreal height)
2487{
2488 if (_height == height)
2489 return;
2490
2491 _height = height;
2492 emit heightChanged();
2493 emit changed();
2494}
2495
2496/*!
2497 \qmlproperty real QtQuick::PathRectangle::strokeAdjustment
2498
2499 This property defines the stroke width adjustment to the rectangle coordinates.
2500
2501 When used in a \l ShapePath with stroking enabled, the actual stroked rectangle will by default
2502 extend beyond the defined rectangle by half the stroke width on all sides. This is the expected
2503 behavior since the path defines the midpoint line of the stroking, and corresponds to QPainter
2504 and SVG rendering.
2505
2506 If one instead wants the defined rectangle to be the outer edge of the stroked rectangle, like
2507 a \l Rectangle item with a border, one can set strokeAdjustment to the stroke width. This will
2508 effectively shift all edges inwards by half the stroke width. Like in the following example:
2509
2510 \qml
2511 ShapePath {
2512 id: myRec
2513 fillColor: "white"
2514 strokeColor: "black"
2515 strokeWidth: 16
2516 joinStyle: ShapePath.MiterJoin
2517
2518 PathRectangle { x: 10; y: 10; width: 200; height: 100; strokeAdjustment: myRec.strokeWidth }
2519 }
2520 \endqml
2521*/
2522
2523qreal QQuickPathRectangle::strokeAdjustment() const
2524{
2525 return _strokeAdjustment;
2526}
2527
2528void QQuickPathRectangle::setStrokeAdjustment(qreal newStrokeAdjustment)
2529{
2530 if (_strokeAdjustment == newStrokeAdjustment)
2531 return;
2532 _strokeAdjustment = newStrokeAdjustment;
2533 emit strokeAdjustmentChanged();
2534 emit changed();
2535}
2536
2537/*!
2538 \include pathrectangle.qdocinc {radius-property} {QtQuick::PathRectangle}
2539
2540 The default value is \c 0.
2541*/
2542
2543qreal QQuickPathRectangle::radius() const
2544{
2545 return _extra.isAllocated() ? _extra->radius : 0;
2546}
2547
2548void QQuickPathRectangle::setRadius(qreal newRadius)
2549{
2550 if (_extra.value().radius == newRadius)
2551 return;
2552 _extra->radius = newRadius;
2553 emit radiusChanged();
2554 if (!(_extra->isRadiusSet(Qt::TopLeftCorner)))
2555 emit topLeftRadiusChanged();
2556 if (!(_extra->isRadiusSet(Qt::TopRightCorner)))
2557 emit topRightRadiusChanged();
2558 if (!(_extra->isRadiusSet(Qt::BottomLeftCorner)))
2559 emit bottomLeftRadiusChanged();
2560 if (!(_extra->isRadiusSet(Qt::BottomRightCorner)))
2561 emit bottomRightRadiusChanged();
2562 emit changed();
2563}
2564
2565/*!
2566 \include pathrectangle.qdocinc {radius-properties} {PathRectangle} {qml/pathrectangle/pathrectangle.qml} {shape}
2567*/
2568
2569qreal QQuickPathRectangle::cornerRadius(Qt::Corner corner) const
2570{
2571 if (_extra.isAllocated())
2572 return (_extra->isRadiusSet(corner)) ? _extra->cornerRadii[corner] : _extra->radius;
2573 else
2574 return 0;
2575}
2576
2577void QQuickPathRectangle::setCornerRadius(Qt::Corner corner, qreal newCornerRadius)
2578{
2579 if (_extra.value().cornerRadii[corner] == newCornerRadius
2580 && (_extra->isRadiusSet(corner)))
2581 return;
2582 _extra->cornerRadii[corner] = newCornerRadius;
2583 _extra->cornerProperties |= (1 << corner);
2584
2585 emitCornerRadiusChanged(corner);
2586}
2587
2588void QQuickPathRectangle::resetCornerRadius(Qt::Corner corner)
2589{
2590 if (!_extra.isAllocated() || !(_extra->isRadiusSet(corner)))
2591 return;
2592 _extra->cornerProperties &= ~(1 << corner);
2593 emitCornerRadiusChanged(corner);
2594}
2595
2596void QQuickPathRectangle::emitCornerRadiusChanged(Qt::Corner corner)
2597{
2598 switch (corner) {
2599 case Qt::TopLeftCorner:
2600 emit topLeftRadiusChanged();
2601 break;
2602 case Qt::TopRightCorner:
2603 emit topRightRadiusChanged();
2604 break;
2605 case Qt::BottomLeftCorner:
2606 emit bottomLeftRadiusChanged();
2607 break;
2608 case Qt::BottomRightCorner:
2609 emit bottomRightRadiusChanged();
2610 break;
2611 }
2612 emit changed();
2613}
2614
2615/*!
2616 \include pathrectangle.qdocinc {bevel-property}
2617 {QtQuick::PathRectangle}{qml/pathrectangle/pathrectangle-bevel.qml}
2618 {shape}
2619 \since 6.10
2620*/
2621
2622bool QQuickPathRectangle::hasBevel() const
2623{
2624 return _extra.isAllocated() ? (_extra->cornerProperties & (1 << 8)) != 0 : false;
2625}
2626
2627void QQuickPathRectangle::setBevel(bool bevel)
2628{
2629 if (((_extra.value().cornerProperties & (1 << 8)) != 0) == bevel)
2630 return;
2631 if (bevel)
2632 _extra->cornerProperties |= (1 << 8);
2633 else
2634 _extra->cornerProperties &= ~(1 << 8);
2635
2636 emit bevelChanged();
2637 if (!(_extra->isBevelSet(Qt::TopLeftCorner)))
2638 emit topLeftBevelChanged();
2639 if (!(_extra->isBevelSet(Qt::TopRightCorner)))
2640 emit topRightBevelChanged();
2641 if (!(_extra->isBevelSet(Qt::BottomLeftCorner)))
2642 emit bottomLeftBevelChanged();
2643 if (!(_extra->isBevelSet(Qt::BottomRightCorner)))
2644 emit bottomRightBevelChanged();
2645 emit changed();
2646}
2647/*!
2648 \include pathrectangle.qdocinc {bevel-properties}
2649 {QtQuick::PathRectangle} {qml/pathrectangle/pathrectangle.qml} {shape}
2650*/
2651
2652bool QQuickPathRectangle::cornerBevel(Qt::Corner corner) const
2653{
2654 if (!_extra.isAllocated())
2655 return false;
2656 return _extra->isBevelSet(corner) || (_extra->cornerProperties & (1 << 8));
2657}
2658
2659void QQuickPathRectangle::setCornerBevel(Qt::Corner corner, bool newCornerBevel)
2660{
2661 if ((_extra.value().isBevelSet(corner)) == newCornerBevel)
2662 return;
2663 if (!newCornerBevel) {
2664 resetCornerBevel(corner);
2665 return;
2666 }
2667 _extra->cornerProperties |= (1 << (corner + 4));
2668 emitCornerBevelChanged(corner);
2669}
2670
2671void QQuickPathRectangle::resetCornerBevel(Qt::Corner corner)
2672{
2673 if (!_extra.isAllocated() || !(_extra->isBevelSet(corner)))
2674 return;
2675 _extra->cornerProperties &= ~(1 << (corner + 4));
2676 emitCornerBevelChanged(corner);
2677}
2678
2679void QQuickPathRectangle::emitCornerBevelChanged(Qt::Corner corner)
2680{
2681 switch (corner) {
2682 case Qt::TopLeftCorner:
2683 emit topLeftBevelChanged();
2684 break;
2685 case Qt::TopRightCorner:
2686 emit topRightBevelChanged();
2687 break;
2688 case Qt::BottomLeftCorner:
2689 emit bottomLeftBevelChanged();
2690 break;
2691 case Qt::BottomRightCorner:
2692 emit bottomRightBevelChanged();
2693 break;
2694 }
2695 emit changed();
2696}
2697
2698void QQuickPathRectangle::addToPath(QPainterPath &path, const QQuickPathData &data)
2699{
2700 QRectF rect(positionForCurve(data, path.currentPosition()), QSizeF(_width, _height));
2701
2702 qreal halfStroke = _strokeAdjustment * 0.5;
2703 rect.adjust(halfStroke, halfStroke, -halfStroke, -halfStroke);
2704 if (rect.isEmpty())
2705 return;
2706
2707 if (!_extra.isAllocated()) {
2708 // No rounded corners
2709 path.addRect(rect);
2710 } else {
2711 // Radii must not exceed half of the width or half of the height
2712 const qreal maxDiameter = qMin(rect.width(), rect.height());
2713 const qreal generalDiameter = qMax(qreal(0), qMin(maxDiameter, 2 * _extra->radius));
2714 auto effectiveDiameter = [&](Qt::Corner corner) {
2715 qreal radius = _extra->cornerRadii[corner];
2716 return (_extra->isRadiusSet(corner)) ? qMin(maxDiameter, 2 * radius) : generalDiameter;
2717 };
2718 const qreal diamTL = effectiveDiameter(Qt::TopLeftCorner);
2719 const qreal diamTR = effectiveDiameter(Qt::TopRightCorner);
2720 const qreal diamBL = effectiveDiameter(Qt::BottomLeftCorner);
2721 const qreal diamBR = effectiveDiameter(Qt::BottomRightCorner);
2722
2723 path.moveTo(rect.left() + diamTL * 0.5, rect.top());
2724 if (diamTR) {
2725 if (!cornerBevel(Qt::TopRightCorner)) {
2726 // Rounded corners.
2727 path.arcTo(QRectF(QPointF(rect.right() - diamTR, rect.top()), QSizeF(diamTR, diamTR)), 90, -90);
2728 } else {
2729 // Beveled corners.
2730 path.lineTo(QPointF(rect.right() - diamTR * 0.5, rect.top()));
2731 path.lineTo(QPointF(rect.right(), rect.top() + diamTR * 0.5));
2732 }
2733 } else {
2734 // Regular corners.
2735 path.lineTo(rect.topRight());
2736 }
2737
2738 if (diamBR) {
2739 if (!cornerBevel(Qt::BottomRightCorner)) {
2740 path.arcTo(QRectF(QPointF(rect.right() - diamBR, rect.bottom() - diamBR), QSizeF(diamBR, diamBR)), 0, -90);
2741 } else {
2742 path.lineTo(QPointF(rect.right(), rect.bottom() - diamBR * 0.5));
2743 path.lineTo(QPointF(rect.right() - diamBR * 0.5, rect.bottom()));
2744 }
2745 } else {
2746 path.lineTo(rect.bottomRight());
2747 }
2748
2749 if (diamBL) {
2750 if (!cornerBevel(Qt::BottomLeftCorner)) {
2751 path.arcTo(QRectF(QPointF(rect.left(), rect.bottom() - diamBL), QSizeF(diamBL, diamBL)), 270, -90);
2752 } else {
2753 path.lineTo(QPointF(rect.left() + diamBL * 0.5, rect.bottom()));
2754 path.lineTo(QPointF(rect.left(), rect.bottom() - diamBL * 0.5));
2755 }
2756 } else {
2757 path.lineTo(rect.bottomLeft());
2758 }
2759
2760 if (diamTL) {
2761 if (!cornerBevel(Qt::TopLeftCorner))
2762 path.arcTo(QRectF(rect.topLeft(), QSizeF(diamTL, diamTL)), 180, -90);
2763 else
2764 path.lineTo(QPointF(rect.left(), rect.top() + diamTL * 0.5));
2765 } else {
2766 path.lineTo(rect.topLeft());
2767 }
2768 path.closeSubpath();
2769 }
2770}
2771
2772/****************************************************************************/
2773
2774/*!
2775 \qmltype PathPercent
2776 \nativetype QQuickPathPercent
2777 \inqmlmodule QtQuick
2778 \ingroup qtquick-animation-paths
2779 \brief Manipulates the way a path is interpreted.
2780
2781 PathPercent allows you to manipulate the spacing between items on a
2782 PathView's path. You can use it to bunch together items on part of
2783 the path, and spread them out on other parts of the path.
2784
2785 The examples below show the normal distribution of items along a path
2786 compared to a distribution which places 50% of the items along the
2787 PathLine section of the path.
2788 \table
2789 \row
2790 \li \image declarative-nopercent.png {PathView with items evenly
2791 distributed along an S-shaped path}
2792 \li
2793 \qml
2794 PathView {
2795 // ...
2796 Path {
2797 startX: 20; startY: 0
2798 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
2799 PathLine { x: 150; y: 80 }
2800 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
2801 }
2802 }
2803 \endqml
2804 \row
2805 \li \image declarative-percent.png {PathView with 50% of items
2806 bunched along the middle PathLine section}
2807 \li
2808 \qml
2809 PathView {
2810 // ...
2811 Path {
2812 startX: 20; startY: 0
2813 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
2814 PathPercent { value: 0.25 }
2815 PathLine { x: 150; y: 80 }
2816 PathPercent { value: 0.75 }
2817 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
2818 PathPercent { value: 1 }
2819 }
2820 }
2821 \endqml
2822 \endtable
2823
2824 \sa Path
2825*/
2826
2827/*!
2828 \qmlproperty real QtQuick::PathPercent::value
2829 The proportion of items that should be laid out up to this point.
2830
2831 This value should always be higher than the last value specified
2832 by a PathPercent at a previous position in the Path.
2833
2834 In the following example we have a Path made up of three PathLines.
2835 Normally, the items of the PathView would be laid out equally along
2836 this path, with an equal number of items per line segment. PathPercent
2837 allows us to specify that the first and third lines should each hold
2838 10% of the laid out items, while the second line should hold the remaining
2839 80%.
2840
2841 \qml
2842 PathView {
2843 // ...
2844 Path {
2845 startX: 0; startY: 0
2846 PathLine { x:100; y: 0; }
2847 PathPercent { value: 0.1 }
2848 PathLine { x: 100; y: 100 }
2849 PathPercent { value: 0.9 }
2850 PathLine { x: 100; y: 0 }
2851 PathPercent { value: 1 }
2852 }
2853 }
2854 \endqml
2855*/
2856
2857qreal QQuickPathPercent::value() const
2858{
2859 return _value;
2860}
2861
2862void QQuickPathPercent::setValue(qreal value)
2863{
2864 if (_value != value) {
2865 _value = value;
2866 emit valueChanged();
2867 emit changed();
2868 }
2869}
2870
2871/*!
2872 \qmltype PathPolyline
2873 \nativetype QQuickPathPolyline
2874 \inqmlmodule QtQuick
2875 \ingroup qtquick-animation-paths
2876 \brief Defines a polyline through a list of coordinates.
2877 \since QtQuick 2.14
2878
2879 The example below creates a triangular path consisting of four vertices
2880 on the edge of the containing Shape's bounding box.
2881 Through the containing shape's \l {QtQuick::Path::}{scale} property,
2882 the path will be rescaled together with its containing shape.
2883
2884 \qml
2885 PathPolyline {
2886 id: ppl
2887 path: [ Qt.point(0.0, 0.0),
2888 Qt.point(1.0, 0.0),
2889 Qt.point(0.5, 1.0),
2890 Qt.point(0.0, 0.0)
2891 ]
2892 }
2893 \endqml
2894
2895 \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
2896*/
2897
2898/*!
2899 \qmlproperty point QtQuick::PathPolyline::start
2900
2901 This read-only property contains the beginning of the polyline.
2902*/
2903
2904/*!
2905 \qmlproperty list<point> QtQuick::PathPolyline::path
2906
2907 This property defines the vertices of the polyline.
2908
2909 It can be a JS array of points constructed with \c Qt.point(),
2910 a QList or QVector of QPointF, or QPolygonF.
2911 If you are binding this to a custom property in some C++ object,
2912 QPolygonF is the most appropriate type to use.
2913*/
2914
2915QQuickPathPolyline::QQuickPathPolyline(QObject *parent) : QQuickCurve(parent)
2916{
2917}
2918
2919QVariant QQuickPathPolyline::path() const
2920{
2921 return QVariant::fromValue(m_path);
2922}
2923
2924void QQuickPathPolyline::setPath(const QVariant &path)
2925{
2926 if (path.userType() == QMetaType::QPolygonF) {
2927 setPath(path.value<QPolygonF>());
2928 } else if (path.canConvert<QList<QPointF>>()) {
2929 setPath(path.value<QList<QPointF>>());
2930 } else if (path.canConvert<QVariantList>()) {
2931 // This handles cases other than QPolygonF or QList<QPointF>, such as
2932 // QList<QPointF>, QList<QPoint>, QVariantList of QPointF, QVariantList of QPoint.
2933 QList<QPointF> pathList;
2934 QVariantList vl = path.value<QVariantList>();
2935 // If path is a QJSValue, e.g. coming from a JS array of Qt.point() in QML,
2936 // then path.value<QVariantList>() is inefficient.
2937 // TODO We should be able to iterate over path.value<QMetaSequence::Iterable>() eventually
2938 for (const QVariant &v : vl)
2939 pathList.append(v.toPointF());
2940 setPath(pathList);
2941 } else {
2942 qWarning() << "PathPolyline: path of type" << path.userType() << "not supported";
2943 }
2944}
2945
2946void QQuickPathPolyline::setPath(const QList<QPointF> &path)
2947{
2948 if (m_path != path) {
2949 const QPointF &oldStart = start();
2950 m_path = path;
2951 const QPointF &newStart = start();
2952 emit pathChanged();
2953 if (oldStart != newStart)
2954 emit startChanged();
2955 emit changed();
2956 }
2957}
2958
2959QPointF QQuickPathPolyline::start() const
2960{
2961 if (m_path.size()) {
2962 const QPointF &p = m_path.first();
2963 return p;
2964 }
2965 return QPointF();
2966}
2967
2968void QQuickPathPolyline::addToPath(QPainterPath &path, const QQuickPathData &/*data*/)
2969{
2970 if (m_path.size() < 2)
2971 return;
2972
2973 path.moveTo(m_path.first());
2974 for (int i = 1; i < m_path.size(); ++i)
2975 path.lineTo(m_path.at(i));
2976}
2977
2978
2979/*!
2980 \qmltype PathMultiline
2981 \nativetype QQuickPathMultiline
2982 \inqmlmodule QtQuick
2983 \ingroup qtquick-animation-paths
2984 \brief Defines a set of polylines through a list of lists of coordinates.
2985 \since QtQuick 2.14
2986
2987 This element allows to define a list of polylines at once.
2988 Each polyline in the list will be preceded by a \l{QPainterPath::moveTo}{moveTo}
2989 command, effectively making each polyline a separate one.
2990 The polylines in this list are supposed to be non-intersecting with each other.
2991 In any case, when used in conjunction with a \l ShapePath, the containing ShapePath's
2992 \l ShapePath::fillRule applies.
2993 That is, with the default \c OddEvenFill and non intersecting shapes, the largest shape in the list defines an area to be filled;
2994 areas where two shapes overlap are holes; areas where three shapes overlap are filled areas inside holes, etc.
2995
2996 The example below creates a high voltage symbol by adding each path
2997 of the symbol to the list of paths.
2998 The coordinates of the vertices are normalized, and through the containing shape's
2999 \l {QtQuick::Path::}{scale} property, the path will be rescaled together with its containing shape.
3000
3001 \qml
3002 PathMultiline {
3003 paths: [
3004 [Qt.point(0.5, 0.06698),
3005 Qt.point(1, 0.93301),
3006 Qt.point(0, 0.93301),
3007 Qt.point(0.5, 0.06698)],
3008
3009 [Qt.point(0.5, 0.12472),
3010 Qt.point(0.95, 0.90414),
3011 Qt.point(0.05, 0.90414),
3012 Qt.point(0.5, 0.12472)],
3013
3014 [Qt.point(0.47131, 0.32986),
3015 Qt.point(0.36229, 0.64789),
3016 Qt.point(0.51492, 0.58590),
3017 Qt.point(0.47563, 0.76014),
3018 Qt.point(0.44950, 0.73590),
3019 Qt.point(0.46292, 0.83392),
3020 Qt.point(0.52162, 0.75190),
3021 Qt.point(0.48531, 0.76230),
3022 Qt.point(0.57529, 0.53189),
3023 Qt.point(0.41261, 0.59189),
3024 Qt.point(0.53001, 0.32786),
3025 Qt.point(0.47131, 0.32986)]
3026 ]
3027 }
3028 \endqml
3029
3030 \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
3031*/
3032
3033/*!
3034 \qmlproperty point QtQuick::PathMultiline::start
3035
3036 This read-only property contains the beginning of the polylines.
3037*/
3038
3039/*!
3040 \qmlproperty list<list<point>> QtQuick::PathMultiline::paths
3041
3042 This property defines the vertices of the polylines.
3043
3044 It can be a JS array of JS arrays of points constructed with \c Qt.point(),
3045 a QList or QVector of QPolygonF, or QList<QList<QPointF>>.
3046 If you are binding this to a custom property in some C++ object,
3047 QList<QPolygonF> or QList<QList<QPointF>> is the most
3048 appropriate type to use.
3049*/
3050
3051QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent)
3052{
3053}
3054
3055QVariant QQuickPathMultiline::paths() const
3056{
3057 return QVariant::fromValue(m_paths);
3058}
3059
3060void QQuickPathMultiline::setPaths(const QVariant &paths)
3061{
3062 if (paths.canConvert<QList<QPolygonF>>()) {
3063 const QList<QPolygonF> pathPolygons = paths.value<QList<QPolygonF>>();
3064 QList<QList<QPointF>> pathVectors;
3065 for (const QPolygonF &p : pathPolygons)
3066 pathVectors << p;
3067 setPaths(pathVectors);
3068 } else if (paths.canConvert<QList<QList<QPointF>>>()) {
3069 setPaths(paths.value<QList<QList<QPointF>>>());
3070 } else if (paths.canConvert<QVariantList>()) {
3071 // This handles cases other than QList<QPolygonF> or QList<QList<QPointF>>, such as
3072 // QList<QList<QPointF>>, QList<QList<QPointF>>, QVariantList of QList<QPointF>,
3073 // QVariantList of QVariantList of QPointF, QList<QList<QPoint>> etc.
3074 QList<QList<QPointF>> pathsList;
3075 QVariantList vll = paths.value<QVariantList>();
3076 for (const QVariant &v : vll) {
3077 // If we bind a QList<QPolygonF> property directly, rather than via QVariant,
3078 // it will come through as QJSValue that can be converted to QVariantList of QPolygonF.
3079 if (v.canConvert<QPolygonF>()) {
3080 pathsList.append(v.value<QPolygonF>());
3081 } else {
3082 QVariantList vl = v.value<QVariantList>();
3083 QList<QPointF> l;
3084 for (const QVariant &point : vl) {
3085 if (point.canConvert<QPointF>())
3086 l.append(point.toPointF());
3087 }
3088 if (l.size() >= 2)
3089 pathsList.append(l);
3090 }
3091 }
3092 setPaths(pathsList);
3093 } else {
3094 qWarning() << "PathMultiline: paths of type" << paths.userType() << "not supported";
3095 setPaths(QList<QList<QPointF>>());
3096 }
3097}
3098
3099void QQuickPathMultiline::setPaths(const QList<QList<QPointF>> &paths)
3100{
3101 if (m_paths != paths) {
3102 const QPointF &oldStart = start();
3103 m_paths = paths;
3104 const QPointF &newStart = start();
3105 emit pathsChanged();
3106 if (oldStart != newStart)
3107 emit startChanged();
3108 emit changed();
3109 }
3110}
3111
3112QPointF QQuickPathMultiline::start() const
3113{
3114 if (m_paths.size())
3115 return m_paths.first().first();
3116 return QPointF();
3117}
3118
3119void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
3120{
3121 if (!m_paths.size())
3122 return;
3123 for (const QList<QPointF> &p: m_paths) {
3124 path.moveTo(p.first());
3125 for (int i = 1; i < p.size(); ++i)
3126 path.lineTo(p.at(i));
3127 }
3128}
3129
3130/*!
3131 \qmltype PathText
3132 \nativetype QQuickPathText
3133 \inqmlmodule QtQuick
3134 \ingroup qtquick-animation-paths
3135 \brief Defines a string in a specified font.
3136 \since QtQuick 2.15
3137
3138 This element defines the shape of a specified string in a specified font. The text's
3139 baseline will be translated to the x and y coordinates, and the outlines from the font
3140 will be added to the path accordingly.
3141
3142 When used to render texts in a Shape item, note the following:
3143 \list
3144 \li For correct fill, the ShapePath's fillRule should be set to ShapePath.WindingFill.
3145 \li Not all fonts provide a nice outline suitable for stroking. If you want a stroked
3146 outline and are getting unsatisfactory results, try a different font.
3147 \endlist
3148
3149 \qml
3150 PathText {
3151 x: 0
3152 y: font.pixelSize
3153 font.family: "Arial"
3154 font.pixelSize: 100
3155 text: "Foobar"
3156 }
3157 \endqml
3158
3159 \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
3160*/
3161
3162/*!
3163 \qmlproperty real QtQuick::PathText::x
3164
3165 The horizontal position of the PathText's baseline.
3166*/
3167
3168/*!
3169 \qmlproperty real QtQuick::PathText::y
3170
3171 The vertical position of the PathText's baseline.
3172
3173 \note This property refers to the position of the baseline of the text, not the top of its bounding box. This may
3174 cause some confusion, e.g. when using the PathText with Qt Quick Shapes. See \l FontMetrics for information on how to
3175 get the ascent of a font, which can be used to translate the text into the expected position.
3176*/
3177
3178/*!
3179 \qmlproperty string QtQuick::PathText::text
3180
3181 The text for which this PathText should contain the outlines.
3182*/
3183
3184/*!
3185 \qmlproperty string QtQuick::PathText::font.family
3186
3187 Sets the family name of the font.
3188
3189 \include qmltypereference.qdoc qml-font-family
3190*/
3191
3192/*!
3193 \qmlproperty string QtQuick::PathText::font.styleName
3194
3195 Sets the style name of the font.
3196
3197 The style name is case insensitive. If set, the font will be matched against style name instead
3198 of the font properties \l font.weight, \l font.bold and \l font.italic.
3199*/
3200
3201/*!
3202 \qmlproperty bool QtQuick::PathText::font.bold
3203
3204 Sets whether the font weight is bold.
3205*/
3206
3207/*!
3208 \qmlproperty int QtQuick::PathText::font.weight
3209
3210 Sets the font's weight.
3211
3212 The weight can be one of:
3213
3214 \value Font.Thin 100
3215 \value Font.ExtraLight 200
3216 \value Font.Light 300
3217 \value Font.Normal 400 (default)
3218 \value Font.Medium 500
3219 \value Font.DemiBold 600
3220 \value Font.Bold 700
3221 \value Font.ExtraBold 800
3222 \value Font.Black 900
3223
3224 \qml
3225 PathText { text: "Hello"; font.weight: Font.DemiBold }
3226 \endqml
3227*/
3228
3229/*!
3230 \qmlproperty bool QtQuick::PathText::font.italic
3231
3232 Sets whether the font has an italic style.
3233*/
3234
3235/*!
3236 \qmlproperty bool QtQuick::PathText::font.underline
3237
3238 Sets whether the text is underlined.
3239*/
3240
3241/*!
3242 \qmlproperty bool QtQuick::PathText::font.strikeout
3243
3244 Sets whether the font has a strikeout style.
3245*/
3246
3247/*!
3248 \qmlproperty real QtQuick::PathText::font.pointSize
3249
3250 Sets the font size in points. The point size must be greater than zero.
3251*/
3252
3253/*!
3254 \qmlproperty int QtQuick::PathText::font.pixelSize
3255
3256 Sets the font size in pixels.
3257
3258 Using this function makes the font device dependent.
3259 Use \c pointSize to set the size of the font in a device independent manner.
3260*/
3261
3262/*!
3263 \qmlproperty real QtQuick::PathText::font.letterSpacing
3264
3265 Sets the letter spacing for the font.
3266
3267 \include qmltypereference.qdoc qml-font-letter-spacing
3268*/
3269
3270/*!
3271 \qmlproperty real QtQuick::PathText::font.wordSpacing
3272
3273 Sets the word spacing for the font.
3274
3275 \include qmltypereference.qdoc qml-font-word-spacing
3276*/
3277
3278/*!
3279 \qmlproperty enumeration QtQuick::PathText::font.capitalization
3280
3281 Sets the capitalization for the text.
3282
3283 \include qmltypereference.qdoc qml-font-capitalization
3284*/
3285
3286/*!
3287 \qmlproperty bool QtQuick::PathText::font.kerning
3288
3289 \include qmltypereference.qdoc qml-font-kerning
3290*/
3291
3292/*!
3293 \qmlproperty bool QtQuick::PathText::font.preferShaping
3294
3295 \include qmltypereference.qdoc qml-font-prefer-shaping
3296*/
3297
3298/*!
3299 \qmlproperty object QtQuick::PathText::font.variableAxes
3300 \since 6.7
3301
3302 \include qmltypereference.qdoc qml-font-variable-axes
3303*/
3304
3305/*!
3306 \qmlproperty object QtQuick::PathText::font.features
3307 \since 6.6
3308
3309 \include qmltypereference.qdoc qml-font-features
3310*/
3311
3312/*!
3313 \qmlproperty bool QtQuick::PathText::font.contextFontMerging
3314 \since 6.8
3315
3316 \include qmltypereference.qdoc qml-font-context-font-merging
3317*/
3318
3319/*!
3320 \qmlproperty bool QtQuick::PathText::font.preferTypoLineMetrics
3321 \since 6.8
3322
3323 \include qmltypereference.qdoc qml-font-prefer-typo-line-metrics
3324*/
3325void QQuickPathText::updatePath() const
3326{
3327 if (!_path.isEmpty())
3328 return;
3329
3330 _path.addText(0.0, 0.0, _font, _text);
3331
3332 // Account for distance from baseline to top, since addText() takes baseline position
3333 QRectF brect = _path.boundingRect();
3334 _path.translate(_x, _y - brect.y());
3335}
3336
3337void QQuickPathText::addToPath(QPainterPath &path)
3338{
3339 if (_text.isEmpty())
3340 return;
3341 updatePath();
3342 path.addPath(_path);
3343}
3344
3345QT_END_NAMESPACE
3346
3347#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)