diff options
Diffstat (limited to 'src/quick/scenegraph/util/qquadpath_p.h')
-rw-r--r-- | src/quick/scenegraph/util/qquadpath_p.h | 341 |
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 |