Qt 4.8
qdeclarativepath.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "private/qdeclarativepath_p.h"
43 #include "private/qdeclarativepath_p_p.h"
44 
45 #include <QSet>
46 #include <QTime>
47 
48 #include <private/qbezier_p.h>
49 #include <QtCore/qmath.h>
50 #include <QtCore/qnumeric.h>
51 
53 
90  : QObject(*(new QDeclarativePathPrivate), parent)
91 {
92 }
93 
95 {
96 }
97 
107 {
108  Q_D(const QDeclarativePath);
109  return d->startX;
110 }
111 
113 {
115  if (qFuzzyCompare(x, d->startX))
116  return;
117  d->startX = x;
119  processPath();
120 }
121 
123 {
124  Q_D(const QDeclarativePath);
125  return d->startY;
126 }
127 
129 {
131  if (qFuzzyCompare(y, d->startY))
132  return;
133  d->startY = y;
135  processPath();
136 }
137 
146 {
147  Q_D(const QDeclarativePath);
148  return d->closed;
149 }
150 
173 {
175  return QDeclarativeListProperty<QDeclarativePathElement>(this, d->_pathElements);
176 }
177 
178 void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
179 {
181  if (!idx)
182  return;
183 
184  qreal lastValue = 0;
185  qreal lastPercent = 0;
186  int search = idx - 1;
187  while(search >= 0) {
188  const AttributePoint &point = d->_attributePoints.at(search);
189  if (point.values.contains(name)) {
190  lastValue = point.values.value(name);
191  lastPercent = point.origpercent;
192  break;
193  }
194  --search;
195  }
196 
197  ++search;
198 
199  const AttributePoint &curPoint = d->_attributePoints.at(idx);
200 
201  for (int ii = search; ii < idx; ++ii) {
202  AttributePoint &point = d->_attributePoints[ii];
203 
204  qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
205  point.values.insert(name, val);
206  }
207 }
208 
210 {
212  const AttributePoint &first = d->_attributePoints.first();
213  qreal val = first.values.value(name);
214  for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
215  const AttributePoint &point = d->_attributePoints.at(ii);
216  if (point.values.contains(name)) {
217  for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
218  AttributePoint &setPoint = d->_attributePoints[jj];
219  setPoint.values.insert(name, val);
220  }
221  return;
222  }
223  }
224 }
225 
227 {
229 
230  if (!d->componentComplete)
231  return;
232 
233  d->_pointCache.clear();
234  d->_attributePoints.clear();
235  d->_path = QPainterPath();
236 
237  AttributePoint first;
238  for (int ii = 0; ii < d->_attributes.count(); ++ii)
239  first.values[d->_attributes.at(ii)] = 0;
240  d->_attributePoints << first;
241 
242  d->_path.moveTo(d->startX, d->startY);
243 
244  QDeclarativeCurve *lastCurve = 0;
245  foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
246  if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(pathElement)) {
247  curve->addToPath(d->_path);
248  AttributePoint p;
249  p.origpercent = d->_path.length();
250  d->_attributePoints << p;
251  lastCurve = curve;
252  } else if (QDeclarativePathAttribute *attribute = qobject_cast<QDeclarativePathAttribute *>(pathElement)) {
253  AttributePoint &point = d->_attributePoints.last();
254  point.values[attribute->name()] = attribute->value();
255  interpolate(d->_attributePoints.count() - 1, attribute->name(), attribute->value());
256  } else if (QDeclarativePathPercent *percent = qobject_cast<QDeclarativePathPercent *>(pathElement)) {
257  AttributePoint &point = d->_attributePoints.last();
258  point.values[QLatin1String("_qfx_percent")] = percent->value();
259  interpolate(d->_attributePoints.count() - 1, QLatin1String("_qfx_percent"), percent->value());
260  }
261  }
262 
263  // Fixup end points
264  const AttributePoint &last = d->_attributePoints.last();
265  for (int ii = 0; ii < d->_attributes.count(); ++ii) {
266  if (!last.values.contains(d->_attributes.at(ii)))
267  endpoint(d->_attributes.at(ii));
268  }
269 
270  // Adjust percent
271  qreal length = d->_path.length();
272  qreal prevpercent = 0;
273  qreal prevorigpercent = 0;
274  for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
275  const AttributePoint &point = d->_attributePoints.at(ii);
276  if (point.values.contains(QLatin1String("_qfx_percent"))) { //special string for QDeclarativePathPercent
277  if ( ii > 0) {
278  qreal scale = (d->_attributePoints[ii].origpercent/length - prevorigpercent) /
279  (point.values.value(QLatin1String("_qfx_percent"))-prevpercent);
280  d->_attributePoints[ii].scale = scale;
281  }
282  d->_attributePoints[ii].origpercent /= length;
283  d->_attributePoints[ii].percent = point.values.value(QLatin1String("_qfx_percent"));
284  prevorigpercent = d->_attributePoints[ii].origpercent;
285  prevpercent = d->_attributePoints[ii].percent;
286  } else {
287  d->_attributePoints[ii].origpercent /= length;
288  d->_attributePoints[ii].percent = d->_attributePoints[ii].origpercent;
289  }
290  }
291 
292  d->closed = lastCurve && d->startX == lastCurve->x() && d->startY == lastCurve->y();
293 
294  emit changed();
295 }
296 
298 {
300  d->componentComplete = false;
301 }
302 
304 {
306  QSet<QString> attrs;
307  d->componentComplete = true;
308 
309  // First gather up all the attributes
310  foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
311  if (QDeclarativePathAttribute *attribute =
312  qobject_cast<QDeclarativePathAttribute *>(pathElement))
313  attrs.insert(attribute->name());
314  }
315  d->_attributes = attrs.toList();
316 
317  processPath();
318 
319  foreach (QDeclarativePathElement *pathElement, d->_pathElements)
320  connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
321 }
322 
324 {
325  Q_D(const QDeclarativePath);
326  return d->_path;
327 }
328 
330 {
331  Q_D(const QDeclarativePath);
332  if (!d->componentComplete) {
333  QSet<QString> attrs;
334 
335  // First gather up all the attributes
336  foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
337  if (QDeclarativePathAttribute *attribute =
338  qobject_cast<QDeclarativePathAttribute *>(pathElement))
339  attrs.insert(attribute->name());
340  }
341  return attrs.toList();
342  }
343  return d->_attributes;
344 }
345 
346 static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength)
347 {
348  const int lastElement = path.elementCount() - 1;
349  for (int i=*from; i <= lastElement; ++i) {
350  const QPainterPath::Element &e = path.elementAt(i);
351 
352  switch (e.type) {
354  break;
356  {
357  QLineF line(path.elementAt(i-1), e);
358  *bezLength = line.length();
359  QPointF a = path.elementAt(i-1);
360  QPointF delta = e - a;
361  *from = i+1;
362  return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
363  }
365  {
366  QBezier b = QBezier::fromPoints(path.elementAt(i-1),
367  e,
368  path.elementAt(i+1),
369  path.elementAt(i+2));
370  *bezLength = b.length();
371  *from = i+3;
372  return b;
373  }
374  default:
375  break;
376  }
377  }
378  *from = lastElement;
379  *bezLength = 0;
380  return QBezier();
381 }
382 
384 {
385  Q_D(const QDeclarativePath);
386  qreal pathLength = d->_path.length();
387  if (pathLength <= 0 || qIsNaN(pathLength))
388  return;
389  // more points means less jitter between items as they move along the
390  // path, but takes longer to generate
391  const int points = qCeil(pathLength*5);
392  const int lastElement = d->_path.elementCount() - 1;
393  d->_pointCache.resize(points+1);
394 
395  int currElement = 0;
396  qreal bezLength = 0;
397  QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
398  qreal currLength = bezLength;
399  qreal epc = currLength / pathLength;
400 
401  for (int i = 0; i < d->_pointCache.size(); i++) {
402  //find which set we are in
403  qreal prevPercent = 0;
404  qreal prevOrigPercent = 0;
405  for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
406  qreal percent = qreal(i)/points;
407  const AttributePoint &point = d->_attributePoints.at(ii);
408  if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
409  qreal elementPercent = (percent - prevPercent);
410 
411  qreal spc = prevOrigPercent + elementPercent * point.scale;
412 
413  while (spc > epc) {
414  if (currElement > lastElement)
415  break;
416  currBez = nextBezier(d->_path, &currElement, &bezLength);
417  if (bezLength == 0.0) {
418  currLength = pathLength;
419  epc = 1.0;
420  break;
421  }
422  currLength += bezLength;
423  epc = currLength / pathLength;
424  }
425  qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
426  d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
427  break;
428  }
429  prevOrigPercent = point.origpercent;
430  prevPercent = point.percent;
431  }
432  }
433 }
434 
436 {
437  Q_D(const QDeclarativePath);
438  if (d->_pointCache.isEmpty()) {
440  if (d->_pointCache.isEmpty())
441  return QPointF();
442  }
443  int idx = qRound(p*d->_pointCache.size());
444  if (idx >= d->_pointCache.size())
445  idx = d->_pointCache.size() - 1;
446  else if (idx < 0)
447  idx = 0;
448  return d->_pointCache.at(idx);
449 }
450 
452 {
453  Q_D(const QDeclarativePath);
454  if (percent < 0 || percent > 1)
455  return 0;
456 
457  for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
458  const AttributePoint &point = d->_attributePoints.at(ii);
459 
460  if (point.percent == percent) {
461  return point.values.value(name);
462  } else if (point.percent > percent) {
463  qreal lastValue =
464  ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
465  qreal lastPercent =
466  ii?(d->_attributePoints.at(ii - 1).percent):0;
467  qreal curValue = point.values.value(name);
468  qreal curPercent = point.percent;
469 
470  return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
471  }
472  }
473 
474  return 0;
475 }
476 
477 /****************************************************************************/
478 
480 {
481  return _x;
482 }
483 
485 {
486  if (_x != x) {
487  _x = x;
488  emit xChanged();
489  emit changed();
490  }
491 }
492 
494 {
495  return _y;
496 }
497 
499 {
500  if (_y != y) {
501  _y = y;
502  emit yChanged();
503  emit changed();
504  }
505 }
506 
507 /****************************************************************************/
508 
561 {
562  return _name;
563 }
564 
566 {
567  if (_name == name)
568  return;
569  _name = name;
570  emit nameChanged();
571 }
572 
615 {
616  return _value;
617 }
618 
620 {
621  if (_value != value) {
622  _value = value;
623  emit valueChanged();
624  emit changed();
625  }
626 }
627 
628 /****************************************************************************/
629 
663 {
664  path.lineTo(x(), y());
665 }
666 
667 /****************************************************************************/
668 
717 {
718  return _controlX;
719 }
720 
722 {
723  if (_controlX != x) {
724  _controlX = x;
725  emit controlXChanged();
726  emit changed();
727  }
728 }
729 
730 
735 {
736  return _controlY;
737 }
738 
740 {
741  if (_controlY != y) {
742  _controlY = y;
743  emit controlYChanged();
744  emit changed();
745  }
746 }
747 
749 {
750  path.quadTo(controlX(), controlY(), x(), y());
751 }
752 
753 /****************************************************************************/
754 
803 {
804  return _control1X;
805 }
806 
808 {
809  if (_control1X != x) {
810  _control1X = x;
811  emit control1XChanged();
812  emit changed();
813  }
814 }
815 
817 {
818  return _control1Y;
819 }
820 
822 {
823  if (_control1Y != y) {
824  _control1Y = y;
825  emit control1YChanged();
826  emit changed();
827  }
828 }
829 
840 {
841  return _control2X;
842 }
843 
845 {
846  if (_control2X != x) {
847  _control2X = x;
848  emit control2XChanged();
849  emit changed();
850  }
851 }
852 
854 {
855  return _control2Y;
856 }
857 
859 {
860  if (_control2Y != y) {
861  _control2Y = y;
862  emit control2YChanged();
863  emit changed();
864  }
865 }
866 
868 {
869  path.cubicTo(control1X(), control1Y(), control2X(), control2Y(), x(), y());
870 }
871 
872 /****************************************************************************/
873 
959 {
960  return _value;
961 }
962 
964 {
965  if (_value != value) {
966  _value = value;
967  emit valueChanged();
968  emit changed();
969  }
970 }
ElementType type
the type of element
Definition: qpainterpath.h:81
qreal startX() const
The QPainterPath::Element class specifies the position and type of a subpath.
Definition: qpainterpath.h:77
qreal controlY() const
static QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength)
double qreal
Definition: qglobal.h:1193
qreal control2X() const
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
QDeclarativeParserStatus ** d
qreal length() const
Returns the length of the line.
Definition: qline.cpp:698
virtual void componentComplete()
Invoked after the root component that caused this instantiation has completed construction.
int qCeil(qreal v)
Definition: qmath.h:63
QDeclarativePath(QObject *parent=0)
void interpolate(int idx, const QString &name, qreal value)
The QPainterPath class provides a container for painting operations, enabling graphical shapes to be ...
Definition: qpainterpath.h:67
qreal x() const
#define SLOT(a)
Definition: qobjectdefs.h:226
The QPointF class defines a point in the plane using floating point precision.
Definition: qpoint.h:214
static Q_DECL_CONSTEXPR bool qFuzzyCompare(double p1, double p2)
Definition: qglobal.h:2030
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
long ASN1_INTEGER_get ASN1_INTEGER * a
QPointF pointAt(qreal t) const
Definition: qbezier_p.h:163
virtual void addToPath(QPainterPath &)
The QString class provides a Unicode character string.
Definition: qstring.h:83
QPointF pointAt(qreal) const
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
qreal y() const
qreal length(qreal error=0.01) const
Definition: qbezier.cpp:529
void addToPath(QPainterPath &path)
#define Q_D(Class)
Definition: qglobal.h:2482
bool contains(const Key &key) const
Returns true if the hash contains an item with the key; otherwise returns false.
Definition: qhash.h:872
qreal controlX() const
const QPainterPath::Element & elementAt(int i) const
Returns the element at the given index in the painter path.
Definition: qpainterpath.h:402
const T value(const Key &key) const
Returns the value associated with the key.
Definition: qhash.h:606
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition: qhash.h:753
The QLineF class provides a two-dimensional vector using floating point precision.
Definition: qline.h:212
virtual void componentComplete()=0
Invoked after the root component that caused this instantiation has completed construction.
void lineTo(const QPointF &p)
Adds a straight line from the current position to the given endPoint.
#define SIGNAL(a)
Definition: qobjectdefs.h:227
static QBezier fromPoints(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4)
Definition: qbezier.cpp:71
qreal control1Y() const
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
qreal startY() const
void createPointCache() const
QList< T > toList() const
Definition: qset.h:296
static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Creates a connection of the given type from the signal in the sender object to the method in the rece...
Definition: qobject.cpp:2580
const char * name
#define emit
Definition: qobjectdefs.h:76
The QStringList class provides a list of strings.
Definition: qstringlist.h:66
const_iterator insert(const T &value)
Definition: qset.h:179
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal...
Definition: qstring.h:654
void addToPath(QPainterPath &path)
Q_CORE_EXPORT bool qIsNaN(double d)
Returns true if the double {d} is not a number (NaN).
Definition: qnumeric.cpp:55
QDeclarativeListProperty< QDeclarativePathElement > pathElements()
void endpoint(const QString &name)
virtual void classBegin()
Invoked after class creation, but before any properties have been set.
QPainterPath path() const
Q_DECL_CONSTEXPR const T & qBound(const T &min, const T &val, const T &max)
Definition: qglobal.h:1219
void setValue(qreal value)
void setStartY(qreal y)
void quadTo(const QPointF &ctrlPt, const QPointF &endPt)
Adds a quadratic Bezier curve between the current position and the given endPoint with the control po...
void cubicTo(const QPointF &ctrlPt1, const QPointF &ctrlPt2, const QPointF &endPt)
Adds a cubic Bezier curve between the current position and the given endPoint using the control point...
void setName(const QString &name)
int elementCount() const
Returns the number of path elements in the painter path.
Definition: qpainterpath.h:397
void setStartX(qreal x)
void addToPath(QPainterPath &path)
qreal control2Y() const
qreal attributeAt(const QString &, qreal) const
Q_DECL_CONSTEXPR int qRound(qreal d)
Definition: qglobal.h:1203
QStringList attributes() const
qreal control1X() const