1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QQUADPATH_P_H |
5 | #define QQUADPATH_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists for the convenience |
12 | // of a number of Qt sources files. This header file may change from |
13 | // version to version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include <QtCore/qrect.h> |
19 | #include <QtCore/qlist.h> |
20 | #include <QtCore/qdebug.h> |
21 | #include <QtGui/qvector2d.h> |
22 | #include <QtGui/qpainterpath.h> |
23 | |
24 | QT_BEGIN_NAMESPACE |
25 | |
26 | class QQuadPath |
27 | { |
28 | public: |
29 | class Element |
30 | { |
31 | public: |
32 | Element () |
33 | : m_isSubpathStart(false), m_isSubpathEnd(false), m_isLine(false) |
34 | { |
35 | } |
36 | |
37 | bool isSubpathStart() const |
38 | { |
39 | return m_isSubpathStart; |
40 | } |
41 | |
42 | bool isSubpathEnd() const |
43 | { |
44 | return m_isSubpathEnd; |
45 | } |
46 | |
47 | bool isLine() const |
48 | { |
49 | return m_isLine; |
50 | } |
51 | |
52 | bool isConvex() const |
53 | { |
54 | return m_curvatureFlags & Convex; |
55 | } |
56 | |
57 | QVector2D startPoint() const |
58 | { |
59 | return sp; |
60 | } |
61 | |
62 | QVector2D controlPoint() const |
63 | { |
64 | return cp; |
65 | } |
66 | |
67 | QVector2D endPoint() const |
68 | { |
69 | return ep; |
70 | } |
71 | |
72 | QVector2D midPoint() const |
73 | { |
74 | return isLine() ? 0.5f * (sp + ep) : (0.25f * sp) + (0.5f * cp) + (0.25 * ep); |
75 | } |
76 | |
77 | int childCount() const { return m_numChildren; } |
78 | |
79 | int indexOfChild(int childNumber) const |
80 | { |
81 | Q_ASSERT(childNumber >= 0 && childNumber < childCount()); |
82 | return -(m_firstChildIndex + 1 + childNumber); |
83 | } |
84 | |
85 | QVector2D pointAtFraction(float t) const; |
86 | |
87 | QVector2D tangentAtFraction(float t) const |
88 | { |
89 | return isLine() ? (ep - sp) : ((1 - t) * 2 * (cp - sp)) + (t * 2 * (ep - cp)); |
90 | } |
91 | |
92 | QVector2D normalAtFraction(float t) const |
93 | { |
94 | const QVector2D tan = tangentAtFraction(t); |
95 | return QVector2D(-tan.y(), tan.x()); |
96 | } |
97 | |
98 | float extent() const; |
99 | |
100 | void setAsConvex(bool isConvex) |
101 | { |
102 | if (isConvex) |
103 | m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags | Element::Convex); |
104 | else |
105 | m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags & ~Element::Convex); |
106 | } |
107 | |
108 | bool isControlPointOnLeft() const |
109 | { |
110 | return isPointOnLeft(p: cp, sp, ep); |
111 | } |
112 | |
113 | private: |
114 | int intersectionsAtY(float y, float *fractions) const; |
115 | |
116 | enum CurvatureFlags : quint8 { |
117 | CurvatureUndetermined = 0, |
118 | FillOnRight = 1, |
119 | Convex = 2 |
120 | }; |
121 | |
122 | QVector2D sp; |
123 | QVector2D cp; |
124 | QVector2D ep; |
125 | int m_firstChildIndex = 0; |
126 | quint8 m_numChildren = 0; |
127 | CurvatureFlags m_curvatureFlags = CurvatureUndetermined; |
128 | quint8 m_isSubpathStart : 1; |
129 | quint8 m_isSubpathEnd : 1; |
130 | quint8 m_isLine : 1; |
131 | friend class QQuadPath; |
132 | friend QDebug operator<<(QDebug, const QQuadPath::Element &); |
133 | }; |
134 | |
135 | void moveTo(const QVector2D &to) |
136 | { |
137 | subPathToStart = true; |
138 | currentPoint = to; |
139 | } |
140 | |
141 | void lineTo(const QVector2D &to) |
142 | { |
143 | addElement(control: {}, to, isLine: true); |
144 | } |
145 | |
146 | void quadTo(const QVector2D &control, const QVector2D &to) |
147 | { |
148 | addElement(control, to); |
149 | } |
150 | |
151 | Element &elementAt(int i) |
152 | { |
153 | return i < 0 ? m_childElements[-(i + 1)] : m_elements[i]; |
154 | } |
155 | |
156 | const Element &elementAt(int i) const |
157 | { |
158 | return i < 0 ? m_childElements[-(i + 1)] : m_elements[i]; |
159 | } |
160 | |
161 | int indexOfChildAt(int i, int childNumber) const |
162 | { |
163 | return elementAt(i).indexOfChild(childNumber); |
164 | } |
165 | |
166 | QRectF controlPointRect() const; |
167 | |
168 | Qt::FillRule fillRule() const { return m_fillRule; } |
169 | void setFillRule(Qt::FillRule rule) { m_fillRule = rule; } |
170 | |
171 | void reserve(int size) { m_elements.reserve(asize: size); } |
172 | int elementCount() const { return m_elements.size(); } |
173 | bool isEmpty() const { return m_elements.size() == 0; } |
174 | int elementCountRecursive() const; |
175 | |
176 | static QQuadPath fromPainterPath(const QPainterPath &path); |
177 | QPainterPath toPainterPath() const; |
178 | |
179 | QQuadPath subPathsClosed() const; |
180 | void addCurvatureData(); |
181 | QQuadPath flattened() const; |
182 | QQuadPath dashed(qreal lineWidth, const QList<qreal> &dashPattern, qreal dashOffset = 0) const; |
183 | void splitElementAt(int index); |
184 | bool contains(const QVector2D &point) const; |
185 | |
186 | template<typename Func> |
187 | void iterateChildrenOf(Element &e, Func &&lambda) |
188 | { |
189 | const int lastChildIndex = e.m_firstChildIndex + e.childCount() - 1; |
190 | for (int i = e.m_firstChildIndex; i <= lastChildIndex; i++) { |
191 | Element &c = m_childElements[i]; |
192 | if (c.childCount() > 0) |
193 | iterateChildrenOf(c, lambda); |
194 | else |
195 | lambda(c); |
196 | } |
197 | } |
198 | |
199 | template<typename Func> |
200 | void iterateChildrenOf(const Element &e, Func &&lambda) const |
201 | { |
202 | const int lastChildIndex = e.m_firstChildIndex + e.childCount() - 1; |
203 | for (int i = e.m_firstChildIndex; i <= lastChildIndex; i++) { |
204 | const Element &c = m_childElements[i]; |
205 | if (c.childCount() > 0) |
206 | iterateChildrenOf(c, lambda); |
207 | else |
208 | lambda(c); |
209 | } |
210 | } |
211 | |
212 | template<typename Func> |
213 | void iterateElements(Func &&lambda) |
214 | { |
215 | for (auto &e : m_elements) { |
216 | if (e.childCount() > 0) |
217 | iterateChildrenOf(e, lambda); |
218 | else |
219 | lambda(e); |
220 | } |
221 | } |
222 | |
223 | template<typename Func> |
224 | void iterateElements(Func &&lambda) const |
225 | { |
226 | for (auto &e : m_elements) { |
227 | if (e.childCount() > 0) |
228 | iterateChildrenOf(e, lambda); |
229 | else |
230 | lambda(e); |
231 | } |
232 | } |
233 | |
234 | static QVector2D closestPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep); |
235 | static bool isPointOnLeft(const QVector2D &p, const QVector2D &sp, const QVector2D &ep); |
236 | static bool isPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep); |
237 | static bool isPointNearLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep); |
238 | |
239 | private: |
240 | void addElement(const QVector2D &control, const QVector2D &to, bool isLine = false); |
241 | Element::CurvatureFlags coordinateOrderOfElement(const Element &element) const; |
242 | |
243 | friend QDebug operator<<(QDebug, const QQuadPath &); |
244 | |
245 | bool subPathToStart = true; |
246 | Qt::FillRule m_fillRule = Qt::OddEvenFill; |
247 | QVector2D currentPoint; |
248 | QList<Element> m_elements; |
249 | QList<Element> m_childElements; |
250 | }; |
251 | |
252 | QDebug operator<<(QDebug, const QQuadPath::Element &); |
253 | QDebug operator<<(QDebug, const QQuadPath &); |
254 | |
255 | QT_END_NAMESPACE |
256 | |
257 | #endif |
258 | |