11#include <private/qbezier_p.h>
12#include <QtCore/qmath.h>
13#include <QtCore/private/qnumeric_p.h>
14#include <QtGui/private/qguisvg_p.h>
18Q_STATIC_LOGGING_CATEGORY(lcPath,
"qt.quick.shapes.path")
20void QQuickPathPrivate::enablePathElement(QQuickPathElement *pathElement)
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);
29 QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement);
30 if (attribute && !_attributes.contains(attribute->name()))
31 _attributes.append(attribute->name());
35 if (!_pathElements.contains(pathElement))
36 q->connect(pathElement, SIGNAL(changed()), q, SLOT(processPath()));
39void QQuickPathPrivate::disablePathElement(QQuickPathElement *pathElement)
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();
52 for (QQuickPathElement *other : std::as_const(_pathElements)) {
53 QQuickPathAttribute *otherAttribute = qobject_cast<QQuickPathAttribute *>(other);
54 if (otherAttribute && otherAttribute->name() == name) {
61 _attributes.removeOne(name);
65 if (!_pathElements.contains(pathElement))
66 q->disconnect(pathElement, SIGNAL(changed()), q, SLOT(processPath()));
70
71
72
73
74
75
76
77
78
79
80
81
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181QQuickPath::QQuickPath(QObject *parent)
182 : QObject(*(
new QQuickPathPrivate), parent)
186QQuickPath::QQuickPath(QQuickPathPrivate &dd, QObject *parent)
187 : QObject(dd, parent)
191QQuickPath::~QQuickPath()
196
197
198
199
200qreal QQuickPath::startX()
const
202 Q_D(
const QQuickPath);
203 return d->startX.isValid() ? d->startX.value() : 0;
206void QQuickPath::setStartX(qreal x)
209 if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
212 emit startXChanged();
216bool QQuickPath::hasStartX()
const
218 Q_D(
const QQuickPath);
219 return d->startX.isValid();
222qreal QQuickPath::startY()
const
224 Q_D(
const QQuickPath);
225 return d->startY.isValid() ? d->startY.value() : 0;
228void QQuickPath::setStartY(qreal y)
231 if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
234 emit startYChanged();
238bool QQuickPath::hasStartY()
const
240 Q_D(
const QQuickPath);
241 return d->startY.isValid();
245
246
247
248bool QQuickPath::isClosed()
const
250 Q_D(
const QQuickPath);
251 if (d->_pathElements.isEmpty())
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
281QQmlListProperty<QQuickPathElement> QQuickPath::pathElements()
283 return QQmlListProperty<QQuickPathElement>(
this,
289 pathElements_replace,
290 pathElements_removeLast);
295 QQuickPath *path =
static_cast<QQuickPath*>(object);
297 return QQuickPathPrivate::get(path);
300QQuickPathElement *QQuickPath::pathElements_at(QQmlListProperty<QQuickPathElement> *property, qsizetype index)
302 QQuickPathPrivate *d = privatePath(property->object);
304 return d->_pathElements.at(index);
307void QQuickPath::pathElements_append(QQmlListProperty<QQuickPathElement> *property, QQuickPathElement *pathElement)
309 QQuickPathPrivate *d = privatePath(property->object);
310 d->appendPathElement(pathElement);
313qsizetype QQuickPath::pathElements_count(QQmlListProperty<QQuickPathElement> *property)
315 QQuickPathPrivate *d = privatePath(property->object);
317 return d->_pathElements.size();
320void QQuickPath::pathElements_clear(QQmlListProperty<QQuickPathElement> *property)
322 QQuickPathPrivate *d = privatePath(property->object);
323 d->clearPathElements();
326void QQuickPath::pathElements_replace(
327 QQmlListProperty<QQuickPathElement> *property, qsizetype position,
328 QQuickPathElement *pathElement)
330 privatePath(property->object)->replacePathElement(position, pathElement);
333void QQuickPath::pathElements_removeLast(QQmlListProperty<QQuickPathElement> *property)
335 privatePath(property->object)->removeLastPathElement();
338void QQuickPath::interpolate(
int idx,
const QString &name, qreal value)
341 interpolate(d->_attributePoints, idx, name, value);
344void QQuickPath::interpolate(QList<AttributePoint> &attributePoints,
int idx,
const QString &name, qreal value)
350 qreal lastPercent = 0;
351 int search = idx - 1;
353 const AttributePoint &point = attributePoints.at(search);
354 if (point.values.contains(name)) {
355 lastValue = point.values.value(name);
356 lastPercent = point.origpercent;
364 const AttributePoint &curPoint = attributePoints.at(idx);
366 for (
int ii = search; ii < idx; ++ii) {
367 AttributePoint &point = attributePoints[ii];
369 qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
370 point.values.insert(name, val);
374void QQuickPath::endpoint(
const QString &name)
377 const AttributePoint &first = d->_attributePoints.first();
378 qreal val = first.values.value(name);
379 for (
int ii = d->_attributePoints.size() - 1; ii >= 0; ii--) {
380 const AttributePoint &point = d->_attributePoints.at(ii);
381 if (point.values.contains(name)) {
382 for (
int jj = ii + 1; jj < d->_attributePoints.size(); ++jj) {
383 AttributePoint &setPoint = d->_attributePoints[jj];
384 setPoint.values.insert(name, val);
391void QQuickPath::endpoint(QList<AttributePoint> &attributePoints,
const QString &name)
393 const AttributePoint &first = attributePoints.first();
394 qreal val = first.values.value(name);
395 for (
int ii = attributePoints.size() - 1; ii >= 0; ii--) {
396 const AttributePoint &point = attributePoints.at(ii);
397 if (point.values.contains(name)) {
398 for (
int jj = ii + 1; jj < attributePoints.size(); ++jj) {
399 AttributePoint &setPoint = attributePoints[jj];
400 setPoint.values.insert(name, val);
407void QQuickPath::processPath()
411 if (!d->componentComplete)
414 if (!d->asynchronous) {
416 }
else if (!d->processPending) {
417 d->processPending =
true;
418 QMetaObject::invokeMethod(
this, &QQuickPath::doProcessPath, Qt::QueuedConnection);
422void QQuickPath::doProcessPath()
426 d->processPending =
false;
428 if (!d->componentComplete)
431 if (d->useCustomPath)
434 d->_pointCache.clear();
435 d->prevBez.isValid =
false;
437 if (d->isShapePath) {
439 d->_path = createShapePath(QPointF(), QPointF(), d->pathLength, &d->closed);
441 d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
445 d->_path = d->_path.simplified();
450inline static void scalePath(QPainterPath &path,
const QSizeF &scale)
452 const qreal xscale = scale.width();
453 const qreal yscale = scale.height();
454 if (xscale == 1 && yscale == 1)
457 for (
int i = 0; i < path.elementCount(); ++i) {
458 const QPainterPath::Element &element = path.elementAt(i);
459 path.setElementPositionAt(i, element.x * xscale, element.y * yscale);
463QPainterPath QQuickPath::createPath(
const QPointF &startPoint,
const QPointF &endPoint,
const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints,
bool *closed)
468 attributePoints.clear();
470 if (!d->componentComplete)
471 return QPainterPath();
475 AttributePoint first;
476 for (
int ii = 0; ii < attributes.size(); ++ii)
477 first.values[attributes.at(ii)] = 0;
478 attributePoints << first;
480 qreal startX = d->startX.isValid() ? d->startX.value() : startPoint.x();
481 qreal startY = d->startY.isValid() ? d->startY.value() : startPoint.y();
482 path.moveTo(startX, startY);
484 const QString percentString = QStringLiteral(
"_qfx_percent");
486 bool usesPercent =
false;
488 qCDebug(lcPath).nospace() <<
this <<
" is creating path starting at " << startPoint
489 <<
" and ending at " << endPoint <<
" with " << pathLength <<
" element(s):";
490 for (QQuickPathElement *pathElement : std::as_const(d->_pathElements)) {
491 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement)) {
494 data.endPoint = endPoint;
495 data.curves = d->_pathCurves;
496 curve->addToPath(path, data);
498 p.origpercent = path.length();
499 attributePoints << p;
501 qCDebug(lcPath) <<
"- index" << index <<
"curve:" << data.curves.at(data.index);
502 }
else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement)) {
503 AttributePoint &point = attributePoints.last();
504 point.values[attribute->name()] = attribute->value();
505 interpolate(attributePoints, attributePoints.size() - 1, attribute->name(), attribute->value());
506 qCDebug(lcPath) <<
"- index" << index <<
"attribute:" << attribute->value();
507 }
else if (QQuickPathPercent *percent = qobject_cast<QQuickPathPercent *>(pathElement)) {
508 AttributePoint &point = attributePoints.last();
509 point.values[percentString] = percent->value();
510 interpolate(attributePoints, attributePoints.size() - 1, percentString, percent->value());
511 qCDebug(lcPath) <<
"- index" << index <<
"percent:" << percent->value();
513 }
else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(pathElement)) {
514 text->addToPath(path);
515 qCDebug(lcPath) <<
"- index" << index <<
"text:" << text->text();
520 const AttributePoint &last = attributePoints.constLast();
521 for (
int ii = 0; ii < attributes.size(); ++ii) {
522 if (!last.values.contains(attributes.at(ii)))
523 endpoint(attributePoints, attributes.at(ii));
525 if (usesPercent && !last.values.contains(percentString)) {
526 d->_attributePoints.last().values[percentString] = 1;
527 interpolate(d->_attributePoints.size() - 1, percentString, 1);
529 scalePath(path, d->scale);
532 qreal length = path.length();
533 qreal prevpercent = 0;
534 qreal prevorigpercent = 0;
535 for (
int ii = 0; ii < attributePoints.size(); ++ii) {
536 const AttributePoint &point = attributePoints.at(ii);
537 if (point.values.contains(percentString)) {
539 qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
540 (point.values.value(percentString)-prevpercent);
541 attributePoints[ii].scale = scale;
543 attributePoints[ii].origpercent /= length;
544 attributePoints[ii].percent = point.values.value(percentString);
545 prevorigpercent = attributePoints.at(ii).origpercent;
546 prevpercent = attributePoints.at(ii).percent;
548 attributePoints[ii].origpercent /= length;
549 attributePoints[ii].percent = attributePoints.at(ii).origpercent;
554 QPointF end = path.currentPosition();
555 *closed = length > 0 && startX * d->scale.width() == end.x() && startY * d->scale.height() == end.y();
562QPainterPath QQuickPath::createShapePath(
const QPointF &startPoint,
const QPointF &endPoint, qreal &pathLength,
bool *closed)
566 if (!d->componentComplete)
567 return QPainterPath();
571 qreal startX = d->startX.isValid() ? d->startX.value() : startPoint.x();
572 qreal startY = d->startY.isValid() ? d->startY.value() : startPoint.y();
573 path.moveTo(startX, startY);
576 qCDebug(lcPath).nospace() <<
this <<
" is creating shape path from " << d->_pathCurves.size()
577 <<
" curve(s) with endPoint " << endPoint <<
":";
578 for (QQuickCurve *curve : std::as_const(d->_pathCurves)) {
581 data.endPoint = endPoint;
582 data.curves = d->_pathCurves;
583 curve->addToPath(path, data);
584 qCDebug(lcPath) <<
"- index" << data.index << data.curves.at(data.index);
588 for (QQuickPathText *text : std::as_const(d->_pathTexts))
589 text->addToPath(path);
592 QPointF end = path.currentPosition();
593 *closed = startX == end.x() && startY == end.y();
595 scalePath(path, d->scale);
600 path.setCachingEnabled(
true);
605void QQuickPath::classBegin()
608 d->componentComplete =
false;
611void QQuickPath::disconnectPathElements()
613 Q_D(
const QQuickPath);
615 for (QQuickPathElement *pathElement : d->_pathElements) {
617 disconnect(pathElement, SIGNAL(changed()),
this, SLOT(processPath()));
621void QQuickPath::connectPathElements()
623 Q_D(
const QQuickPath);
625 for (QQuickPathElement *pathElement : d->_pathElements) {
627 connect(pathElement, SIGNAL(changed()),
this, SLOT(processPath()));
631void QQuickPath::gatherAttributes()
635 QSet<QString> attributes;
637 Q_ASSERT(d->_pathCurves.isEmpty());
640 for (QQuickPathElement *pathElement : std::as_const(d->_pathElements)) {
641 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement))
642 d->_pathCurves.append(curve);
643 else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(pathElement))
644 d->_pathTexts.append(text);
645 else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement))
646 attributes.insert(attribute->name());
649 d->_attributes = attributes.values();
652void QQuickPath::componentComplete()
655 d->componentComplete =
true;
662 connectPathElements();
665QPainterPath QQuickPath::path()
const
667 Q_D(
const QQuickPath);
671void QQuickPath::setPath(
const QPainterPath &path)
674 d->useCustomPath = !path.isEmpty();
675 d->_pointCache.clear();
676 d->prevBez.isValid =
false;
681QStringList QQuickPath::attributes()
const
683 Q_D(
const QQuickPath);
684 if (!d->componentComplete) {
688 for (QQuickPathElement *pathElement : d->_pathElements) {
689 if (QQuickPathAttribute *attribute =
690 qobject_cast<QQuickPathAttribute *>(pathElement))
691 attrs.insert(attribute->name());
693 return attrs.values();
695 return d->_attributes;
698static inline QBezier nextBezier(
const QPainterPath &path,
int *current, qreal *bezLength,
bool reverse =
false)
700 const int lastElement = reverse ? 0 : path.elementCount() - 1;
701 const int start = reverse ? *current - 1 : *current + 1;
702 for (
int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
703 const QPainterPath::Element &e = path.elementAt(i);
706 case QPainterPath::MoveToElement:
708 case QPainterPath::LineToElement:
710 QLineF line(path.elementAt(i-1), e);
711 *bezLength = line.length();
712 QPointF a = path.elementAt(i-1);
713 QPointF delta = e - a;
715 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
717 case QPainterPath::CurveToElement:
719 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
722 path.elementAt(i+2));
723 *bezLength = b.length();
731 *current = lastElement;
736static inline int segmentCount(
const QPainterPath &path, qreal pathLength)
740 if (path.elementCount() == 2
741 && path.elementAt(0).type == QPainterPath::MoveToElement
742 && path.elementAt(1).type == QPainterPath::LineToElement) {
747 return qCeil(pathLength*5);
751static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
753 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
756void QQuickPath::createPointCache()
const
758 Q_D(
const QQuickPath);
759 qreal pathLength = d->pathLength;
760 if (pathLength <= 0 || qt_is_nan(pathLength))
763 const int segments = segmentCount(d->_path, pathLength);
764 const int lastElement = d->_path.elementCount() - 1;
765 d->_pointCache.resize(segments+1);
767 int currElement = -1;
769 QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
770 qreal currLength = bezLength;
771 qreal epc = currLength / pathLength;
773 for (
int i = 0; i < d->_pointCache.size(); i++) {
775 qreal prevPercent = 0;
776 qreal prevOrigPercent = 0;
777 for (
int ii = 0; ii < d->_attributePoints.size(); ++ii) {
778 qreal percent = qreal(i)/segments;
779 const AttributePoint &point = d->_attributePoints.at(ii);
780 if (percent < point.percent || ii == d->_attributePoints.size() - 1) {
781 qreal elementPercent = (percent - prevPercent);
783 qreal spc = prevOrigPercent + elementPercent * point.scale;
786 if (currElement > lastElement)
788 currBez = nextBezier(d->_path, &currElement, &bezLength);
789 if (bezLength == 0.0) {
790 currLength = pathLength;
794 currLength += bezLength;
795 epc = currLength / pathLength;
797 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
798 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
801 prevOrigPercent = point.origpercent;
802 prevPercent = point.percent;
807void QQuickPath::invalidateSequentialHistory()
const
809 Q_D(
const QQuickPath);
810 d->prevBez.isValid =
false;
814
815
816
817
818
819
820
821void QQuickPath::setSimplify(
bool s)
824 if (d->simplify == s)
830 emit simplifyChanged();
833bool QQuickPath::simplify()
const
835 Q_D(
const QQuickPath);
840
841
842
843
844
845
846
847
848bool QQuickPath::isAsynchronous()
const
850 Q_D(
const QQuickPath);
851 return d->asynchronous;
854void QQuickPath::setAsynchronous(
bool a)
857 if (d->asynchronous == a)
861 emit asynchronousChanged();
865
866
867
868
869
870
871
872
873
874
875QSizeF QQuickPath::scale()
const
877 Q_D(
const QQuickPath);
881void QQuickPath::setScale(
const QSizeF &scale)
884 if (scale == d->scale)
891QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle)
const
893 Q_D(
const QQuickPath);
894 return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
897QPointF QQuickPath::sequentialPointAt(
const QPainterPath &path,
const qreal &pathLength,
const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
899 Q_ASSERT(p >= 0.0 && p <= 1.0);
901 if (!prevBez.isValid)
902 return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
903 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
905 return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
906 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
909QPointF QQuickPath::forwardsPointAt(
const QPainterPath &path,
const qreal &pathLength,
const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
911 if (pathLength <= 0 || qt_is_nan(pathLength))
912 return path.pointAtPercent(0);
914 const int lastElement = path.elementCount() - 1;
915 bool haveCachedBez = prevBez.isValid;
916 int currElement = haveCachedBez ? prevBez.element : -1;
917 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
918 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
919 qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
920 qreal epc = currLength / pathLength;
923 qreal prevPercent = 0;
924 qreal prevOrigPercent = 0;
925 for (
int ii = 0; ii < attributePoints.size(); ++ii) {
927 const AttributePoint &point = attributePoints.at(ii);
928 if (percent < point.percent || ii == attributePoints.size() - 1) {
929 qreal elementPercent = (percent - prevPercent);
931 qreal spc = prevOrigPercent + elementPercent * point.scale;
934 Q_ASSERT(!(currElement > lastElement));
935 Q_UNUSED(lastElement);
936 currBez = nextBezier(path, &currElement, &bezLength);
937 currLength += bezLength;
938 epc = currLength / pathLength;
940 prevBez.element = currElement;
941 prevBez.bezLength = bezLength;
942 prevBez.currLength = currLength;
943 prevBez.bezier = currBez;
945 prevBez.isValid =
true;
947 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
950 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
951 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
952 *angle = QLineF(0, 0, m1, m2).angle();
955 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
957 prevOrigPercent = point.origpercent;
958 prevPercent = point.percent;
965QPointF QQuickPath::backwardsPointAt(
const QPainterPath &path,
const qreal &pathLength,
const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
967 if (pathLength <= 0 || qt_is_nan(pathLength))
968 return path.pointAtPercent(0);
970 const int firstElement = 1;
971 bool haveCachedBez = prevBez.isValid;
972 int currElement = haveCachedBez ? prevBez.element : path.elementCount();
973 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
974 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength,
true );
975 qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
976 qreal prevLength = currLength - bezLength;
977 qreal epc = prevLength / pathLength;
979 for (
int ii = attributePoints.size() - 1; ii > 0; --ii) {
981 const AttributePoint &point = attributePoints.at(ii);
982 const AttributePoint &prevPoint = attributePoints.at(ii-1);
983 if (percent > prevPoint.percent || ii == 1) {
984 qreal elementPercent = (percent - prevPoint.percent);
986 qreal spc = prevPoint.origpercent + elementPercent * point.scale;
989 Q_ASSERT(!(currElement < firstElement));
990 Q_UNUSED(firstElement);
991 currBez = nextBezier(path, &currElement, &bezLength,
true );
994 currLength = (currElement == firstElement) ? bezLength : prevLength;
995 prevLength = currLength - bezLength;
996 epc = prevLength / pathLength;
998 prevBez.element = currElement;
999 prevBez.bezLength = bezLength;
1000 prevBez.currLength = currLength;
1001 prevBez.bezier = currBez;
1003 prevBez.isValid =
true;
1005 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
1008 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
1009 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
1010 *angle = QLineF(0, 0, m1, m2).angle();
1013 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
1017 return QPointF(0,0);
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036QPointF QQuickPath::pointAtPercent(qreal t)
const
1038 Q_D(
const QQuickPath);
1040 return d->_path.pointAtPercent(t);
1042 if (d->_pointCache.isEmpty()) {
1044 if (d->_pointCache.isEmpty())
1048 const int segmentCount = d->_pointCache.size() - 1;
1049 qreal idxf = t*segmentCount;
1050 int idx1 = qFloor(idxf);
1051 qreal delta = idxf - idx1;
1052 if (idx1 > segmentCount)
1053 idx1 = segmentCount;
1058 return d->_pointCache.at(idx1);
1061 int idx2 = qCeil(idxf);
1062 if (idx2 > segmentCount)
1063 idx2 = segmentCount;
1067 QPointF p1 = d->_pointCache.at(idx1);
1068 QPointF p2 = d->_pointCache.at(idx2);
1069 QPointF pos = p1 * (1.0-delta) + p2 * delta;
1074qreal QQuickPath::attributeAt(
const QString &name, qreal percent)
const
1076 Q_D(
const QQuickPath);
1077 if (percent < 0 || percent > 1)
1080 for (
int ii = 0; ii < d->_attributePoints.size(); ++ii) {
1081 const AttributePoint &point = d->_attributePoints.at(ii);
1083 if (point.percent == percent) {
1084 return point.values.value(name);
1085 }
else if (point.percent > percent) {
1087 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
1089 ii?(d->_attributePoints.at(ii - 1).percent):0;
1090 qreal curValue = point.values.value(name);
1091 qreal curPercent = point.percent;
1093 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
1102qreal QQuickCurve::x()
const
1104 return _x.isValid() ? _x.value() : 0;
1107void QQuickCurve::setX(qreal x)
1109 if (!_x.isValid() || _x != x) {
1116bool QQuickCurve::hasX()
1118 return _x.isValid();
1121qreal QQuickCurve::y()
const
1123 return _y.isValid() ? _y.value() : 0;
1126void QQuickCurve::setY(qreal y)
1128 if (!_y.isValid() || _y != y) {
1135bool QQuickCurve::hasY()
1137 return _y.isValid();
1140qreal QQuickCurve::relativeX()
const
1145void QQuickCurve::setRelativeX(qreal x)
1147 if (!_relativeX.isValid() || _relativeX != x) {
1149 emit relativeXChanged();
1154bool QQuickCurve::hasRelativeX()
1156 return _relativeX.isValid();
1159qreal QQuickCurve::relativeY()
const
1164void QQuickCurve::setRelativeY(qreal y)
1166 if (!_relativeY.isValid() || _relativeY != y) {
1168 emit relativeYChanged();
1173bool QQuickCurve::hasRelativeY()
1175 return _relativeY.isValid();
1178#ifndef QT_NO_DEBUG_STREAM
1181 QDebugStateSaver saver(debug);
1182 debug.nospace() << curve->metaObject()->className() <<
'(' << (
const void *)curve;
1183 if (!curve->objectName().isEmpty())
1184 debug <<
" name=" << curve->objectName();
1185 debug <<
" x=" << curve->x();
1186 debug <<
" y=" << curve->y();
1187 debug <<
" relativeX=" << curve->relativeX();
1188 debug <<
" relativeY=" << curve->relativeY();
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1232
1233
1234
1235
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1250
1251
1253QString QQuickPathAttribute::name()
const
1258void QQuickPathAttribute::setName(
const QString &name)
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1303
1304
1305qreal QQuickPathAttribute::value()
const
1310void QQuickPathAttribute::setValue(qreal value)
1312 if (_value != value) {
1314 emit valueChanged();
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1342
1343
1344
1345
1346
1347
1348
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1367 QQuickCurve *curve = data.curves.at(data
.index);
1368 bool isEnd = data
.index == data.curves.size() - 1;
1369 return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
1370 curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
1373void QQuickPathLine::addToPath(QPainterPath &path,
const QQuickPathData &data)
1375 path.lineTo(positionForCurve(data, path.currentPosition()));
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1408
1409
1410
1411
1412
1413
1414
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1431void QQuickPathMove::addToPath(QPainterPath &path,
const QQuickPathData &data)
1433 path.moveTo(positionForCurve(data, path.currentPosition()));
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1463
1464
1465
1466
1469
1470
1471
1472
1473
1474
1475
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1493
1494
1495
1496
1497
1500
1501
1502qreal QQuickPathQuad::controlX()
const
1507void QQuickPathQuad::setControlX(qreal x)
1509 if (_controlX != x) {
1511 emit controlXChanged();
1518
1519
1520qreal QQuickPathQuad::controlY()
const
1525void QQuickPathQuad::setControlY(qreal y)
1527 if (_controlY != y) {
1529 emit controlYChanged();
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1549qreal QQuickPathQuad::relativeControlX()
const
1551 return _relativeControlX;
1554void QQuickPathQuad::setRelativeControlX(qreal x)
1556 if (!_relativeControlX.isValid() || _relativeControlX != x) {
1557 _relativeControlX = x;
1558 emit relativeControlXChanged();
1563bool QQuickPathQuad::hasRelativeControlX()
1565 return _relativeControlX.isValid();
1568qreal QQuickPathQuad::relativeControlY()
const
1570 return _relativeControlY;
1573void QQuickPathQuad::setRelativeControlY(qreal y)
1575 if (!_relativeControlY.isValid() || _relativeControlY != y) {
1576 _relativeControlY = y;
1577 emit relativeControlYChanged();
1582bool QQuickPathQuad::hasRelativeControlY()
1584 return _relativeControlY.isValid();
1587void QQuickPathQuad::addToPath(QPainterPath &path,
const QQuickPathData &data)
1589 const QPointF &prevPoint = path.currentPosition();
1590 QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1591 hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1592 path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1625
1626
1627
1628
1629
1630
1631
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1649
1650
1651
1652
1653
1654qreal QQuickPathCubic::control1X()
const
1659void QQuickPathCubic::setControl1X(qreal x)
1661 if (_control1X != x) {
1663 emit control1XChanged();
1668qreal QQuickPathCubic::control1Y()
const
1673void QQuickPathCubic::setControl1Y(qreal y)
1675 if (_control1Y != y) {
1677 emit control1YChanged();
1683
1684
1685
1686
1687
1688qreal QQuickPathCubic::control2X()
const
1693void QQuickPathCubic::setControl2X(qreal x)
1695 if (_control2X != x) {
1697 emit control2XChanged();
1702qreal QQuickPathCubic::control2Y()
const
1707void QQuickPathCubic::setControl2Y(qreal y)
1709 if (_control2Y != y) {
1711 emit control2YChanged();
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1733qreal QQuickPathCubic::relativeControl1X()
const
1735 return _relativeControl1X;
1738void QQuickPathCubic::setRelativeControl1X(qreal x)
1740 if (!_relativeControl1X.isValid() || _relativeControl1X != x) {
1741 _relativeControl1X = x;
1742 emit relativeControl1XChanged();
1747bool QQuickPathCubic::hasRelativeControl1X()
1749 return _relativeControl1X.isValid();
1752qreal QQuickPathCubic::relativeControl1Y()
const
1754 return _relativeControl1Y;
1757void QQuickPathCubic::setRelativeControl1Y(qreal y)
1759 if (!_relativeControl1Y.isValid() || _relativeControl1Y != y) {
1760 _relativeControl1Y = y;
1761 emit relativeControl1YChanged();
1766bool QQuickPathCubic::hasRelativeControl1Y()
1768 return _relativeControl1Y.isValid();
1771qreal QQuickPathCubic::relativeControl2X()
const
1773 return _relativeControl2X;
1776void QQuickPathCubic::setRelativeControl2X(qreal x)
1778 if (!_relativeControl2X.isValid() || _relativeControl2X != x) {
1779 _relativeControl2X = x;
1780 emit relativeControl2XChanged();
1785bool QQuickPathCubic::hasRelativeControl2X()
1787 return _relativeControl2X.isValid();
1790qreal QQuickPathCubic::relativeControl2Y()
const
1792 return _relativeControl2Y;
1795void QQuickPathCubic::setRelativeControl2Y(qreal y)
1797 if (!_relativeControl2Y.isValid() || _relativeControl2Y != y) {
1798 _relativeControl2Y = y;
1799 emit relativeControl2YChanged();
1804bool QQuickPathCubic::hasRelativeControl2Y()
1806 return _relativeControl2Y.isValid();
1809void QQuickPathCubic::addToPath(QPainterPath &path,
const QQuickPathData &data)
1811 const QPointF &prevPoint = path.currentPosition();
1812 QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1813 hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1814 QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1815 hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1816 path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1843
1844
1845
1846
1847
1848
1849
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1868 int count = path.elementCount();
1872 int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1873 return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1876void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path,
const QQuickPathData &data)
1884 QPointF prevFar, prev, point, next;
1887 int index = data.index - 1;
1888 QQuickCurve *curve = index == -1 ? 0 : data.curves.at(index);
1889 if (qobject_cast<QQuickPathCatmullRomCurve*>(curve)) {
1890 prev = path.currentPosition();
1891 prevFar = previousPathPosition(path);
1893 prev = path.currentPosition();
1894 bool prevFarSet =
false;
1895 if (index == -1 && data.curves.size() > 1) {
1896 if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.size()-1))) {
1899 QQuickPathData loopData;
1900 loopData.endPoint = data.endPoint;
1901 loopData.curves = data.curves;
1902 for (
int i = data.index; i < data.curves.size(); ++i) {
1904 pos = positionForCurve(loopData, pos);
1905 if (i == data.curves.size()-2)
1908 if (pos == QPointF(path.elementAt(0))) {
1920 point = positionForCurve(data, path.currentPosition());
1923 index = data.index + 1;
1924 if (index < data.curves.size() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
1925 QQuickPathData nextData;
1926 nextData.index = index;
1927 nextData.endPoint = data.endPoint;
1928 nextData.curves = data.curves;
1929 next = positionForCurve(nextData, point);
1931 if (point == QPointF(path.elementAt(0)) && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(0)) && path.elementCount() >= 3) {
1934 next = QPointF(path.elementAt(3));
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949 QPointF control1(prevFar.x() * qreal(-0.167) +
1951 point.x() * qreal(0.167),
1952 prevFar.y() * qreal(-0.167) +
1954 point.y() * qreal(0.167));
1956 QPointF control2(prev.x() * qreal(0.167) +
1958 next.x() * qreal(-0.167),
1959 prev.y() * qreal(0.167) +
1961 next.y() * qreal(-0.167));
1963 path.cubicTo(control1, control2, point);
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1993
1994
1995
1996
1997
1998
1999
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2032qreal QQuickPathArc::radiusX()
const
2037void QQuickPathArc::setRadiusX(qreal radius)
2039 if (_radiusX == radius)
2043 emit radiusXChanged();
2047qreal QQuickPathArc::radiusY()
const
2052void QQuickPathArc::setRadiusY(qreal radius)
2054 if (_radiusY == radius)
2058 emit radiusYChanged();
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2082bool QQuickPathArc::useLargeArc()
const
2084 return _useLargeArc;
2087void QQuickPathArc::setUseLargeArc(
bool largeArc)
2089 if (_useLargeArc == largeArc)
2092 _useLargeArc = largeArc;
2093 emit useLargeArcChanged();
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2115QQuickPathArc::ArcDirection QQuickPathArc::direction()
const
2120void QQuickPathArc::setDirection(ArcDirection direction)
2122 if (_direction == direction)
2125 _direction = direction;
2126 emit directionChanged();
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2152qreal QQuickPathArc::xAxisRotation()
const
2154 return _xAxisRotation;
2157void QQuickPathArc::setXAxisRotation(qreal rotation)
2159 if (_xAxisRotation == rotation)
2162 _xAxisRotation = rotation;
2163 emit xAxisRotationChanged();
2167void QQuickPathArc::addToPath(QPainterPath &path,
const QQuickPathData &data)
2169 const QPointF &startPoint = path.currentPosition();
2170 const QPointF &endPoint = positionForCurve(data, startPoint);
2171 QGuiSvg::pathArc(path,
2176 _direction == Clockwise ? 1 : 0,
2179 startPoint.x(), startPoint.y());
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2199
2200
2201
2202
2203
2205qreal QQuickPathAngleArc::centerX()
const
2210void QQuickPathAngleArc::setCenterX(qreal centerX)
2212 if (_centerX == centerX)
2216 emit centerXChanged();
2220qreal QQuickPathAngleArc::centerY()
const
2225void QQuickPathAngleArc::setCenterY(qreal centerY)
2227 if (_centerY == centerY)
2231 emit centerYChanged();
2236
2237
2238
2239
2240
2242qreal QQuickPathAngleArc::radiusX()
const
2247void QQuickPathAngleArc::setRadiusX(qreal radius)
2249 if (_radiusX == radius)
2253 emit radiusXChanged();
2257qreal QQuickPathAngleArc::radiusY()
const
2262void QQuickPathAngleArc::setRadiusY(qreal radius)
2264 if (_radiusY == radius)
2268 emit radiusYChanged();
2273
2274
2275
2276
2277
2278
2280qreal QQuickPathAngleArc::startAngle()
const
2285void QQuickPathAngleArc::setStartAngle(qreal angle)
2287 if (_startAngle == angle)
2290 _startAngle = angle;
2291 emit startAngleChanged();
2296
2297
2298
2299
2300
2301
2302
2304qreal QQuickPathAngleArc::sweepAngle()
const
2309void QQuickPathAngleArc::setSweepAngle(qreal angle)
2311 if (_sweepAngle == angle)
2314 _sweepAngle = angle;
2315 emit sweepAngleChanged();
2320
2321
2322
2323
2324
2325
2326
2327
2329bool QQuickPathAngleArc::moveToStart()
const
2331 return _moveToStart;
2334void QQuickPathAngleArc::setMoveToStart(
bool move)
2336 if (_moveToStart == move)
2339 _moveToStart = move;
2340 emit moveToStartChanged();
2344void QQuickPathAngleArc::addToPath(QPainterPath &path,
const QQuickPathData &)
2346 qreal x = _centerX - _radiusX;
2347 qreal y = _centerY - _radiusY;
2348 qreal width = _radiusX * 2;
2349 qreal height = _radiusY * 2;
2351 path.arcMoveTo(x, y, width, height, -_startAngle);
2352 path.arcTo(x, y, width, height, -_startAngle, -_sweepAngle);
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2382
2383
2384
2385
2386
2387
2388
2390QString QQuickPathSvg::path()
const
2395void QQuickPathSvg::setPath(
const QString &path)
2405void QQuickPathSvg::addToPath(QPainterPath &path,
const QQuickPathData &)
2407 std::optional<QPainterPath> qpath = QGuiSvg::parsePath(_path);
2410 path = qpath.value();
2412 path.addPath(qpath.value());
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2454
2455
2456
2457
2458
2459
2460
2461
2462
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2480
2481
2482
2483
2484
2485
2486
2488qreal QQuickPathRectangle::width()
const
2493void QQuickPathRectangle::setWidth(qreal width)
2495 if (_width == width)
2499 emit widthChanged();
2503qreal QQuickPathRectangle::height()
const
2508void QQuickPathRectangle::setHeight(qreal height)
2510 if (_height == height)
2514 emit heightChanged();
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2545qreal QQuickPathRectangle::strokeAdjustment()
const
2547 return _strokeAdjustment;
2550void QQuickPathRectangle::setStrokeAdjustment(qreal newStrokeAdjustment)
2552 if (_strokeAdjustment == newStrokeAdjustment)
2554 _strokeAdjustment = newStrokeAdjustment;
2555 emit strokeAdjustmentChanged();
2560
2561
2562
2563
2565qreal QQuickPathRectangle::radius()
const
2567 return _extra.isAllocated() ? _extra->radius : 0;
2570void QQuickPathRectangle::setRadius(qreal newRadius)
2572 if (_extra.value().radius == newRadius)
2574 _extra->radius = newRadius;
2575 emit radiusChanged();
2576 if (!(_extra->isRadiusSet(Qt::TopLeftCorner)))
2577 emit topLeftRadiusChanged();
2578 if (!(_extra->isRadiusSet(Qt::TopRightCorner)))
2579 emit topRightRadiusChanged();
2580 if (!(_extra->isRadiusSet(Qt::BottomLeftCorner)))
2581 emit bottomLeftRadiusChanged();
2582 if (!(_extra->isRadiusSet(Qt::BottomRightCorner)))
2583 emit bottomRightRadiusChanged();
2588
2589
2591qreal QQuickPathRectangle::cornerRadius(Qt::Corner corner)
const
2593 if (_extra.isAllocated())
2594 return (_extra->isRadiusSet(corner)) ? _extra->cornerData[corner].radius : _extra->radius;
2599void QQuickPathRectangle::setCornerRadius(Qt::Corner corner, qreal newCornerRadius)
2601 if (_extra.value().cornerData[corner].radius == newCornerRadius
2602 && (_extra->isRadiusSet(corner)))
2604 _extra->cornerData[corner].radius = newCornerRadius;
2605 _extra->cornerData[corner].isRadiusSet =
true;
2607 emitCornerRadiusChanged(corner);
2610void QQuickPathRectangle::resetCornerRadius(Qt::Corner corner)
2612 if (!_extra.isAllocated() || !(_extra->isRadiusSet(corner)))
2614 _extra->cornerData[corner].isRadiusSet =
false;
2615 emitCornerRadiusChanged(corner);
2618void QQuickPathRectangle::emitCornerRadiusChanged(Qt::Corner corner)
2621 case Qt::TopLeftCorner:
2622 emit topLeftRadiusChanged();
2624 case Qt::TopRightCorner:
2625 emit topRightRadiusChanged();
2627 case Qt::BottomLeftCorner:
2628 emit bottomLeftRadiusChanged();
2630 case Qt::BottomRightCorner:
2631 emit bottomRightRadiusChanged();
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2654bool QQuickPathRectangle::hasBevel()
const
2656 return cornerShape() == Bevel;
2659void QQuickPathRectangle::setBevel(
bool bevel)
2661 setCornerShape(bevel ? Bevel : Rounded);
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2681bool QQuickPathRectangle::cornerBevel(Qt::Corner corner)
const
2683 return cornerShape(corner) == Bevel;
2686void QQuickPathRectangle::setCornerBevel(Qt::Corner corner,
bool newCornerBevel)
2688 if (!newCornerBevel) {
2689 resetCornerBevel(corner);
2692 setCornerShape(corner, Bevel);
2695void QQuickPathRectangle::resetCornerBevel(Qt::Corner corner)
2697 resetCornerShape(corner);
2701
2702
2703
2704
2705
2707QQuickPathRectangle::CornerShape QQuickPathRectangle::cornerShape()
const
2709 return _extra.isAllocated() ? _extra->shape : Rounded;
2712void QQuickPathRectangle::setCornerShape(CornerShape shape)
2714 if (_extra.value().shape == shape)
2716 const bool bevelToggled = (_extra->shape == Bevel) != (shape == Bevel);
2717 _extra->shape = shape;
2718 emit cornerShapeChanged();
2720 emit bevelChanged();
2721 if (!(_extra->isShapeSet(Qt::TopLeftCorner))) {
2722 emit topLeftCornerShapeChanged();
2724 emit topLeftBevelChanged();
2726 if (!(_extra->isShapeSet(Qt::TopRightCorner))) {
2727 emit topRightCornerShapeChanged();
2729 emit topRightBevelChanged();
2731 if (!(_extra->isShapeSet(Qt::BottomLeftCorner))) {
2732 emit bottomLeftCornerShapeChanged();
2734 emit bottomLeftBevelChanged();
2736 if (!(_extra->isShapeSet(Qt::BottomRightCorner))) {
2737 emit bottomRightCornerShapeChanged();
2739 emit bottomRightBevelChanged();
2744QQuickPathRectangle::CornerShape QQuickPathRectangle::cornerShape(Qt::Corner corner)
const
2746 if (_extra.isAllocated())
2747 return (_extra->isShapeSet(corner)) ? CornerShape(_extra->cornerData[corner].shape)
2753void QQuickPathRectangle::setCornerShape(Qt::Corner corner, CornerShape newCornerShape)
2755 if ((_extra.value().isShapeSet(corner)) && _extra->cornerData[corner].shape == newCornerShape)
2757 const bool oldBevel = cornerShape(corner) == Bevel;
2758 _extra->cornerData[corner].shape = newCornerShape;
2759 _extra->cornerData[corner].isShapeSet =
true;
2760 emitCornerShapeChanged(corner, oldBevel);
2763void QQuickPathRectangle::resetCornerShape(Qt::Corner corner)
2765 if (!_extra.isAllocated() || !(_extra->isShapeSet(corner)))
2767 const bool oldBevel = cornerShape(corner) == Bevel;
2768 _extra->cornerData[corner].isShapeSet =
false;
2769 emitCornerShapeChanged(corner, oldBevel);
2772void QQuickPathRectangle::emitCornerShapeChanged(Qt::Corner corner,
bool oldBevel)
2774 const bool bevelToggled = oldBevel != (cornerShape(corner) == Bevel);
2776 case Qt::TopLeftCorner:
2777 emit topLeftCornerShapeChanged();
2779 emit topLeftBevelChanged();
2781 case Qt::TopRightCorner:
2782 emit topRightCornerShapeChanged();
2784 emit topRightBevelChanged();
2786 case Qt::BottomLeftCorner:
2787 emit bottomLeftCornerShapeChanged();
2789 emit bottomLeftBevelChanged();
2791 case Qt::BottomRightCorner:
2792 emit bottomRightCornerShapeChanged();
2794 emit bottomRightBevelChanged();
2800void QQuickPathRectangle::addToPath(QPainterPath &path,
const QQuickPathData &data)
2802 QRectF rect(positionForCurve(data, path.currentPosition()), QSizeF(_width, _height));
2804 qreal halfStroke = _strokeAdjustment * 0.5;
2805 rect.adjust(halfStroke, halfStroke, -halfStroke, -halfStroke);
2809 if (!_extra.isAllocated()) {
2816 const qreal maxDiameter = qMin(rect.width(), rect.height());
2817 const qreal generalRadius = qBound(qreal(0), _extra->radius, maxDiameter * 0.5);
2818 auto effectiveRadius = [&](Qt::Corner corner) {
2819 qreal radius = _extra->cornerData[corner].radius;
2820 return (_extra->isRadiusSet(corner)) ? qBound(qreal(0), radius, maxDiameter * 0.5)
2823 const qreal rTL = effectiveRadius(Qt::TopLeftCorner);
2824 const qreal rTR = effectiveRadius(Qt::TopRightCorner);
2825 const qreal rBL = effectiveRadius(Qt::BottomLeftCorner);
2826 const qreal rBR = effectiveRadius(Qt::BottomRightCorner);
2834 static constexpr QPointF cornerOffsets[9] = {
2835 { 0.300, 0.000 }, { 0.473, 0.000 }, { 0.619, 0.039 }, { 0.804, 0.088 }, { 0.912, 0.196 },
2836 { 0.961, 0.381 }, { 1.000, 0.527 }, { 1.000, 0.700 }, { 1.000, 1.000 }
2840 auto rotate = [](QPointF p,
int steps) {
2841 for (
int i = 0; i < steps; ++i)
2842 p = QPointF(-p.y(), p.x());
2846 auto addSquircleCorner = [&](QPointF start,
int steps, qreal r) {
2847 for (
int seg = 0; seg < 3; ++seg) {
2848 QPointF c1 = start + rotate(cornerOffsets[seg * 3 + 0] * r, steps);
2849 QPointF c2 = start + rotate(cornerOffsets[seg * 3 + 1] * r, steps);
2850 QPointF end = start + rotate(cornerOffsets[seg * 3 + 2] * r, steps);
2851 path.cubicTo(c1, c2, end);
2855 path.moveTo(rect.left() + rTL, rect.top());
2857 switch (cornerShape(Qt::TopRightCorner)) {
2859 path.lineTo(QPointF(rect.right() - rTR, rect.top()));
2860 path.lineTo(QPointF(rect.right(), rect.top() + rTR));
2863 path.lineTo(QPointF(rect.right() - rTR, rect.top()));
2864 addSquircleCorner(QPointF(rect.right() - rTR, rect.top()), 0, rTR);
2868 QRectF(QPointF(rect.right() - 2 * rTR, rect.top()), QSizeF(2 * rTR, 2 * rTR)),
2873 path.lineTo(rect.topRight());
2877 switch (cornerShape(Qt::BottomRightCorner)) {
2879 path.lineTo(QPointF(rect.right(), rect.bottom() - rBR));
2880 path.lineTo(QPointF(rect.right() - rBR, rect.bottom()));
2883 path.lineTo(QPointF(rect.right(), rect.bottom() - rBR));
2884 addSquircleCorner(QPointF(rect.right(), rect.bottom() - rBR), 1, rBR);
2887 path.arcTo(QRectF(QPointF(rect.right() - 2 * rBR, rect.bottom() - 2 * rBR),
2888 QSizeF(2 * rBR, 2 * rBR)),
2893 path.lineTo(rect.bottomRight());
2897 switch (cornerShape(Qt::BottomLeftCorner)) {
2899 path.lineTo(QPointF(rect.left() + rBL, rect.bottom()));
2900 path.lineTo(QPointF(rect.left(), rect.bottom() - rBL));
2903 path.lineTo(QPointF(rect.left() + rBL, rect.bottom()));
2904 addSquircleCorner(QPointF(rect.left() + rBL, rect.bottom()), 2, rBL);
2908 QRectF(QPointF(rect.left(), rect.bottom() - 2 * rBL), QSizeF(2 * rBL, 2 * rBL)),
2913 path.lineTo(rect.bottomLeft());
2917 switch (cornerShape(Qt::TopLeftCorner)) {
2919 path.lineTo(QPointF(rect.left(), rect.top() + rTL));
2922 path.lineTo(QPointF(rect.left(), rect.top() + rTL));
2923 addSquircleCorner(QPointF(rect.left(), rect.top() + rTL), 3, rTL);
2926 path.arcTo(QRectF(rect.topLeft(), QSizeF(2 * rTL, 2 * rTL)), 180, -90);
2930 path.lineTo(rect.topLeft());
2932 path.closeSubpath();
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3020qreal QQuickPathPercent::value()
const
3025void QQuickPathPercent::setValue(qreal value)
3027 if (_value != value) {
3029 emit valueChanged();
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3062
3063
3064
3065
3068
3069
3070
3071
3072
3073
3074
3075
3076
3078QQuickPathPolyline::QQuickPathPolyline(QObject *parent) : QQuickCurve(parent)
3082QVariant QQuickPathPolyline::path()
const
3084 return QVariant::fromValue(m_path);
3087void QQuickPathPolyline::setPath(
const QVariant &path)
3089 if (path.userType() == QMetaType::QPolygonF) {
3090 setPath(path.value<QPolygonF>());
3091 }
else if (path.canConvert<QList<QPointF>>()) {
3092 setPath(path.value<QList<QPointF>>());
3093 }
else if (path.canConvert<QVariantList>()) {
3096 QList<QPointF> pathList;
3097 QVariantList vl = path.value<QVariantList>();
3101 for (
const QVariant &v : vl)
3102 pathList.append(v.toPointF());
3105 qWarning() <<
"PathPolyline: path of type" << path.userType() <<
"not supported";
3109void QQuickPathPolyline::setPath(
const QList<QPointF> &path)
3111 if (m_path != path) {
3112 const QPointF &oldStart = start();
3114 const QPointF &newStart = start();
3116 if (oldStart != newStart)
3117 emit startChanged();
3122QPointF QQuickPathPolyline::start()
const
3124 if (m_path.size()) {
3125 const QPointF &p = m_path.first();
3131void QQuickPathPolyline::addToPath(QPainterPath &path,
const QQuickPathData &)
3133 if (m_path.size() < 2)
3136 path.moveTo(m_path.first());
3137 for (
int i = 1; i < m_path.size(); ++i)
3138 path.lineTo(m_path.at(i));
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3197
3198
3199
3200
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3214QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent)
3218QVariant QQuickPathMultiline::paths()
const
3220 return QVariant::fromValue(m_paths);
3223void QQuickPathMultiline::setPaths(
const QVariant &paths)
3225 if (paths.canConvert<QList<QPolygonF>>()) {
3226 const QList<QPolygonF> pathPolygons = paths.value<QList<QPolygonF>>();
3227 QList<QList<QPointF>> pathVectors;
3228 for (
const QPolygonF &p : pathPolygons)
3230 setPaths(pathVectors);
3231 }
else if (paths.canConvert<QList<QList<QPointF>>>()) {
3232 setPaths(paths.value<QList<QList<QPointF>>>());
3233 }
else if (paths.canConvert<QVariantList>()) {
3237 QList<QList<QPointF>> pathsList;
3238 QVariantList vll = paths.value<QVariantList>();
3239 for (
const QVariant &v : vll) {
3242 if (v.canConvert<QPolygonF>()) {
3243 pathsList.append(v.value<QPolygonF>());
3245 QVariantList vl = v.value<QVariantList>();
3247 for (
const QVariant &point : vl) {
3248 if (point.canConvert<QPointF>())
3249 l.append(point.toPointF());
3252 pathsList.append(l);
3255 setPaths(pathsList);
3257 qWarning() <<
"PathMultiline: paths of type" << paths.userType() <<
"not supported";
3258 setPaths(QList<QList<QPointF>>());
3262void QQuickPathMultiline::setPaths(
const QList<QList<QPointF>> &paths)
3264 if (m_paths != paths) {
3265 const QPointF &oldStart = start();
3267 const QPointF &newStart = start();
3268 emit pathsChanged();
3269 if (oldStart != newStart)
3270 emit startChanged();
3275QPointF QQuickPathMultiline::start()
const
3278 return m_paths.first().first();
3282void QQuickPathMultiline::addToPath(QPainterPath &path,
const QQuickPathData &)
3284 if (!m_paths.size())
3286 for (
const QList<QPointF> &p: m_paths) {
3287 path.moveTo(p.first());
3288 for (
int i = 1; i < p.size(); ++i)
3289 path.lineTo(p.at(i));
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3326
3327
3328
3329
3332
3333
3334
3335
3336
3337
3338
3339
3342
3343
3344
3345
3348
3349
3350
3351
3352
3353
3356
3357
3358
3359
3360
3361
3362
3365
3366
3367
3368
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3393
3394
3395
3396
3399
3400
3401
3402
3405
3406
3407
3408
3411
3412
3413
3414
3417
3418
3419
3420
3421
3422
3423
3426
3427
3428
3429
3430
3431
3434
3435
3436
3437
3438
3439
3442
3443
3444
3445
3446
3447
3450
3451
3452
3453
3456
3457
3458
3459
3462
3463
3464
3465
3466
3469
3470
3471
3472
3473
3476
3477
3478
3479
3480
3483
3484
3485
3486
3487
3488void QQuickPathText::updatePath()
const
3490 if (!_path.isEmpty())
3493 _path.addText(0.0, 0.0, _font, _text);
3496 QRectF brect = _path.boundingRect();
3497 _path.translate(_x, _y - brect.y());
3500void QQuickPathText::addToPath(QPainterPath &path)
3502 if (_text.isEmpty())
3505 path.addPath(_path);
3510#include "moc_qquickpath_p.cpp"
Combined button and popup list for selecting options.
QDebug operator<<(QDebug debug, const QQuickCurve *curve)
static qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
static QQuickPathPrivate * privatePath(QObject *object)
static int segmentCount(const QPainterPath &path, qreal pathLength)
QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
\qmltype PathLine \nativetype QQuickPathLine \inqmlmodule QtQuick
QPointF previousPathPosition(const QPainterPath &path)
\qmltype PathCurve \nativetype QQuickPathCatmullRomCurve \inqmlmodule QtQuick
static QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse=false)
static void scalePath(QPainterPath &path, const QSizeF &scale)