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
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2579qreal QQuickPathRectangle::radius()
const
2581 return _extra.isAllocated() ? _extra->radius : 0;
2584void QQuickPathRectangle::setRadius(qreal newRadius)
2586 if (_extra.value().radius == newRadius)
2588 _extra->radius = newRadius;
2589 emit radiusChanged();
2590 if (!(_extra->isRadiusSet(Qt::TopLeftCorner)))
2591 emit topLeftRadiusChanged();
2592 if (!(_extra->isRadiusSet(Qt::TopRightCorner)))
2593 emit topRightRadiusChanged();
2594 if (!(_extra->isRadiusSet(Qt::BottomLeftCorner)))
2595 emit bottomLeftRadiusChanged();
2596 if (!(_extra->isRadiusSet(Qt::BottomRightCorner)))
2597 emit bottomRightRadiusChanged();
2602
2603
2604
2605
2606
2608qreal QQuickPathRectangle::cornerRadius(Qt::Corner corner)
const
2610 if (_extra.isAllocated())
2611 return (_extra->isRadiusSet(corner)) ? _extra->cornerData[corner].radius : _extra->radius;
2616void QQuickPathRectangle::setCornerRadius(Qt::Corner corner, qreal newCornerRadius)
2618 if (_extra.value().cornerData[corner].radius == newCornerRadius
2619 && (_extra->isRadiusSet(corner)))
2621 _extra->cornerData[corner].radius = newCornerRadius;
2622 _extra->cornerData[corner].isRadiusSet =
true;
2624 emitCornerRadiusChanged(corner);
2627void QQuickPathRectangle::resetCornerRadius(Qt::Corner corner)
2629 if (!_extra.isAllocated() || !(_extra->isRadiusSet(corner)))
2631 _extra->cornerData[corner].isRadiusSet =
false;
2632 emitCornerRadiusChanged(corner);
2635void QQuickPathRectangle::emitCornerRadiusChanged(Qt::Corner corner)
2638 case Qt::TopLeftCorner:
2639 emit topLeftRadiusChanged();
2641 case Qt::TopRightCorner:
2642 emit topRightRadiusChanged();
2644 case Qt::BottomLeftCorner:
2645 emit bottomLeftRadiusChanged();
2647 case Qt::BottomRightCorner:
2648 emit bottomRightRadiusChanged();
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2671bool QQuickPathRectangle::hasBevel()
const
2673 return cornerShape() == Bevel;
2676void QQuickPathRectangle::setBevel(
bool bevel)
2678 setCornerShape(bevel ? Bevel : Rounded);
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2698bool QQuickPathRectangle::cornerBevel(Qt::Corner corner)
const
2700 return cornerShape(corner) == Bevel;
2703void QQuickPathRectangle::setCornerBevel(Qt::Corner corner,
bool newCornerBevel)
2705 if (!newCornerBevel) {
2706 resetCornerBevel(corner);
2709 setCornerShape(corner, Bevel);
2712void QQuickPathRectangle::resetCornerBevel(Qt::Corner corner)
2714 resetCornerShape(corner);
2718
2719
2720
2721
2722
2724QQuickPathRectangle::CornerShape QQuickPathRectangle::cornerShape()
const
2726 return _extra.isAllocated() ? _extra->shape : Rounded;
2729void QQuickPathRectangle::setCornerShape(CornerShape shape)
2731 if (_extra.value().shape == shape)
2733 const bool bevelToggled = (_extra->shape == Bevel) != (shape == Bevel);
2734 _extra->shape = shape;
2735 emit cornerShapeChanged();
2737 emit bevelChanged();
2738 if (!(_extra->isShapeSet(Qt::TopLeftCorner))) {
2739 emit topLeftCornerShapeChanged();
2741 emit topLeftBevelChanged();
2743 if (!(_extra->isShapeSet(Qt::TopRightCorner))) {
2744 emit topRightCornerShapeChanged();
2746 emit topRightBevelChanged();
2748 if (!(_extra->isShapeSet(Qt::BottomLeftCorner))) {
2749 emit bottomLeftCornerShapeChanged();
2751 emit bottomLeftBevelChanged();
2753 if (!(_extra->isShapeSet(Qt::BottomRightCorner))) {
2754 emit bottomRightCornerShapeChanged();
2756 emit bottomRightBevelChanged();
2761QQuickPathRectangle::CornerShape QQuickPathRectangle::cornerShape(Qt::Corner corner)
const
2763 if (_extra.isAllocated())
2764 return (_extra->isShapeSet(corner)) ? CornerShape(_extra->cornerData[corner].shape)
2770void QQuickPathRectangle::setCornerShape(Qt::Corner corner, CornerShape newCornerShape)
2772 if ((_extra.value().isShapeSet(corner)) && _extra->cornerData[corner].shape == newCornerShape)
2774 const bool oldBevel = cornerShape(corner) == Bevel;
2775 _extra->cornerData[corner].shape = newCornerShape;
2776 _extra->cornerData[corner].isShapeSet =
true;
2777 emitCornerShapeChanged(corner, oldBevel);
2780void QQuickPathRectangle::resetCornerShape(Qt::Corner corner)
2782 if (!_extra.isAllocated() || !(_extra->isShapeSet(corner)))
2784 const bool oldBevel = cornerShape(corner) == Bevel;
2785 _extra->cornerData[corner].isShapeSet =
false;
2786 emitCornerShapeChanged(corner, oldBevel);
2789void QQuickPathRectangle::emitCornerShapeChanged(Qt::Corner corner,
bool oldBevel)
2791 const bool bevelToggled = oldBevel != (cornerShape(corner) == Bevel);
2793 case Qt::TopLeftCorner:
2794 emit topLeftCornerShapeChanged();
2796 emit topLeftBevelChanged();
2798 case Qt::TopRightCorner:
2799 emit topRightCornerShapeChanged();
2801 emit topRightBevelChanged();
2803 case Qt::BottomLeftCorner:
2804 emit bottomLeftCornerShapeChanged();
2806 emit bottomLeftBevelChanged();
2808 case Qt::BottomRightCorner:
2809 emit bottomRightCornerShapeChanged();
2811 emit bottomRightBevelChanged();
2817void QQuickPathRectangle::addToPath(QPainterPath &path,
const QQuickPathData &data)
2819 QRectF rect(positionForCurve(data, path.currentPosition()), QSizeF(_width, _height));
2821 qreal halfStroke = _strokeAdjustment * 0.5;
2822 rect.adjust(halfStroke, halfStroke, -halfStroke, -halfStroke);
2826 if (!_extra.isAllocated()) {
2836 const qreal maxDiameter = qMin(rect.width(), rect.height());
2837 const qreal maxRadius = maxDiameter * 0.5;
2838 const qreal generalRadius = qBound(-maxRadius, _extra->radius, maxRadius);
2839 auto effectiveRadius = [&](Qt::Corner corner) {
2840 qreal radius = _extra->cornerData[corner].radius;
2841 return (_extra->isRadiusSet(corner)) ? qBound(-maxRadius, radius, maxRadius)
2844 const qreal rTL = effectiveRadius(Qt::TopLeftCorner);
2845 const qreal rTR = effectiveRadius(Qt::TopRightCorner);
2846 const qreal rBL = effectiveRadius(Qt::BottomLeftCorner);
2847 const qreal rBR = effectiveRadius(Qt::BottomRightCorner);
2857 static constexpr QPointF cornerOffsets[9] = {
2858 { 0.300, 0.000 }, { 0.473, 0.000 }, { 0.619, 0.039 }, { 0.804, 0.088 }, { 0.912, 0.196 },
2859 { 0.961, 0.381 }, { 1.000, 0.527 }, { 1.000, 0.700 }, { 1.000, 1.000 }
2863 auto rotate = [](QPointF p,
int steps) {
2864 for (
int i = 0; i < steps; ++i)
2865 p = QPointF(-p.y(), p.x());
2869 auto addSquircleCorner = [&](QPointF start,
int steps, qreal r,
bool inverted) {
2870 for (
int seg = 0; seg < 3; ++seg) {
2871 QPointF o1 = cornerOffsets[seg * 3 + 0];
2872 QPointF o2 = cornerOffsets[seg * 3 + 1];
2873 QPointF o3 = cornerOffsets[seg * 3 + 2];
2875 o1 = QPointF(o1.y(), o1.x());
2876 o2 = QPointF(o2.y(), o2.x());
2877 o3 = QPointF(o3.y(), o3.x());
2879 QPointF c1 = start + rotate(o1 * r, steps);
2880 QPointF c2 = start + rotate(o2 * r, steps);
2881 QPointF end = start + rotate(o3 * r, steps);
2882 path.cubicTo(c1, c2, end);
2892 auto addRoundedCorner = [&](QPointF corner, QPointF inward, qreal startAngle, qreal r,
2894 const QRectF box(corner - QPointF(r, r), QSizeF(2 * r, 2 * r));
2896 path.arcTo(box, startAngle + 90, 90);
2898 path.arcTo(box.translated(inward * r), startAngle, -90);
2901 path.moveTo(rect.left() + qAbs(rTL), rect.top());
2903 const qreal r = qAbs(rTR);
2904 const bool inverted = rTR < 0;
2905 switch (cornerShape(Qt::TopRightCorner)) {
2907 path.lineTo(QPointF(rect.right() - r, rect.top()));
2908 path.lineTo(QPointF(rect.right(), rect.top() + r));
2911 path.lineTo(QPointF(rect.right() - r, rect.top()));
2912 addSquircleCorner(QPointF(rect.right() - r, rect.top()), 0, r, inverted);
2915 addRoundedCorner(rect.topRight(), QPointF(-1, 1), 90, r, inverted);
2919 path.lineTo(rect.topRight());
2923 const qreal r = qAbs(rBR);
2924 const bool inverted = rBR < 0;
2925 switch (cornerShape(Qt::BottomRightCorner)) {
2927 path.lineTo(QPointF(rect.right(), rect.bottom() - r));
2928 path.lineTo(QPointF(rect.right() - r, rect.bottom()));
2931 path.lineTo(QPointF(rect.right(), rect.bottom() - r));
2932 addSquircleCorner(QPointF(rect.right(), rect.bottom() - r), 1, r, inverted);
2935 addRoundedCorner(rect.bottomRight(), QPointF(-1, -1), 0, r, inverted);
2939 path.lineTo(rect.bottomRight());
2943 const qreal r = qAbs(rBL);
2944 const bool inverted = rBL < 0;
2945 switch (cornerShape(Qt::BottomLeftCorner)) {
2947 path.lineTo(QPointF(rect.left() + r, rect.bottom()));
2948 path.lineTo(QPointF(rect.left(), rect.bottom() - r));
2951 path.lineTo(QPointF(rect.left() + r, rect.bottom()));
2952 addSquircleCorner(QPointF(rect.left() + r, rect.bottom()), 2, r, inverted);
2955 addRoundedCorner(rect.bottomLeft(), QPointF(1, -1), 270, r, inverted);
2959 path.lineTo(rect.bottomLeft());
2963 const qreal r = qAbs(rTL);
2964 const bool inverted = rTL < 0;
2965 switch (cornerShape(Qt::TopLeftCorner)) {
2967 path.lineTo(QPointF(rect.left(), rect.top() + r));
2970 path.lineTo(QPointF(rect.left(), rect.top() + r));
2971 addSquircleCorner(QPointF(rect.left(), rect.top() + r), 3, r, inverted);
2974 addRoundedCorner(rect.topLeft(), QPointF(1, 1), 180, r, inverted);
2978 path.lineTo(rect.topLeft());
2980 path.closeSubpath();
2986
2987
2988
2989
2990
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
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3068qreal QQuickPathPercent::value()
const
3073void QQuickPathPercent::setValue(qreal value)
3075 if (_value != value) {
3077 emit valueChanged();
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3110
3111
3112
3113
3116
3117
3118
3119
3120
3121
3122
3123
3124
3126QQuickPathPolyline::QQuickPathPolyline(QObject *parent) : QQuickCurve(parent)
3130QVariant QQuickPathPolyline::path()
const
3132 return QVariant::fromValue(m_path);
3135void QQuickPathPolyline::setPath(
const QVariant &path)
3137 if (path.userType() == QMetaType::QPolygonF) {
3138 setPath(path.value<QPolygonF>());
3139 }
else if (path.canConvert<QList<QPointF>>()) {
3140 setPath(path.value<QList<QPointF>>());
3141 }
else if (path.canConvert<QVariantList>()) {
3144 QList<QPointF> pathList;
3145 QVariantList vl = path.value<QVariantList>();
3149 for (
const QVariant &v : vl)
3150 pathList.append(v.toPointF());
3153 qWarning() <<
"PathPolyline: path of type" << path.userType() <<
"not supported";
3157void QQuickPathPolyline::setPath(
const QList<QPointF> &path)
3159 if (m_path != path) {
3160 const QPointF &oldStart = start();
3162 const QPointF &newStart = start();
3164 if (oldStart != newStart)
3165 emit startChanged();
3170QPointF QQuickPathPolyline::start()
const
3172 if (m_path.size()) {
3173 const QPointF &p = m_path.first();
3179void QQuickPathPolyline::addToPath(QPainterPath &path,
const QQuickPathData &)
3181 if (m_path.size() < 2)
3184 path.moveTo(m_path.first());
3185 for (
int i = 1; i < m_path.size(); ++i)
3186 path.lineTo(m_path.at(i));
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3245
3246
3247
3248
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3262QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent)
3266QVariant QQuickPathMultiline::paths()
const
3268 return QVariant::fromValue(m_paths);
3271void QQuickPathMultiline::setPaths(
const QVariant &paths)
3273 if (paths.canConvert<QList<QPolygonF>>()) {
3274 const QList<QPolygonF> pathPolygons = paths.value<QList<QPolygonF>>();
3275 QList<QList<QPointF>> pathVectors;
3276 for (
const QPolygonF &p : pathPolygons)
3278 setPaths(pathVectors);
3279 }
else if (paths.canConvert<QList<QList<QPointF>>>()) {
3280 setPaths(paths.value<QList<QList<QPointF>>>());
3281 }
else if (paths.canConvert<QVariantList>()) {
3285 QList<QList<QPointF>> pathsList;
3286 QVariantList vll = paths.value<QVariantList>();
3287 for (
const QVariant &v : vll) {
3290 if (v.canConvert<QPolygonF>()) {
3291 pathsList.append(v.value<QPolygonF>());
3293 QVariantList vl = v.value<QVariantList>();
3295 for (
const QVariant &point : vl) {
3296 if (point.canConvert<QPointF>())
3297 l.append(point.toPointF());
3300 pathsList.append(l);
3303 setPaths(pathsList);
3305 qWarning() <<
"PathMultiline: paths of type" << paths.userType() <<
"not supported";
3306 setPaths(QList<QList<QPointF>>());
3310void QQuickPathMultiline::setPaths(
const QList<QList<QPointF>> &paths)
3312 if (m_paths != paths) {
3313 const QPointF &oldStart = start();
3315 const QPointF &newStart = start();
3316 emit pathsChanged();
3317 if (oldStart != newStart)
3318 emit startChanged();
3323QPointF QQuickPathMultiline::start()
const
3326 return m_paths.first().first();
3330void QQuickPathMultiline::addToPath(QPainterPath &path,
const QQuickPathData &)
3332 if (!m_paths.size())
3334 for (
const QList<QPointF> &p: m_paths) {
3335 path.moveTo(p.first());
3336 for (
int i = 1; i < p.size(); ++i)
3337 path.lineTo(p.at(i));
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3374
3375
3376
3377
3380
3381
3382
3383
3384
3385
3386
3387
3390
3391
3392
3393
3396
3397
3398
3399
3400
3401
3404
3405
3406
3407
3408
3409
3410
3413
3414
3415
3416
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3441
3442
3443
3444
3447
3448
3449
3450
3453
3454
3455
3456
3459
3460
3461
3462
3465
3466
3467
3468
3469
3470
3471
3474
3475
3476
3477
3478
3479
3482
3483
3484
3485
3486
3487
3490
3491
3492
3493
3494
3495
3498
3499
3500
3501
3504
3505
3506
3507
3510
3511
3512
3513
3514
3517
3518
3519
3520
3521
3524
3525
3526
3527
3528
3531
3532
3533
3534
3535
3536void QQuickPathText::updatePath()
const
3538 if (!_path.isEmpty())
3541 _path.addText(0.0, 0.0, _font, _text);
3544 QRectF brect = _path.boundingRect();
3545 _path.translate(_x, _y - brect.y());
3548void QQuickPathText::addToPath(QPainterPath &path)
3550 if (_text.isEmpty())
3553 path.addPath(_path);
3558#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)