/
qquickpath.cpp
1800 lines (1492 loc) · 51.7 KB
1
2
/****************************************************************************
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/
5
**
6
** This file is part of the QtQml module of the Qt Toolkit.
7
8
9
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
10
11
12
13
14
15
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16
17
**
** In addition, as a special exception, Nokia gives you certain additional
18
** rights. These rights are described in the Nokia Qt LGPL Exception
19
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
21
22
23
24
25
26
27
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
28
**
29
30
31
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
32
33
34
35
36
**
**
**
**
**
37
**
38
39
40
41
** $QT_END_LICENSE$
**
****************************************************************************/
42
43
44
#include "qquickpath_p.h"
#include "qquickpath_p_p.h"
#include "qquicksvgparser_p.h"
45
46
47
48
49
50
51
52
53
54
55
#include <QSet>
#include <QTime>
#include <private/qbezier_p.h>
#include <QtCore/qmath.h>
#include <QtCore/qnumeric.h>
QT_BEGIN_NAMESPACE
/*!
56
\qmlclass PathElement QQuickPathElement
57
\inqmlmodule QtQuick 2
58
59
60
61
62
63
\ingroup qml-view-elements
\brief PathElement is the base path type.
This type is the base for all path types. It cannot
be instantiated.
64
\sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
65
66
67
*/
/*!
68
\qmlclass Path QQuickPath
69
\inqmlmodule QtQuick 2
70
71
72
73
\ingroup qml-view-elements
\brief A Path object defines a path for use by \l PathView.
A Path is composed of one or more path segments - PathLine, PathQuad,
74
PathCubic, PathArc, PathCurve, PathSvg.
75
76
77
78
79
80
81
The spacing of the items along the Path can be adjusted via a
PathPercent object.
PathAttribute allows named attributes with values to be defined
along the path.
82
\sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
83
*/
84
85
QQuickPath::QQuickPath(QObject *parent)
: QObject(*(new QQuickPathPrivate), parent)
86
87
88
{
}
89
QQuickPath::~QQuickPath()
90
91
92
93
{
}
/*!
94
95
\qmlproperty real QtQuick2::Path::startX
\qmlproperty real QtQuick2::Path::startY
96
97
These properties hold the starting position of the path.
*/
98
qreal QQuickPath::startX() const
99
{
100
Q_D(const QQuickPath);
101
return d->startX.isNull ? 0 : d->startX.value;
102
103
}
104
void QQuickPath::setStartX(qreal x)
105
{
106
Q_D(QQuickPath);
107
if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
108
109
110
111
112
113
return;
d->startX = x;
emit startXChanged();
processPath();
}
114
bool QQuickPath::hasStartX() const
115
{
116
Q_D(const QQuickPath);
117
118
119
return d->startX.isValid();
}
120
qreal QQuickPath::startY() const
121
{
122
Q_D(const QQuickPath);
123
return d->startY.isNull ? 0 : d->startY.value;
124
125
}
126
void QQuickPath::setStartY(qreal y)
127
{
128
Q_D(QQuickPath);
129
if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
130
131
132
133
134
135
return;
d->startY = y;
emit startYChanged();
processPath();
}
136
bool QQuickPath::hasStartY() const
137
{
138
Q_D(const QQuickPath);
139
140
141
return d->startY.isValid();
}
142
/*!
143
\qmlproperty bool QtQuick2::Path::closed
144
145
This property holds whether the start and end of the path are identical.
*/
146
bool QQuickPath::isClosed() const
147
{
148
Q_D(const QQuickPath);
149
150
151
return d->closed;
}
152
bool QQuickPath::hasEnd() const
153
{
154
Q_D(const QQuickPath);
155
for (int i = d->_pathElements.count() - 1; i > -1; --i) {
156
if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(d->_pathElements.at(i))) {
157
158
159
160
161
162
163
164
165
if ((!curve->hasX() && !curve->hasRelativeX()) || (!curve->hasY() && !curve->hasRelativeY()))
return false;
else
return true;
}
}
return hasStartX() && hasStartY();
}
166
/*!
167
\qmlproperty list<PathElement> QtQuick2::Path::pathElements
168
169
170
171
172
173
This property holds the objects composing the path.
\default
A path can contain the following path objects:
\list
174
175
176
177
178
179
180
181
\li \l PathLine - a straight line to a given position.
\li \l PathQuad - a quadratic Bezier curve to a given position with a control point.
\li \l PathCubic - a cubic Bezier curve to a given position with two control points.
\li \l PathArc - an arc to a given position with a radius.
\li \l PathSvg - a path specified as an SVG path data string.
\li \l PathCurve - a point on a Catmull-Rom curve.
\li \l PathAttribute - an attribute at a given position in the path.
\li \l PathPercent - a way to spread out items along various segments of the path.
182
183
\endlist
184
\snippet doc/src/snippets/qml/pathview/pathattributes.qml 2
185
186
*/
187
QQmlListProperty<QQuickPathElement> QQuickPath::pathElements()
188
{
189
190
Q_D(QQuickPath);
return QQmlListProperty<QQuickPathElement>(this, d->_pathElements);
191
192
}
193
void QQuickPath::interpolate(int idx, const QString &name, qreal value)
194
{
195
Q_D(QQuickPath);
196
197
198
interpolate(d->_attributePoints, idx, name, value);
}
199
void QQuickPath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value)
200
{
201
202
203
204
205
206
207
if (!idx)
return;
qreal lastValue = 0;
qreal lastPercent = 0;
int search = idx - 1;
while(search >= 0) {
208
const AttributePoint &point = attributePoints.at(search);
209
210
211
212
213
214
215
216
217
218
if (point.values.contains(name)) {
lastValue = point.values.value(name);
lastPercent = point.origpercent;
break;
}
--search;
}
++search;
219
const AttributePoint &curPoint = attributePoints.at(idx);
220
221
for (int ii = search; ii < idx; ++ii) {
222
AttributePoint &point = attributePoints[ii];
223
224
225
226
227
228
qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
point.values.insert(name, val);
}
}
229
void QQuickPath::endpoint(const QString &name)
230
{
231
Q_D(QQuickPath);
232
233
234
235
236
237
238
239
240
241
242
243
244
245
const AttributePoint &first = d->_attributePoints.first();
qreal val = first.values.value(name);
for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
const AttributePoint &point = d->_attributePoints.at(ii);
if (point.values.contains(name)) {
for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
AttributePoint &setPoint = d->_attributePoints[jj];
setPoint.values.insert(name, val);
}
return;
}
}
}
246
void QQuickPath::endpoint(QList<AttributePoint> &attributePoints, const QString &name)
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
{
const AttributePoint &first = attributePoints.first();
qreal val = first.values.value(name);
for (int ii = attributePoints.count() - 1; ii >= 0; ii--) {
const AttributePoint &point = attributePoints.at(ii);
if (point.values.contains(name)) {
for (int jj = ii + 1; jj < attributePoints.count(); ++jj) {
AttributePoint &setPoint = attributePoints[jj];
setPoint.values.insert(name, val);
}
return;
}
}
}
262
static QString percentString(QLatin1String("_qfx_percent"));
263
264
void QQuickPath::processPath()
265
{
266
Q_D(QQuickPath);
267
268
269
270
271
if (!d->componentComplete)
return;
d->_pointCache.clear();
272
273
274
275
276
277
278
d->prevBez.isValid = false;
d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
emit changed();
}
279
QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
280
{
281
Q_D(QQuickPath);
282
283
284
285
286
287
288
289
pathLength = 0;
attributePoints.clear();
if (!d->componentComplete)
return QPainterPath();
QPainterPath path;
290
291
AttributePoint first;
292
293
294
for (int ii = 0; ii < attributes.count(); ++ii)
first.values[attributes.at(ii)] = 0;
attributePoints << first;
295
296
297
298
qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
path.moveTo(startX, startY);
299
300
bool usesPercent = false;
301
int index = 0;
302
303
304
foreach (QQuickPathElement *pathElement, d->_pathElements) {
if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement)) {
QQuickPathData data;
305
306
307
308
data.index = index;
data.endPoint = endPoint;
data.curves = d->_pathCurves;
curve->addToPath(path, data);
309
AttributePoint p;
310
311
312
p.origpercent = path.length();
attributePoints << p;
++index;
313
} else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement)) {
314
AttributePoint &point = attributePoints.last();
315
point.values[attribute->name()] = attribute->value();
316
interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
317
} else if (QQuickPathPercent *percent = qobject_cast<QQuickPathPercent *>(pathElement)) {
318
AttributePoint &point = attributePoints.last();
319
point.values[percentString] = percent->value();
320
interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
321
usesPercent = true;
322
323
324
325
}
}
// Fixup end points
326
327
328
329
const AttributePoint &last = attributePoints.last();
for (int ii = 0; ii < attributes.count(); ++ii) {
if (!last.values.contains(attributes.at(ii)))
endpoint(attributePoints, attributes.at(ii));
330
}
331
332
333
334
335
if (usesPercent && !last.values.contains(percentString)) {
d->_attributePoints.last().values[percentString] = 1;
interpolate(d->_attributePoints.count() - 1, percentString, 1);
}
336
337
// Adjust percent
338
qreal length = path.length();
339
340
qreal prevpercent = 0;
qreal prevorigpercent = 0;
341
342
for (int ii = 0; ii < attributePoints.count(); ++ii) {
const AttributePoint &point = attributePoints.at(ii);
343
if (point.values.contains(percentString)) { //special string for QQuickPathPercent
344
if ( ii > 0) {
345
qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
346
(point.values.value(percentString)-prevpercent);
347
attributePoints[ii].scale = scale;
348
}
349
350
351
352
attributePoints[ii].origpercent /= length;
attributePoints[ii].percent = point.values.value(percentString);
prevorigpercent = attributePoints[ii].origpercent;
prevpercent = attributePoints[ii].percent;
353
} else {
354
355
attributePoints[ii].origpercent /= length;
attributePoints[ii].percent = attributePoints[ii].origpercent;
356
357
358
}
}
359
360
361
362
363
if (closed) {
QPointF end = path.currentPosition();
*closed = length > 0 && startX == end.x() && startY == end.y();
}
pathLength = length;
364
365
return path;
366
367
}
368
void QQuickPath::classBegin()
369
{
370
Q_D(QQuickPath);
371
372
373
d->componentComplete = false;
}
374
void QQuickPath::componentComplete()
375
{
376
Q_D(QQuickPath);
377
378
379
380
QSet<QString> attrs;
d->componentComplete = true;
// First gather up all the attributes
381
382
383
foreach (QQuickPathElement *pathElement, d->_pathElements) {
if (QQuickCurve *curve =
qobject_cast<QQuickCurve *>(pathElement))
384
d->_pathCurves.append(curve);
385
386
else if (QQuickPathAttribute *attribute =
qobject_cast<QQuickPathAttribute *>(pathElement))
387
388
389
390
391
392
attrs.insert(attribute->name());
}
d->_attributes = attrs.toList();
processPath();
393
foreach (QQuickPathElement *pathElement, d->_pathElements)
394
395
396
connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
}
397
QPainterPath QQuickPath::path() const
398
{
399
Q_D(const QQuickPath);
400
401
402
return d->_path;
}
403
QStringList QQuickPath::attributes() const
404
{
405
Q_D(const QQuickPath);
406
407
408
409
if (!d->componentComplete) {
QSet<QString> attrs;
// First gather up all the attributes
410
411
412
foreach (QQuickPathElement *pathElement, d->_pathElements) {
if (QQuickPathAttribute *attribute =
qobject_cast<QQuickPathAttribute *>(pathElement))
413
414
415
416
417
418
419
attrs.insert(attribute->name());
}
return attrs.toList();
}
return d->_attributes;
}
420
static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false)
421
{
422
const int lastElement = reverse ? 0 : path.elementCount() - 1;
423
424
const int start = reverse ? *current - 1 : *current + 1;
for (int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
425
426
427
428
429
430
431
432
433
434
435
const QPainterPath::Element &e = path.elementAt(i);
switch (e.type) {
case QPainterPath::MoveToElement:
break;
case QPainterPath::LineToElement:
{
QLineF line(path.elementAt(i-1), e);
*bezLength = line.length();
QPointF a = path.elementAt(i-1);
QPointF delta = e - a;
436
*current = i;
437
438
439
440
441
442
443
444
445
return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
}
case QPainterPath::CurveToElement:
{
QBezier b = QBezier::fromPoints(path.elementAt(i-1),
e,
path.elementAt(i+1),
path.elementAt(i+2));
*bezLength = b.length();
446
*current = i;
447
448
449
450
451
452
return b;
}
default:
break;
}
}
453
*current = lastElement;
454
455
456
457
*bezLength = 0;
return QBezier();
}
458
459
460
461
462
463
//derivative of the equation
static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
{
return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
}
464
void QQuickPath::createPointCache() const
465
{
466
Q_D(const QQuickPath);
467
qreal pathLength = d->pathLength;
468
469
470
471
472
473
474
475
if (pathLength <= 0 || qIsNaN(pathLength))
return;
// more points means less jitter between items as they move along the
// path, but takes longer to generate
const int points = qCeil(pathLength*5);
const int lastElement = d->_path.elementCount() - 1;
d->_pointCache.resize(points+1);
476
int currElement = -1;
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
qreal bezLength = 0;
QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
qreal currLength = bezLength;
qreal epc = currLength / pathLength;
for (int i = 0; i < d->_pointCache.size(); i++) {
//find which set we are in
qreal prevPercent = 0;
qreal prevOrigPercent = 0;
for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
qreal percent = qreal(i)/points;
const AttributePoint &point = d->_attributePoints.at(ii);
if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
qreal elementPercent = (percent - prevPercent);
qreal spc = prevOrigPercent + elementPercent * point.scale;
while (spc > epc) {
if (currElement > lastElement)
break;
currBez = nextBezier(d->_path, &currElement, &bezLength);
if (bezLength == 0.0) {
currLength = pathLength;
epc = 1.0;
break;
}
currLength += bezLength;
epc = currLength / pathLength;
}
qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
break;
}
prevOrigPercent = point.origpercent;
prevPercent = point.percent;
}
}
}
516
void QQuickPath::invalidateSequentialHistory() const
517
{
518
Q_D(const QQuickPath);
519
520
521
d->prevBez.isValid = false;
}
522
QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
523
{
524
Q_D(const QQuickPath);
525
526
527
return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
}
528
QPointF QQuickPath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
529
{
530
531
Q_ASSERT(p >= 0.0 && p <= 1.0);
532
533
534
535
536
537
538
539
if (!prevBez.isValid)
return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
}
540
QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
541
542
543
544
545
546
{
if (pathLength <= 0 || qIsNaN(pathLength))
return path.pointAtPercent(0); //expensive?
const int lastElement = path.elementCount() - 1;
bool haveCachedBez = prevBez.isValid;
547
int currElement = haveCachedBez ? prevBez.element : -1;
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
qreal epc = currLength / pathLength;
//find which set we are in
qreal prevPercent = 0;
qreal prevOrigPercent = 0;
for (int ii = 0; ii < attributePoints.count(); ++ii) {
qreal percent = p;
const AttributePoint &point = attributePoints.at(ii);
if (percent < point.percent || ii == attributePoints.count() - 1) {
qreal elementPercent = (percent - prevPercent);
qreal spc = prevOrigPercent + elementPercent * point.scale;
while (spc > epc) {
565
Q_ASSERT(!(currElement > lastElement));
566
Q_UNUSED(lastElement);
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
currBez = nextBezier(path, &currElement, &bezLength);
currLength += bezLength;
epc = currLength / pathLength;
}
prevBez.element = currElement;
prevBez.bezLength = bezLength;
prevBez.currLength = currLength;
prevBez.bezier = currBez;
prevBez.p = p;
prevBez.isValid = true;
qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
if (angle) {
qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
*angle = QLineF(0, 0, m1, m2).angle();
}
return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
}
prevOrigPercent = point.origpercent;
prevPercent = point.percent;
}
return QPointF(0,0);
}
//ideally this should be merged with forwardsPointAt
596
QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
597
598
599
600
{
if (pathLength <= 0 || qIsNaN(pathLength))
return path.pointAtPercent(0);
601
const int firstElement = 1; //element 0 is always a MoveTo, which we ignore
602
bool haveCachedBez = prevBez.isValid;
603
int currElement = haveCachedBez ? prevBez.element : path.elementCount();
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
qreal prevLength = currLength - bezLength;
qreal epc = prevLength / pathLength;
for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
qreal percent = p;
const AttributePoint &point = attributePoints.at(ii);
const AttributePoint &prevPoint = attributePoints.at(ii-1);
if (percent > prevPoint.percent || ii == 1) {
qreal elementPercent = (percent - prevPoint.percent);
qreal spc = prevPoint.origpercent + elementPercent * point.scale;
while (spc < epc) {
620
Q_ASSERT(!(currElement < firstElement));
621
Q_UNUSED(firstElement);
622
currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
623
624
625
//special case for first element is to avoid floating point math
//causing an epc that never hits 0.
currLength = (currElement == firstElement) ? bezLength : prevLength;
626
627
prevLength = currLength - bezLength;
epc = prevLength / pathLength;
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
}
prevBez.element = currElement;
prevBez.bezLength = bezLength;
prevBez.currLength = currLength;
prevBez.bezier = currBez;
prevBez.p = p;
prevBez.isValid = true;
qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
if (angle) {
qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
*angle = QLineF(0, 0, m1, m2).angle();
}
return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
}
}
return QPointF(0,0);
}
651
QPointF QQuickPath::pointAt(qreal p) const
652
{
653
Q_D(const QQuickPath);
654
655
656
657
658
if (d->_pointCache.isEmpty()) {
createPointCache();
if (d->_pointCache.isEmpty())
return QPointF();
}
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
const int pointCacheSize = d->_pointCache.size();
qreal idxf = p*pointCacheSize;
int idx1 = qFloor(idxf);
qreal delta = idxf - idx1;
if (idx1 >= pointCacheSize)
idx1 = pointCacheSize - 1;
else if (idx1 < 0)
idx1 = 0;
if (delta == 0.0)
return d->_pointCache.at(idx1);
// interpolate between the two points.
int idx2 = qCeil(idxf);
if (idx2 >= pointCacheSize)
idx2 = pointCacheSize - 1;
else if (idx2 < 0)
idx2 = 0;
QPointF p1 = d->_pointCache.at(idx1);
QPointF p2 = d->_pointCache.at(idx2);
QPointF pos = p1 * (1.0-delta) + p2 * delta;
return pos;
684
685
}
686
qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
687
{
688
Q_D(const QQuickPath);
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
if (percent < 0 || percent > 1)
return 0;
for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
const AttributePoint &point = d->_attributePoints.at(ii);
if (point.percent == percent) {
return point.values.value(name);
} else if (point.percent > percent) {
qreal lastValue =
ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
qreal lastPercent =
ii?(d->_attributePoints.at(ii - 1).percent):0;
qreal curValue = point.values.value(name);
qreal curPercent = point.percent;
return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
}
}
return 0;
}
/****************************************************************************/
714
qreal QQuickCurve::x() const
715
{
716
return _x.isNull ? 0 : _x.value;
717
718
}
719
void QQuickCurve::setX(qreal x)
720
{
721
if (_x.isNull || _x != x) {
722
723
724
725
726
727
_x = x;
emit xChanged();
emit changed();
}
}
728
bool QQuickCurve::hasX()
729
730
731
732
{
return _x.isValid();
}
733
qreal QQuickCurve::y() const
734
{
735
return _y.isNull ? 0 : _y.value;
736
737
}
738
void QQuickCurve::setY(qreal y)
739
{
740
if (_y.isNull || _y != y) {
741
742
743
744
745
746
_y = y;
emit yChanged();
emit changed();
}
}
747
bool QQuickCurve::hasY()
748
749
750
751
{
return _y.isValid();
}
752
qreal QQuickCurve::relativeX() const
753
754
755
756
{
return _relativeX;
}
757
void QQuickCurve::setRelativeX(qreal x)
758
759
760
761
762
763
764
765
{
if (_relativeX.isNull || _relativeX != x) {
_relativeX = x;
emit relativeXChanged();
emit changed();
}
}
766
bool QQuickCurve::hasRelativeX()
767
768
769
770
{
return _relativeX.isValid();
}
771
qreal QQuickCurve::relativeY() const
772
773
774
775
{
return _relativeY;
}
776
void QQuickCurve::setRelativeY(qreal y)
777
778
779
780
781
782
783
784
{
if (_relativeY.isNull || _relativeY != y) {
_relativeY = y;
emit relativeYChanged();
emit changed();
}
}
785
bool QQuickCurve::hasRelativeY()
786
787
788
789
{
return _relativeY.isValid();
}
790
791
792
/****************************************************************************/
/*!
793
\qmlclass PathAttribute QQuickPathAttribute
794
\inqmlmodule QtQuick 2
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
\ingroup qml-view-elements
\brief The PathAttribute allows setting an attribute at a given position in a Path.
The PathAttribute object allows attributes consisting of a name and
a value to be specified for various points along a path. The
attributes are exposed to the delegate as
\l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
The value of an attribute at any particular point along the path is interpolated
from the PathAttributes bounding that point.
The example below shows a path with the items scaled to 30% with
opacity 50% at the top of the path and scaled 100% with opacity
100% at the bottom. Note the use of the PathView.iconScale and
PathView.iconOpacity attached properties to set the scale and opacity
of the delegate.
\table
\row
813
814
\li \image declarative-pathattribute.png
\li
815
\snippet doc/src/snippets/qml/pathview/pathattributes.qml 0
816
817
818
819
820
821
822
823
824
(see the PathView documentation for the specification of ContactModel.qml
used for ContactModel above.)
\endtable
\sa Path
*/
/*!
825
\qmlproperty string QtQuick2::PathAttribute::name
826
827
828
829
830
831
832
833
834
835
836
837
838
839
This property holds the name of the attribute to change.
This attribute will be available to the delegate as PathView.<name>
Note that using an existing Item property name such as "opacity" as an
attribute is allowed. This is because path attributes add a new
\l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
which in no way clashes with existing properties.
*/
/*!
the name of the attribute to change.
*/
840
QString QQuickPathAttribute::name() const
841
842
843
844
{
return _name;
}
845
void QQuickPathAttribute::setName(const QString &name)
846
847
848
849
850
851
852
853
{
if (_name == name)
return;
_name = name;
emit nameChanged();
}
/*!
854
\qmlproperty real QtQuick2::PathAttribute::value
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
This property holds the value for the attribute.
The value specified can be used to influence the visual appearance
of an item along the path. For example, the following Path specifies
an attribute named \e itemRotation, which has the value \e 0 at the
beginning of the path, and the value 90 at the end of the path.
\qml
Path {
startX: 0
startY: 0
PathAttribute { name: "itemRotation"; value: 0 }
PathLine { x: 100; y: 100 }
PathAttribute { name: "itemRotation"; value: 90 }
}
\endqml
In our delegate, we can then bind the \e rotation property to the
\l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
\e PathView.itemRotation created for this attribute.
\qml
Rectangle {
width: 10; height: 10
rotation: PathView.itemRotation
}
\endqml
As each item is positioned along the path, it will be rotated accordingly:
an item at the beginning of the path with be not be rotated, an item at
the end of the path will be rotated 90 degrees, and an item mid-way along
the path will be rotated 45 degrees.
*/
/*!
the new value of the attribute.
*/
892
qreal QQuickPathAttribute::value() const
893
894
895
896
{
return _value;
}
897
void QQuickPathAttribute::setValue(qreal value)
898
899
900
901
902
903
904
905
906
907
908
{
if (_value != value) {
_value = value;
emit valueChanged();
emit changed();
}
}
/****************************************************************************/
/*!
909
\qmlclass PathLine QQuickPathLine
910
\inqmlmodule QtQuick 2
911
912
913
914
915
916
917
918
919
920
921
922
923
\ingroup qml-view-elements
\brief The PathLine defines a straight line.
The example below creates a path consisting of a straight line from
0,100 to 200,100:
\qml
Path {
startX: 0; startY: 100
PathLine { x: 200; y: 100 }
}
\endqml
924
\sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
925
926
927
*/
/*!
928
929
\qmlproperty real QtQuick2::PathLine::x
\qmlproperty real QtQuick2::PathLine::y
930
931
Defines the end point of the line.
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
\sa relativeX, relativeY
*/
/*!
\qmlproperty real QtQuick2::PathLine::relativeX
\qmlproperty real QtQuick2::PathLine::relativeY
Defines the end point of the line relative to its start.
If both a relative and absolute end position are specified for a single axis, the relative
position will be used.
Relative and absolute positions can be mixed, for example it is valid to set a relative x
and an absolute y.
\sa x, y
949
950
*/
951
inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
952
{
953
QQuickCurve *curve = data.curves.at(data.index);
954
955
956
957
958
bool isEnd = data.index == data.curves.size() - 1;
return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
}
959
void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
960
961
{
path.lineTo(positionForCurve(data, path.currentPosition()));
962
963
964
965
966
}
/****************************************************************************/
/*!
967
\qmlclass PathQuad QQuickPathQuad
968
\inqmlmodule QtQuick 2
969
970
971
972
973
974
\ingroup qml-view-elements
\brief The PathQuad defines a quadratic Bezier curve with a control point.
The following QML produces the path shown below:
\table
\row
975
976
\li \image declarative-pathquad.png
\li
977
978
979
980
981
982
983
984
\qml
Path {
startX: 0; startY: 0
PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
}
\endqml
\endtable
985
\sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg
986
987
988
*/
/*!
989
990
\qmlproperty real QtQuick2::PathQuad::x
\qmlproperty real QtQuick2::PathQuad::y
991
992
Defines the end point of the curve.
993
994
995
996
997
998
999
1000
\sa relativeX, relativeY
*/
/*!
\qmlproperty real QtQuick2::PathQuad::relativeX
\qmlproperty real QtQuick2::PathQuad::relativeY