Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qt_quadratic_bezier.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 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// ---------------- Cubic -> Quadratic path stuff - temporarily here -----------
5
6#include <private/qbezier_p.h>
7#include <QtMath>
8
10
12{
13 static bool init = false;
14 const int numSteps = 21;
15 Q_STATIC_ASSERT(numSteps % 2 == 1); // numTries must be odd
16 static qreal t2s[numSteps];
17 static qreal tmts[numSteps];
18 if (!init) {
19 // Precompute bezier factors
20 qreal t = 0.20;
21 const qreal step = (1 - (2 * t)) / (numSteps - 1);
22 for (int i = 0; i < numSteps; i++) {
23 t2s[i] = t * t;
24 tmts[i] = 2 * t * (1 - t);
25 t += step;
26 }
27 init = true;
28 }
29
30 const QPointF midPoint = b.midPoint();
31 auto distForIndex = [&](int i) -> qreal {
32 QPointF qp = (t2s[numSteps - 1 - i] * b.pt1()) + (tmts[i] * qcp) + (t2s[i] * b.pt4());
33 QPointF d = midPoint - qp;
34 return QPointF::dotProduct(d, d);
35 };
36
37 const int halfSteps = (numSteps - 1) / 2;
38 bool foundIt = false;
39 const qreal centerDist = distForIndex(halfSteps);
40 qreal minDist = centerDist;
41 // Search for the minimum in right half
42 for (int i = 0; i < halfSteps; i++) {
43 qreal tDist = distForIndex(halfSteps + 1 + i);
44 if (tDist < minDist) {
45 minDist = tDist;
46 } else {
47 foundIt = (i > 0);
48 break;
49 }
50 }
51 if (!foundIt) {
52 // Search in left half
53 minDist = centerDist;
54 for (int i = 0; i < halfSteps; i++) {
55 qreal tDist = distForIndex(halfSteps - 1 - i);
56 if (tDist < minDist) {
57 minDist = tDist;
58 } else {
59 foundIt = (i > 0);
60 break;
61 }
62 }
63 }
64 return foundIt ? minDist : centerDist;
65}
66
68{
69 const QLineF st = b.startTangent();
70 const QLineF et = b.endTangent();
71 const QPointF midPoint = b.midPoint();
72 bool valid = true;
73 QPointF quadControlPoint;
74 if (st.intersects(et, &quadControlPoint) == QLineF::NoIntersection) {
75 valid = false;
76 } else {
77 // Check if intersection is on wrong side
78 const QPointF bl = b.pt4() - b.pt1();
79 const QPointF ml = midPoint - b.pt1();
80 const QPointF ql = quadControlPoint - b.pt1();
81 qreal cx1 = (ml.x() * bl.y()) - (ml.y() * bl.x());
82 qreal cx2 = (ql.x() * bl.y()) - (ql.y() * bl.x());
83 valid = (std::signbit(cx1) == std::signbit(cx2));
84 }
85 return valid ? quadControlPoint : midPoint;
86}
87
88static int qt_getInflectionPoints(const QBezier &orig, qreal *tpoints)
89{
90 auto isValidRoot = [](qreal r) {
91 return qIsFinite(r) && (r > 0) && (!qFuzzyIsNull(float(r))) && (r < 1)
92 && (!qFuzzyIsNull(float(r - 1)));
93 };
94
95 // normalize so pt1.x,pt1.y,pt4.y == 0
96 QTransform xf;
97 const QLineF l(orig.pt1(), orig.pt4());
98 xf.rotate(l.angle());
99 xf.translate(-orig.pt1().x(), -orig.pt1().y());
100 const QBezier n = orig.mapBy(xf);
101 Q_ASSERT(n.pt1() == QPoint() && qFuzzyIsNull(float(n.pt4().y())));
102
103 const qreal x2 = n.pt2().x();
104 const qreal x3 = n.pt3().x();
105 const qreal x4 = n.pt4().x();
106 const qreal y2 = n.pt2().y();
107 const qreal y3 = n.pt3().y();
108
109 const qreal p = x3 * y2;
110 const qreal q = x4 * y2;
111 const qreal r = x2 * y3;
112 const qreal s = x4 * y3;
113
114 const qreal a = 18 * ((-3 * p) + (2 * q) + (3 * r) - s);
115 if (qFuzzyIsNull(float(a))) {
116 if (std::signbit(y2) != std::signbit(y3) && qFuzzyCompare(float(x4 - x3), float(x2))) {
117 tpoints[0] = 0.5; // approx
118 return 1;
119 } else if (!a) {
120 return 0;
121 }
122 }
123 const qreal b = 18 * (((3 * p) - q) - (3 * r));
124 const qreal c = 18 * (r - p);
125 const qreal rad = (b * b) - (4 * a * c);
126 if (rad < 0)
127 return 0;
128 const qreal sqr = qSqrt(rad);
129 const qreal root1 = (-b + sqr) / (2 * a);
130 const qreal root2 = (-b - sqr) / (2 * a);
131
132 int res = 0;
133 if (isValidRoot(root1))
134 tpoints[res++] = root1;
135 if (root2 != root1 && isValidRoot(root2))
136 tpoints[res++] = root2;
137
138 if (res == 2 && tpoints[0] > tpoints[1])
139 qSwap(tpoints[0], tpoints[1]);
140
141 return res;
142}
143
144static void qt_addToQuadratics(const QBezier &b, QPolygonF *p, int maxSplits, qreal maxDiff)
145{
147 if (maxSplits <= 0 || qt_scoreQuadratic(b, qcp) < maxDiff) {
148 p->append(qcp);
149 p->append(b.pt4());
150 } else {
151 QBezier rhs = b;
152 QBezier lhs;
153 rhs.parameterSplitLeft(0.5, &lhs);
154 qt_addToQuadratics(lhs, p, maxSplits - 1, maxDiff);
155 qt_addToQuadratics(rhs, p, maxSplits - 1, maxDiff);
156 }
157}
158
159void qt_toQuadratics(const QBezier &b, QPolygonF *out, qreal errorLimit)
160{
161 out->resize(0);
162 out->append(b.pt1());
163
164 {
165 // Shortcut if the cubic is really a quadratic
166 const qreal f = 3.0 / 2.0;
167 const QPointF c1 = b.pt1() + f * (b.pt2() - b.pt1());
168 const QPointF c2 = b.pt4() + f * (b.pt3() - b.pt4());
169 if (c1 == c2) {
170 out->append(c1);
171 out->append(b.pt4());
172 return;
173 }
174 }
175
176 const QRectF cpr = b.bounds();
177 const QPointF dim = cpr.bottomRight() - cpr.topLeft();
178 qreal maxDiff = QPointF::dotProduct(dim, dim) * errorLimit * errorLimit; // maxdistance^2
179
180 qreal infPoints[2];
181 int numInfPoints = qt_getInflectionPoints(b, infPoints);
182 const int maxSubSplits = numInfPoints > 0 ? 2 : 3;
183 qreal t0 = 0;
184 // number of main segments == #inflectionpoints + 1
185 for (int i = 0; i < numInfPoints + 1; i++) {
186 qreal t1 = (i < numInfPoints) ? infPoints[i] : 1;
188 qt_addToQuadratics(segment, out, maxSubSplits, maxDiff);
189 t0 = t1;
190 }
191}
192
QPointF pt1() const
Definition qbezier_p.h:63
void parameterSplitLeft(qreal t, QBezier *left)
Definition qbezier_p.h:210
QBezier bezierOnInterval(qreal t0, qreal t1) const
Definition qbezier.cpp:680
QBezier mapBy(const QTransform &transform) const
Definition qbezier.cpp:40
QPointF pt4() const
Definition qbezier_p.h:66
\inmodule QtCore
Definition qline.h:182
qreal angle() const
Definition qline.cpp:558
@ NoIntersection
Definition qline.h:185
IntersectionType intersects(const QLineF &l, QPointF *intersectionPoint=nullptr) const
Definition qline.cpp:647
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
static constexpr qreal dotProduct(const QPointF &p1, const QPointF &p2)
Definition qpoint.h:239
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
\inmodule QtCore\reentrant
Definition qpoint.h:23
The QPolygonF class provides a list of points using floating point precision.
Definition qpolygon.h:96
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:510
constexpr QPointF bottomRight() const noexcept
Returns the position of the rectangle's bottom-right corner.
Definition qrect.h:511
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QTransform & rotate(qreal a, Qt::Axis axis=Qt::ZAxis, qreal distanceToPlane=1024.0f)
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
qSwap(pi, e)
Combined button and popup list for selecting options.
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:105
bool qIsFinite(qfloat16 f) noexcept
Definition qfloat16.h:239
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:243
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLboolean r
[2]
GLfloat GLfloat f
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
GLfloat n
GLuint res
const GLubyte * c
GLfixed GLfixed GLfixed y2
GLuint segment
GLfixed GLfixed x2
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define t1
static void qt_addToQuadratics(const QBezier &b, QPolygonF *p, int maxSplits, qreal maxDiff)
static QPointF qt_quadraticForCubic(const QBezier &b)
void qt_toQuadratics(const QBezier &b, QPolygonF *out, qreal errorLimit)
static QT_BEGIN_NAMESPACE qreal qt_scoreQuadratic(const QBezier &b, QPointF qcp)
static int qt_getInflectionPoints(const QBezier &orig, qreal *tpoints)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
double qreal
Definition qtypes.h:92
QTextStream out(stdout)
[7]
MyCustomStruct c2