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