aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/util/qquickpath.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/util/qquickpath.cpp')
-rw-r--r--src/quick/util/qquickpath.cpp353
1 files changed, 343 insertions, 10 deletions
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 60d725d650..61319c388c 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
This type is the base for all path types. It cannot
be instantiated.
- \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc,
+ \sa Path, PathAttribute, PathPercent, PathLine, PathPolyline, PathQuad, PathCubic, PathArc,
PathAngleArc, PathCurve, PathSvg
*/
@@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE
\ingroup qtquick-animation-paths
\brief Defines a path for use by \l PathView and \l Shape.
- A Path is composed of one or more path segments - PathLine, PathQuad,
+ A Path is composed of one or more path segments - PathLine, PathPolyline, PathQuad,
PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg.
The spacing of the items along the Path can be adjusted via a
@@ -104,6 +104,17 @@ QT_BEGIN_NAMESPACE
\li Yes
\li Yes
\row
+ \li PathPolyline
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \li PathMultiLine
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
\li PathQuad
\li Yes
\li Yes
@@ -156,7 +167,7 @@ QT_BEGIN_NAMESPACE
\note Path is a non-visual type; it does not display anything on its own.
To draw a path, use \l Shape.
- \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg
+ \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathPolyline, PathMove, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg
*/
QQuickPath::QQuickPath(QObject *parent)
: QObject(*(new QQuickPathPrivate), parent)
@@ -240,6 +251,8 @@ bool QQuickPath::isClosed() const
A path can contain the following path objects:
\list
\li \l PathLine - a straight line to a given position.
+ \li \l PathPolyline - a polyline specified as a list of coordinates.
+ \li \l PathMultiline - a list of polylines specified as a list of lists of coordinates.
\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.
@@ -407,6 +420,19 @@ void QQuickPath::processPath()
emit changed();
}
+inline static void scalePath(QPainterPath &path, const QSizeF &scale)
+{
+ const qreal xscale = scale.width();
+ const qreal yscale = scale.height();
+ if (xscale == 1 && yscale == 1)
+ return;
+
+ for (int i = 0; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &element = path.elementAt(i);
+ path.setElementPositionAt(i, element.x * xscale, element.y * yscale);
+ }
+}
+
QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
{
Q_D(QQuickPath);
@@ -465,7 +491,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
d->_attributePoints.last().values[percentString] = 1;
interpolate(d->_attributePoints.count() - 1, percentString, 1);
}
-
+ scalePath(path, d->scale);
// Adjust percent
qreal length = path.length();
@@ -491,7 +517,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
if (closed) {
QPointF end = path.currentPosition();
- *closed = length > 0 && startX == end.x() && startY == end.y();
+ *closed = length > 0 && startX * d->scale.width() == end.x() && startY * d->scale.height() == end.y();
}
pathLength = length;
@@ -525,6 +551,7 @@ QPainterPath QQuickPath::createShapePath(const QPointF &startPoint, const QPoint
QPointF end = path.currentPosition();
*closed = startX == end.x() && startY == end.y();
}
+ scalePath(path, d->scale);
// Note: Length of paths inside ShapePath is not used, so currently
// length is always 0. This avoids potentially heavy path.length()
@@ -570,7 +597,7 @@ void QQuickPath::gatherAttributes()
attributes.insert(attribute->name());
}
- d->_attributes = attributes.toList();
+ d->_attributes = attributes.values();
}
void QQuickPath::componentComplete()
@@ -603,7 +630,7 @@ QStringList QQuickPath::attributes() const
qobject_cast<QQuickPathAttribute *>(pathElement))
attrs.insert(attribute->name());
}
- return attrs.toList();
+ return attrs.values();
}
return d->_attributes;
}
@@ -723,6 +750,33 @@ void QQuickPath::invalidateSequentialHistory() const
d->prevBez.isValid = false;
}
+/*!
+ \qmlproperty size QtQuick::Path::scale
+
+ This property holds the scale factor for the path.
+ The width and height of \a scale can be different, to
+ achieve anisotropic scaling.
+
+ \note Setting this property will not affect the border width.
+
+ \since QtQuick 2.14
+*/
+QSizeF QQuickPath::scale() const
+{
+ Q_D(const QQuickPath);
+ return d->scale;
+}
+
+void QQuickPath::setScale(const QSizeF &scale)
+{
+ Q_D(QQuickPath);
+ if (scale == d->scale)
+ return;
+ d->scale = scale;
+ emit scaleChanged();
+ processPath();
+}
+
QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
{
Q_D(const QQuickPath);
@@ -852,9 +906,28 @@ QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &path
return QPointF(0,0);
}
-QPointF QQuickPath::pointAt(qreal p) const
+/*!
+ \qmlmethod point Path::pointAtPercent(real t)
+
+ Returns the point at the percentage \a t of the current path.
+ The argument \a t has to be between 0 and 1.
+
+ \note Similarly to other percent methods in \l QPainterPath,
+ the percentage measurement is not linear with regards to the length,
+ if curves are present in the path.
+ When curves are present, the percentage argument is mapped to the \c t
+ parameter of the Bezier equations.
+
+ \sa QPainterPath::pointAt
+
+ \since QtQuick 2.14
+*/
+QPointF QQuickPath::pointAtPercent(qreal t) const
{
Q_D(const QQuickPath);
+ if (d->isShapePath) // this since ShapePath does not calculate the length at all,
+ return d->_path.pointAtPercent(t); // in order to be faster.
+
if (d->_pointCache.isEmpty()) {
createPointCache();
if (d->_pointCache.isEmpty())
@@ -862,7 +935,7 @@ QPointF QQuickPath::pointAt(qreal p) const
}
const int segmentCount = d->_pointCache.size() - 1;
- qreal idxf = p*segmentCount;
+ qreal idxf = t*segmentCount;
int idx1 = qFloor(idxf);
qreal delta = idxf - idx1;
if (idx1 > segmentCount)
@@ -1127,7 +1200,7 @@ void QQuickPathAttribute::setValue(qreal value)
}
\endqml
- \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
+ \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline
*/
/*!
@@ -2286,6 +2359,266 @@ void QQuickPathPercent::setValue(qreal value)
emit changed();
}
}
+
+/*!
+ \qmltype PathPolyline
+ \instantiates QQuickPathPolyline
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Defines a polyline through a list of coordinates.
+ \since QtQuick 2.14
+
+ The example below creates a triangular path consisting of four vertices
+ on the edge of the containing Shape's bounding box.
+ Through the containing shape's \l scale property, the path will be
+ rescaled together with its containing shape.
+
+ \qml
+ PathPolyline {
+ id: ppl
+ path: [ Qt.point(0.0, 0.0),
+ Qt.point(1.0, 0.0),
+ Qt.point(0.5, 1.0),
+ Qt.point(0.0, 0.0)
+ ]
+ }
+ \endqml
+
+ \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline
+*/
+
+/*!
+ \qmlproperty point QtQuick::PathPolyline::start
+
+ This read-only property contains the beginning of the polyline.
+*/
+
+/*!
+ \qmlproperty list<point> QtQuick::PathPolyline::path
+
+ This property defines the vertices of the polyline.
+
+ It can be a JS array of points constructed with \c Qt.point(),
+ a QList or QVector of QPointF, or QPolygonF.
+ If you are binding this to a custom property in some C++ object,
+ QPolygonF is the most appropriate type to use.
+*/
+
+QQuickPathPolyline::QQuickPathPolyline(QObject *parent) : QQuickCurve(parent)
+{
+}
+
+QVariant QQuickPathPolyline::path() const
+{
+ return QVariant::fromValue(m_path);
+}
+
+void QQuickPathPolyline::setPath(const QVariant &path)
+{
+ if (path.type() == QVariant::PolygonF) {
+ setPath(path.value<QPolygonF>());
+ } else if (path.canConvert<QVector<QPointF>>()) {
+ setPath(path.value<QVector<QPointF>>());
+ } else if (path.canConvert<QVariantList>()) {
+ // This handles cases other than QPolygonF or QVector<QPointF>, such as
+ // QList<QPointF>, QVector<QPoint>, QVariantList of QPointF, QVariantList of QPoint.
+ QVector<QPointF> pathList;
+ QVariantList vl = path.value<QVariantList>();
+ // If path is a QJSValue, e.g. coming from a JS array of Qt.point() in QML,
+ // then path.value<QVariantList>() is inefficient.
+ // TODO We should be able to iterate over path.value<QSequentialIterable>() eventually
+ for (const QVariant &v : vl)
+ pathList.append(v.toPointF());
+ setPath(pathList);
+ } else {
+ qWarning() << "PathPolyline: path of type" << path.type() << "not supported";
+ }
+}
+
+void QQuickPathPolyline::setPath(const QVector<QPointF> &path)
+{
+ if (m_path != path) {
+ const QPointF &oldStart = start();
+ m_path = path;
+ const QPointF &newStart = start();
+ emit pathChanged();
+ if (oldStart != newStart)
+ emit startChanged();
+ emit changed();
+ }
+}
+
+QPointF QQuickPathPolyline::start() const
+{
+ if (m_path.size()) {
+ const QPointF &p = m_path.first();
+ return p;
+ }
+ return QPointF();
+}
+
+void QQuickPathPolyline::addToPath(QPainterPath &path, const QQuickPathData &/*data*/)
+{
+ if (m_path.size() < 2)
+ return;
+
+ path.moveTo(m_path.first());
+ for (int i = 1; i < m_path.size(); ++i)
+ path.lineTo(m_path.at(i));
+}
+
+
+/*!
+ \qmltype PathMultiline
+ \instantiates QQuickPathMultiline
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Defines a set of polylines through a list of lists of coordinates.
+ \since QtQuick 2.14
+
+ This element allows to define a list of polylines at once.
+ Each polyline in the list will be preceded by a \l{QPainterPath::moveTo}{moveTo}
+ command, effectively making each polyline a separate one.
+ The polylines in this list are supposed to be non-intersecting with each other.
+ In any case, when used in conjunction with a \l ShapePath, the containing ShapePath's
+ \l ShapePath::fillRule applies.
+ That is, with the default \c OddEvenFill and non intersecting shapes, the largest shape in the list defines an area to be filled;
+ areas where two shapes overlap are holes; areas where three shapes overlap are filled areas inside holes, etc.
+
+ The example below creates a high voltage symbol by adding each path
+ of the symbol to the list of paths.
+ The coordinates of the vertices are normalized, and through the containing shape's
+ \l scale property, the path will be rescaled together with its containing shape.
+
+ \qml
+ PathMultiline {
+ paths: [
+ [Qt.point(0.5, 0.06698),
+ Qt.point(1, 0.93301),
+ Qt.point(0, 0.93301),
+ Qt.point(0.5, 0.06698)],
+
+ [Qt.point(0.5, 0.12472),
+ Qt.point(0.95, 0.90414),
+ Qt.point(0.05, 0.90414),
+ Qt.point(0.5, 0.12472)],
+
+ [Qt.point(0.47131, 0.32986),
+ Qt.point(0.36229, 0.64789),
+ Qt.point(0.51492, 0.58590),
+ Qt.point(0.47563, 0.76014),
+ Qt.point(0.44950, 0.73590),
+ Qt.point(0.46292, 0.83392),
+ Qt.point(0.52162, 0.75190),
+ Qt.point(0.48531, 0.76230),
+ Qt.point(0.57529, 0.53189),
+ Qt.point(0.41261, 0.59189),
+ Qt.point(0.53001, 0.32786),
+ Qt.point(0.47131, 0.32986)]
+ ]
+ }
+ \endqml
+
+ \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
+*/
+
+/*!
+ \qmlproperty point QtQuick::PathMultiline::start
+
+ This read-only property contains the beginning of the polylines.
+*/
+
+/*!
+ \qmlproperty list<list<point>> QtQuick::PathMultiline::paths
+
+ This property defines the vertices of the polylines.
+
+ It can be a JS array of JS arrays of points constructed with \c Qt.point(),
+ a QList or QVector of QPolygonF, or QVector<QVector<QPointF>>.
+ If you are binding this to a custom property in some C++ object,
+ QVector<QPolygonF> or QVector<QVector<QPointF>> is the most
+ appropriate type to use.
+*/
+
+QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent)
+{
+}
+
+QVariant QQuickPathMultiline::paths() const
+{
+ return QVariant::fromValue(m_paths);
+}
+
+void QQuickPathMultiline::setPaths(const QVariant &paths)
+{
+ if (paths.canConvert<QVector<QPolygonF>>()) {
+ const QVector<QPolygonF> pathPolygons = paths.value<QVector<QPolygonF>>();
+ QVector<QVector<QPointF>> pathVectors;
+ for (const QPolygonF &p : pathPolygons)
+ pathVectors << p;
+ setPaths(pathVectors);
+ } else if (paths.canConvert<QVector<QVector<QPointF>>>()) {
+ setPaths(paths.value<QVector<QVector<QPointF>>>());
+ } else if (paths.canConvert<QVariantList>()) {
+ // This handles cases other than QVector<QPolygonF> or QVector<QVector<QPointF>>, such as
+ // QList<QVector<QPointF>>, QList<QList<QPointF>>, QVariantList of QVector<QPointF>,
+ // QVariantList of QVariantList of QPointF, QVector<QList<QPoint>> etc.
+ QVector<QVector<QPointF>> pathsList;
+ QVariantList vll = paths.value<QVariantList>();
+ for (const QVariant &v : vll) {
+ // If we bind a QVector<QPolygonF> property directly, rather than via QVariant,
+ // it will come through as QJSValue that can be converted to QVariantList of QPolygonF.
+ if (v.canConvert<QPolygonF>()) {
+ pathsList.append(v.value<QPolygonF>());
+ } else {
+ QVariantList vl = v.value<QVariantList>();
+ QVector<QPointF> l;
+ for (const QVariant &point : vl) {
+ if (point.canConvert<QPointF>())
+ l.append(point.toPointF());
+ }
+ if (l.size() >= 2)
+ pathsList.append(l);
+ }
+ }
+ setPaths(pathsList);
+ } else {
+ qWarning() << "PathMultiline: paths of type" << paths.type() << "not supported";
+ setPaths(QVector<QVector<QPointF>>());
+ }
+}
+
+void QQuickPathMultiline::setPaths(const QVector<QVector<QPointF>> &paths)
+{
+ if (m_paths != paths) {
+ const QPointF &oldStart = start();
+ m_paths = paths;
+ const QPointF &newStart = start();
+ emit pathsChanged();
+ if (oldStart != newStart)
+ emit startChanged();
+ emit changed();
+ }
+}
+
+QPointF QQuickPathMultiline::start() const
+{
+ if (m_paths.size())
+ return m_paths.first().first();
+ return QPointF();
+}
+
+void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
+{
+ if (!m_paths.size())
+ return;
+ for (const QVector<QPointF> &p: m_paths) {
+ path.moveTo(p.first());
+ for (int i = 1; i < p.size(); ++i)
+ path.lineTo(p.at(i));
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qquickpath_p.cpp"