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