aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/util/qquadpath_p.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/util/qquadpath_p.h')
-rw-r--r--src/quick/scenegraph/util/qquadpath_p.h341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/quick/scenegraph/util/qquadpath_p.h b/src/quick/scenegraph/util/qquadpath_p.h
new file mode 100644
index 0000000000..98ef5b664c
--- /dev/null
+++ b/src/quick/scenegraph/util/qquadpath_p.h
@@ -0,0 +1,341 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUADPATH_P_H
+#define QQUADPATH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qrect.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qdebug.h>
+#include <QtGui/qvector2d.h>
+#include <QtGui/qpainterpath.h>
+#include <QtQuick/qtquickexports.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QQuadPath
+{
+public:
+ // This is a copy of the flags in QQuickShapePath ### TODO: use a common definition
+ enum PathHint : quint8 {
+ PathLinear = 0x1,
+ PathQuadratic = 0x2,
+ PathConvex = 0x4,
+ PathFillOnRight = 0x8,
+ PathSolid = 0x10,
+ PathNonIntersecting = 0x20,
+ PathNonOverlappingControlPointTriangles = 0x40
+ };
+ Q_DECLARE_FLAGS(PathHints, PathHint)
+
+ class Q_QUICK_EXPORT Element
+ {
+ public:
+ Element ()
+ : m_isSubpathStart(false), m_isSubpathEnd(false), m_isLine(false)
+ {
+ }
+
+ Element (QVector2D s, QVector2D c, QVector2D e)
+ : sp(s), cp(c), ep(e), m_isSubpathStart(false), m_isSubpathEnd(false), m_isLine(false)
+ {
+ }
+
+ bool isSubpathStart() const
+ {
+ return m_isSubpathStart;
+ }
+
+ bool isSubpathEnd() const
+ {
+ return m_isSubpathEnd;
+ }
+
+ bool isLine() const
+ {
+ return m_isLine;
+ }
+
+ bool isConvex() const
+ {
+ return m_curvatureFlags & Convex;
+ }
+
+ QVector2D startPoint() const
+ {
+ return sp;
+ }
+
+ QVector2D controlPoint() const
+ {
+ return cp;
+ }
+
+ QVector2D endPoint() const
+ {
+ return ep;
+ }
+
+ QVector2D midPoint() const
+ {
+ return isLine() ? 0.5f * (sp + ep) : (0.25f * sp) + (0.5f * cp) + (0.25 * ep);
+ }
+
+ /* For a curve, returns the control point. For a line, returns an arbitrary point on the
+ * inside side of the line (assuming the curvature has been set for the path). The point
+ * doesn't need to actually be inside the shape: it just makes for easier calculations
+ * later when it is at the same side as the fill. */
+ QVector2D referencePoint() const
+ {
+ if (isLine()) {
+ QVector2D normal(sp.y() - ep.y(), ep.x() - sp.x());
+ return m_curvatureFlags & Element::FillOnRight ? sp + normal : sp - normal;
+ } else {
+ return cp;
+ }
+ }
+
+ Element segmentFromTo(float t0, float t1) const;
+
+ Element reversed() const;
+
+ int childCount() const { return m_numChildren; }
+
+ int indexOfChild(int childNumber) const
+ {
+ Q_ASSERT(childNumber >= 0 && childNumber < childCount());
+ return -(m_firstChildIndex + 1 + childNumber);
+ }
+
+ QVector2D pointAtFraction(float t) const;
+
+ QVector2D tangentAtFraction(float t) const
+ {
+ return isLine() ? (ep - sp) : ((1 - t) * 2 * (cp - sp)) + (t * 2 * (ep - cp));
+ }
+
+ QVector2D normalAtFraction(float t) const
+ {
+ const QVector2D tan = tangentAtFraction(t);
+ return QVector2D(-tan.y(), tan.x());
+ }
+
+ float extent() const;
+
+ void setAsConvex(bool isConvex)
+ {
+ if (isConvex)
+ m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags | Element::Convex);
+ else
+ m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags & ~Element::Convex);
+ }
+
+ void setFillOnRight(bool isFillOnRight)
+ {
+ if (isFillOnRight)
+ m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags | Element::FillOnRight);
+ else
+ m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags & ~Element::FillOnRight);
+ }
+
+ bool isFillOnRight() const { return m_curvatureFlags & FillOnRight; }
+
+ bool isControlPointOnLeft() const
+ {
+ return isPointOnLeft(cp, sp, ep);
+ }
+
+ enum CurvatureFlags : quint8 {
+ CurvatureUndetermined = 0,
+ FillOnRight = 1,
+ Convex = 2
+ };
+
+ enum FillSide : quint8 {
+ FillSideUndetermined = 0,
+ FillSideRight = 1,
+ FillSideLeft = 2,
+ FillSideBoth = 3
+ };
+
+ private:
+ int intersectionsAtY(float y, float *fractions, bool swapXY = false) const;
+
+ QVector2D sp;
+ QVector2D cp;
+ QVector2D ep;
+ int m_firstChildIndex = 0;
+ quint8 m_numChildren = 0;
+ CurvatureFlags m_curvatureFlags = CurvatureUndetermined;
+ quint8 m_isSubpathStart : 1;
+ quint8 m_isSubpathEnd : 1;
+ quint8 m_isLine : 1;
+ friend class QQuadPath;
+ friend Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath::Element &);
+ };
+
+ void moveTo(const QVector2D &to)
+ {
+ m_subPathToStart = true;
+ m_currentPoint = to;
+ }
+
+ void lineTo(const QVector2D &to)
+ {
+ addElement({}, to, true);
+ }
+
+ void quadTo(const QVector2D &control, const QVector2D &to)
+ {
+ addElement(control, to);
+ }
+
+ Element &elementAt(int i)
+ {
+ return i < 0 ? m_childElements[-(i + 1)] : m_elements[i];
+ }
+
+ const Element &elementAt(int i) const
+ {
+ return i < 0 ? m_childElements[-(i + 1)] : m_elements[i];
+ }
+
+ int indexOfChildAt(int i, int childNumber) const
+ {
+ return elementAt(i).indexOfChild(childNumber);
+ }
+
+ QRectF controlPointRect() const;
+
+ Qt::FillRule fillRule() const { return m_windingFill ? Qt::WindingFill : Qt::OddEvenFill; }
+ void setFillRule(Qt::FillRule rule) { m_windingFill = (rule == Qt::WindingFill); }
+
+ void reserve(int size) { m_elements.reserve(size); }
+ int elementCount() const { return m_elements.size(); }
+ bool isEmpty() const { return m_elements.size() == 0; }
+ int elementCountRecursive() const;
+
+ static QQuadPath fromPainterPath(const QPainterPath &path, PathHints hints = {});
+ QPainterPath toPainterPath() const;
+ QString asSvgString() const;
+
+ QQuadPath subPathsClosed(bool *didClose = nullptr) const;
+ void addCurvatureData();
+ QQuadPath flattened() const;
+ QQuadPath dashed(qreal lineWidth, const QList<qreal> &dashPattern, qreal dashOffset = 0) const;
+ void splitElementAt(int index);
+ bool contains(const QVector2D &point) const;
+ bool contains(const QVector2D &point, int fromIndex, int toIndex) const;
+ Element::FillSide fillSideOf(int elementIdx, float elementT) const;
+
+ template<typename Func>
+ void iterateChildrenOf(Element &e, Func &&lambda)
+ {
+ const int lastChildIndex = e.m_firstChildIndex + e.childCount() - 1;
+ for (int i = e.m_firstChildIndex; i <= lastChildIndex; i++) {
+ Element &c = m_childElements[i];
+ if (c.childCount() > 0)
+ iterateChildrenOf(c, lambda);
+ else
+ lambda(c, -(i + 1));
+ }
+ }
+
+ template<typename Func>
+ void iterateChildrenOf(const Element &e, Func &&lambda) const
+ {
+ const int lastChildIndex = e.m_firstChildIndex + e.childCount() - 1;
+ for (int i = e.m_firstChildIndex; i <= lastChildIndex; i++) {
+ const Element &c = m_childElements[i];
+ if (c.childCount() > 0)
+ iterateChildrenOf(c, lambda);
+ else
+ lambda(c, -(i + 1));
+ }
+ }
+
+ template<typename Func>
+ void iterateElements(Func &&lambda)
+ {
+ for (int i = 0; i < m_elements.size(); i++) {
+ Element &e = m_elements[i];
+ if (e.childCount() > 0)
+ iterateChildrenOf(e, lambda);
+ else
+ lambda(e, i);
+ }
+ }
+
+ template<typename Func>
+ void iterateElements(Func &&lambda) const
+ {
+ for (int i = 0; i < m_elements.size(); i++) {
+ const Element &e = m_elements[i];
+ if (e.childCount() > 0)
+ iterateChildrenOf(e, lambda);
+ else
+ lambda(e, i);
+ }
+ }
+
+ static QVector2D closestPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
+ static bool isPointOnLeft(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
+ static bool isPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
+ static bool isPointNearLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
+
+ bool testHint(PathHint hint) const
+ {
+ return m_hints.testFlag(hint);
+ }
+
+ void setHint(PathHint hint, bool on = true)
+ {
+ m_hints.setFlag(hint, on);
+ }
+
+ PathHints pathHints() const
+ {
+ return m_hints;
+ }
+
+ void setPathHints(PathHints newHints)
+ {
+ m_hints = newHints;
+ }
+
+private:
+ void addElement(const QVector2D &control, const QVector2D &to, bool isLine = false);
+ void addElement(const Element &e);
+ Element::CurvatureFlags coordinateOrderOfElement(const Element &element) const;
+
+ friend Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath &);
+
+ QList<Element> m_elements;
+ QList<Element> m_childElements;
+ QVector2D m_currentPoint;
+ bool m_subPathToStart = true;
+ bool m_windingFill = false;
+ PathHints m_hints;
+
+ friend class QSGCurveProcessor;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuadPath::PathHints);
+
+Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath::Element &);
+Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath &);
+
+QT_END_NAMESPACE
+
+#endif