Krita Source Code Documentation
Loading...
Searching...
No Matches
KisBezierUtils.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef KISBEZIERUTILS_H
8#define KISBEZIERUTILS_H
9
10#include "kritaglobal_export.h"
11
12#include <kis_algebra_2d.h>
13
14
15namespace KisBezierUtils
16{
18
19
20
21
22inline QPointF bezierCurveDeriv(const QPointF p0,
23 const QPointF p1,
24 const QPointF p2,
25 const QPointF p3,
26 qreal t)
27{
28 const qreal t_2 = pow2(t);
29 const qreal t_inv = 1.0 - t;
30 const qreal t_inv_2 = pow2(t_inv);
31
32 return
33 3 * t_inv_2 * (p1 - p0) +
34 6 * t_inv * t * (p2 - p1) +
35 3 * t_2 * (p3 - p2);
36}
37
38inline QPointF bezierCurveDeriv2(const QPointF p0,
39 const QPointF p1,
40 const QPointF p2,
41 const QPointF p3,
42 qreal t)
43{
44 const qreal t_inv = 1.0 - t;
45
46 return
47 6 * t_inv * (p2 - 2 * p1 + p0) +
48 6 * t * (p3 - 2 * p2 + p1);
49}
50
51
52inline void deCasteljau(const QPointF &q0,
53 const QPointF &q1,
54 const QPointF &q2,
55 const QPointF &q3,
56 qreal t,
57 QPointF *p0,
58 QPointF *p1,
59 QPointF *p2,
60 QPointF *p3,
61 QPointF *p4)
62{
63 QPointF q[4];
64
65 q[0] = q0;
66 q[1] = q1;
67 q[2] = q2;
68 q[3] = q3;
69
70 // points of the new segment after the split point
71 QPointF p[3];
72
73 // the De Casteljau algorithm
74 for (unsigned short j = 1; j <= 3; ++j) {
75 for (unsigned short i = 0; i <= 3 - j; ++i) {
76 q[i] = (1.0 - t) * q[i] + t * q[i + 1];
77 }
78 p[j - 1] = q[0];
79 }
80
81 *p0 = p[0];
82 *p1 = p[1];
83 *p2 = p[2];
84 *p3 = q[1];
85 *p4 = q[2];
86}
87
88inline QPointF bezierCurve(const QPointF p0,
89 const QPointF p1,
90 const QPointF p2,
91 const QPointF p3,
92 qreal t)
93{
94#if 0
95 const qreal t_2 = pow2(t);
96 const qreal t_3 = t_2 * t;
97 const qreal t_inv = 1.0 - t;
98 const qreal t_inv_2 = pow2(t_inv);
99 const qreal t_inv_3 = t_inv_2 * t_inv;
100
101 return
102 t_inv_3 * p0 +
103 3 * t_inv_2 * t * p1 +
104 3 * t_inv * t_2 * p2 +
105 t_3 * p3;
106#else
107 QPointF q0, q1, q2, q3, q4;
108 deCasteljau(p0, p1, p2, p3, t, &q0, &q1, &q2, &q3, &q4);
109 return q2;
110#endif
111}
112
113inline QPointF bezierCurve(const QList<QPointF> &points,
114 qreal t)
115{
116 QPointF result;
117
118 if (points.size() == 2) {
119 result = lerp(points.first(), points.last(), t);
120 } else if (points.size() == 3) {
121 result = bezierCurve(points[0], points[1], points[1], points[2], t);
122 } else if (points.size() == 4) {
123 result = bezierCurve(points[0], points[1], points[2], points[3], t);
124 } else {
125 KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "Unsupported number of bezier control points");
126 }
127
128 return result;
129}
130
131inline bool isLinearSegmentByDerivatives(const QPointF &p0, const QPointF &d0,
132 const QPointF &p1, const QPointF &d1,
133 const qreal eps = 1e-4)
134{
135 const QPointF diff = p1 - p0;
136 const qreal dist = KisAlgebra2D::norm(diff);
137
138 const qreal normCoeff = 1.0 / 3.0 / dist;
139
140 const qreal offset1 =
141 normCoeff * qAbs(KisAlgebra2D::crossProduct(diff, d0));
142 if (offset1 > eps) return false;
143
144 const qreal offset2 =
145 normCoeff * qAbs(KisAlgebra2D::crossProduct(diff, d1));
146 if (offset2 > eps) return false;
147
148 return true;
149}
150
151inline bool isLinearSegmentByControlPoints(const QPointF &p0, const QPointF &p1,
152 const QPointF &p2, const QPointF &p3,
153 const qreal eps = 1e-4)
154{
155 return isLinearSegmentByDerivatives(p0, (p1 - p0) * 3.0, p3, (p3 - p2) * 3.0, eps);
156}
157
158inline int bezierDegree(const QPointF p0,
159 const QPointF p1,
160 const QPointF p2,
161 const QPointF p3)
162{
163 const qreal eps = 1e-4;
164
165 int degree = 3;
166
168 degree = 1;
170 degree = 2;
171 }
172
173 return degree;
174}
175
176KRITAGLOBAL_EXPORT
177QVector<qreal> linearizeCurve(const QPointF p0,
178 const QPointF p1,
179 const QPointF p2,
180 const QPointF p3,
181 const qreal eps);
182KRITAGLOBAL_EXPORT
184
185KRITAGLOBAL_EXPORT
186qreal nearestPoint(const QList<QPointF> controlPoints, const QPointF &point, qreal *resultDistance = 0, QPointF *resultPoint = 0);
187
188KRITAGLOBAL_EXPORT
189int controlPolygonZeros(const QList<QPointF> &controlPoints);
190
200KRITAGLOBAL_EXPORT
201QPointF calculateLocalPos(const std::array<QPointF, 12> &points,
202 const QPointF &globalPoint);
203
213KRITAGLOBAL_EXPORT
214QPointF calculateGlobalPos(const std::array<QPointF, 12> &points, const QPointF &localPoint);
215
225KRITAGLOBAL_EXPORT
226QPointF calculateLocalPosSVG2(const std::array<QPointF, 12> &points,
227 const QPointF &globalPoint);
228
238KRITAGLOBAL_EXPORT
239QPointF calculateGlobalPosSVG2(const std::array<QPointF, 12> &points, const QPointF &localPoint);
240
241
249KRITAGLOBAL_EXPORT
250QPointF interpolateQuadric(const QPointF &p0, const QPointF &p2, const QPointF &pt, qreal t);
251
256KRITAGLOBAL_EXPORT
257std::pair<QPointF, QPointF> offsetSegment(qreal t, const QPointF &offset);
258
259KRITAGLOBAL_EXPORT
260qreal curveLength(const QPointF p0,
261 const QPointF p1,
262 const QPointF p2,
263 const QPointF p3,
264 const qreal error);
265
266KRITAGLOBAL_EXPORT
267qreal curveLengthAtPoint(const QPointF p0,
268 const QPointF p1,
269 const QPointF p2,
270 const QPointF p3,
271 qreal t,
272 const qreal error);
273
274KRITAGLOBAL_EXPORT
275qreal curveParamByProportion(const QPointF p0,
276 const QPointF p1,
277 const QPointF p2,
278 const QPointF p3,
279 qreal proportion,
280 const qreal error);
281
282KRITAGLOBAL_EXPORT
283qreal curveProportionByParam(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t, const qreal error);
284
296KRITAGLOBAL_EXPORT
297std::pair<QPointF, QPointF> removeBezierNode(const QPointF &p0,
298 const QPointF &p1,
299 const QPointF &p2,
300 const QPointF &p3,
301 const QPointF &q1,
302 const QPointF &q2,
303 const QPointF &q3);
304
314KRITAGLOBAL_EXPORT
315QVector<qreal> intersectWithLine(const QPointF &p0,
316 const QPointF &p1,
317 const QPointF &p2,
318 const QPointF &p3,
319 const QLineF &line,
320 qreal eps);
321
326KRITAGLOBAL_EXPORT
327boost::optional<qreal> intersectWithLineNearest(const QPointF &p0,
328 const QPointF &p1,
329 const QPointF &p2,
330 const QPointF &p3,
331 const QLineF &line,
332 const QPointF &nearestAnchor,
333 qreal eps);
334
335} // namespace KisBezierUtils
336
337#endif // KISBEZIERUTILS_H
QPointF q1
const Params2D p
QPointF q0
QPointF p0
QPointF q3
QPointF p2
QPointF q2
QPointF p3
QPointF p1
QPointF lerp(const QPointF &p1, const QPointF &p2, qreal t)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
const qreal eps
T pow2(const T &x)
Definition kis_global.h:166
Point lerp(const Point &pt1, const Point &pt2, qreal t)
qreal norm(const T &a)
PointTypeTraits< T >::value_type crossProduct(const T &a, const T &b)
bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2)
qreal curveParamByProportion(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal proportion, const qreal error)
QPointF calculateLocalPos(const std::array< QPointF, 12 > &points, const QPointF &globalPoint)
calculates local (u,v) coordinates of the patch corresponding to globalPoint
void deCasteljau(const QPointF &q0, const QPointF &q1, const QPointF &q2, const QPointF &q3, qreal t, QPointF *p0, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4)
QPointF calculateLocalPosSVG2(const std::array< QPointF, 12 > &points, const QPointF &globalPoint)
calculates local (u,v) coordinates of the patch corresponding to globalPoint
QVector< qreal > intersectWithLine(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, qreal eps)
qreal curveProportionByParam(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t, const qreal error)
QVector< qreal > linearizeCurve(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, const qreal eps)
qreal curveLengthAtPoint(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t, const qreal error)
QPointF calculateGlobalPos(const std::array< QPointF, 12 > &points, const QPointF &localPoint)
calculates global coordinate corresponding to the patch coordinate (u, v)
bool isLinearSegmentByDerivatives(const QPointF &p0, const QPointF &d0, const QPointF &p1, const QPointF &d1, const qreal eps=1e-4)
int controlPolygonZeros(const QList< QPointF > &controlPoints)
QPointF calculateGlobalPosSVG2(const std::array< QPointF, 12 > &points, const QPointF &localPoint)
calculates global coordinate corresponding to the patch coordinate (u, v)
QPointF bezierCurveDeriv(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t)
bool isLinearSegmentByControlPoints(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const qreal eps=1e-4)
std::pair< QPointF, QPointF > offsetSegment(qreal t, const QPointF &offset)
moves point t of the curve by offset offset
QPointF bezierCurve(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t)
QPointF bezierCurveDeriv2(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t)
qreal nearestPoint(const QList< QPointF > controlPoints, const QPointF &point, qreal *resultDistance, QPointF *resultPoint)
std::pair< QPointF, QPointF > removeBezierNode(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &q1, const QPointF &q2, const QPointF &q3)
Adjusts position for the bezier control points after removing a node.
qreal curveLength(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, const qreal error)
QVector< qreal > mergeLinearizationSteps(const QVector< qreal > &a, const QVector< qreal > &b)
boost::optional< qreal > intersectWithLineNearest(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, const QPointF &nearestAnchor, qreal eps)
QPointF interpolateQuadric(const QPointF &p0, const QPointF &p2, const QPointF &pt, qreal t)
Interpolates quadric curve passing through given points.
int bezierDegree(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3)