HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
bezier.hpp
1// Copyright Take Vos 2019-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../numeric/module.hpp"
8#include "../SIMD/module.hpp"
9#include "../geometry/module.hpp"
10#include "../macros.hpp"
11#include <array>
12#include <optional>
13
14hi_export_module(hikogui.graphic_path.bezier);
15
16hi_export namespace hi
17{
18 inline namespace v1 {
19
20 // B(t)=(P_{2}-P_{1})t+P_{1}
21 template<typename T>
22 constexpr std::array<T, 2> bezierToPolynomial(T P1, T P2) noexcept
23 {
24 return {P2 - P1, P1};
25 }
26
27 // B(t)=(P_{1}-2C+P_{2})t^{2}+2(C-P_{1})t+P_{1}
28 template<typename T>
29 constexpr std::array<T, 3> bezierToPolynomial(T P1, T C, T P2) noexcept
30 {
31 return {P1 - C * 2 + P2, (C - P1) * 2, P1};
32 }
33
34 // B(t)=(-P_{1}+3C_{1}-3C_{2}+P_{2})t^{3}+(3P_{1}-6_{1}+3C_{2})t^{2}+(-3P_{1}+3C_{1})t+P_{1}
35 template<typename T>
36 constexpr std::array<T, 4> bezierToPolynomial(T P1, T C1, T C2, T P2) noexcept
37 {
38 return {-P1 + C1 * 3 - C2 * 3 + P2, P1 * 3 - C1 * 6 + C2 * 3, P1 * -3 + C1 * 3, P1};
39 }
40
41 constexpr point2 bezierPointAt(point2 P1, point2 P2, float t) noexcept
42 {
43 hilet[a, b] = bezierToPolynomial(static_cast<f32x4>(P1), static_cast<f32x4>(P2));
44 return point2{a * t + b};
45 }
46
47 constexpr point2 bezierPointAt(point2 P1, point2 C, point2 P2, float t) noexcept
48 {
49 hilet[a, b, c] = bezierToPolynomial(static_cast<f32x4>(P1), static_cast<f32x4>(C), static_cast<f32x4>(P2));
50 return point2{a * t * t + b * t + c};
51 }
52
53 constexpr point2 bezierPointAt(point2 P1, point2 C1, point2 C2, point2 P2, float t) noexcept
54 {
55 hilet[a, b, c, d] =
56 bezierToPolynomial(static_cast<f32x4>(P1), static_cast<f32x4>(C1), static_cast<f32x4>(C2), static_cast<f32x4>(P2));
57 return point2{a * t * t * t + b * t * t + c * t + d};
58 }
59
60 constexpr vector2 bezierTangentAt(point2 P1, point2 P2, float t) noexcept
61 {
62 return P2 - P1;
63 }
64
65 constexpr vector2 bezierTangentAt(point2 P1, point2 C, point2 P2, float t) noexcept
66 {
67 hilet P1_ = static_cast<f32x4>(P1);
68 hilet C_ = static_cast<f32x4>(C);
69 hilet P2_ = static_cast<f32x4>(P2);
70
71 return vector2{2 * t * (P2_ - 2 * C_ + P1_) + 2 * (C_ - P1_)};
72 }
73
74 constexpr vector2 bezierTangentAt(point2 P1, point2 C1, point2 C2, point2 P2, float t) noexcept
75 {
76 hilet P1_ = static_cast<f32x4>(P1);
77 hilet C1_ = static_cast<f32x4>(C1);
78 hilet C2_ = static_cast<f32x4>(C2);
79 hilet P2_ = static_cast<f32x4>(P2);
80
81 return vector2{3 * t * t * (P2_ - 3 * C2_ + 3 * C1_ - P1_) + 6 * t * (C2_ - 2 * C1_ + P1_) + 3 * (C1_ - P1_)};
82 }
83
84 constexpr results<float, 1> bezierFindT(float P1, float P2, float x) noexcept
85 {
86 hilet[a, b] = bezierToPolynomial(P1, P2);
87 return solvePolynomial(a, b - x);
88 }
89
90 constexpr results<float, 2> bezierFindT(float P1, float C, float P2, float x) noexcept
91 {
92 hilet[a, b, c] = bezierToPolynomial(P1, C, P2);
93 return solvePolynomial(a, b, c - x);
94 }
95
96 hi_force_inline constexpr results<float, 3> bezierFindT(float P1, float C1, float C2, float P2, float x) noexcept
97 {
98 hilet[a, b, c, d] = bezierToPolynomial(P1, C1, C2, P2);
99 return solvePolynomial(a, b, c, d - x);
100 }
101
106 hi_force_inline constexpr results<float, 1> bezierFindTForNormalsIntersectingPoint(point2 P1, point2 P2, point2 P) noexcept
107 {
108 hilet t_above = dot(P - P1, P2 - P1);
109 hilet t_below = dot(P2 - P1, P2 - P1);
110 if (t_below == 0.0) {
111 [[unlikely]] return {};
112 } else {
113 return {t_above / t_below};
114 }
115 }
116
121 hi_force_inline constexpr results<float, 3>
122 bezierFindTForNormalsIntersectingPoint(point2 P1, point2 C, point2 P2, point2 P) noexcept
123 {
124 hilet P1_ = static_cast<f32x4>(P1);
125 hilet P2_ = static_cast<f32x4>(P2);
126 hilet C_ = static_cast<f32x4>(C);
127
128 hilet p = P - P1;
129 hilet p1 = C - P1;
130 hilet p2 = vector2{P2_ - (2 * C_) + P1_};
131
132 hilet a = dot(p2, p2);
133 hilet b = 3 * dot(p1, p2);
134 hilet c = dot(2 * p1, p1) - dot(p2, p);
135 hilet d = -dot(p1, p);
136 return solvePolynomial(a, b, c, d);
137 }
138
146 constexpr results<float, 1> bezierFindX(point2 P1, point2 P2, float y) noexcept
147 {
148 if (y < std::min({P1.y(), P2.y()}) || y > std::max({P1.y(), P2.y()})) {
149 return {};
150 }
151
153 for (hilet t : bezierFindT(P1.y(), P2.y(), y)) {
154 if (t >= 0.0f && t < 1.0f) {
155 r.add(bezierPointAt(P1, P2, t).x());
156 }
157 }
158
159 return r;
160 }
161
169 constexpr results<float, 2> bezierFindX(point2 P1, point2 C, point2 P2, float y) noexcept
170 {
172
173 if (y < std::min({P1.y(), C.y(), P2.y()}) || y > std::max({P1.y(), C.y(), P2.y()})) {
174 return r;
175 }
176
177 for (hilet t : bezierFindT(P1.y(), C.y(), P2.y(), y)) {
178 if (t >= 0.0f && t <= 1.0f) {
179 r.add(bezierPointAt(P1, C, P2, t).x());
180 }
181 }
182
183 return r;
184 }
185
193 constexpr results<float, 3> bezierFindX(point2 P1, point2 C1, point2 C2, point2 P2, float y) noexcept
194 {
196
197 if (y < std::min({P1.y(), C1.y(), C2.y(), P2.y()}) || y > std::max({P1.y(), C1.y(), C2.y(), P2.y()})) {
198 return r;
199 }
200
201 for (hilet t : bezierFindT(P1.y(), C1.y(), C2.y(), P2.y(), y)) {
202 if (t >= 0.0f && t <= 1.0f) {
203 r.add(bezierPointAt(P1, C1, C2, P2, t).x());
204 }
205 }
206
207 return r;
208 }
209
213 inline float bezierFlatness(point2 P1, point2 P2) noexcept
214 {
215 return 1.0f;
216 }
217
222 inline float bezierFlatness(point2 P1, point2 C, point2 P2) noexcept
223 {
224 hilet P1P2 = hypot(P2 - P1);
225 if (P1P2 == 0.0f) {
226 return 1.0;
227 }
228
229 hilet P1C1 = hypot(C - P1);
230 hilet C1P2 = hypot(P2 - C);
231 return P1P2 / (P1C1 + C1P2);
232 }
233
238 inline float bezierFlatness(point2 P1, point2 C1, point2 C2, point2 P2) noexcept
239 {
240 hilet P1P2 = hypot(P2 - P1);
241 if (P1P2 == 0.0f) {
242 return 1.0;
243 }
244
245 hilet P1C1 = hypot(C1 - P1);
246 hilet C1C2 = hypot(C2 - C1);
247 hilet C2P2 = hypot(P2 - C2);
248 return P1P2 / (P1C1 + C1C2 + C2P2);
249 }
250
251 inline std::pair<point2, point2> parallelLine(point2 P1, point2 P2, float distance) noexcept
252 {
253 hilet v = P2 - P1;
254 hilet n = normal(v);
255 return {P1 + n * distance, P2 + n * distance};
256 }
257
260 inline std::optional<point2> getIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
261 {
262 // convert points to vectors.
263 hilet p = A1;
264 hilet r = A2 - A1;
265 hilet q = B1;
266 hilet s = B2 - B1;
267
268 // find t and u in:
269 // p + t*r == q + us
270 hilet crossRS = cross(r, s);
271 if (crossRS == 0.0f) {
272 // Parallel, other non, or a range of points intersect.
273 return {};
274
275 } else {
276 hilet q_min_p = q - p;
277 hilet t = cross(q_min_p, s) / crossRS;
278 hilet u = cross(q_min_p, r) / crossRS;
279
280 if (t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f) {
281 return bezierPointAt(A1, A2, t);
282 } else {
283 // The lines intersect outside of one or both of the segments.
284 return {};
285 }
286 }
287 }
288
291 inline std::optional<point2> getExtrapolatedIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
292 {
293 // convert points to vectors.
294 hilet p = A1;
295 hilet r = A2 - A1;
296 hilet q = B1;
297 hilet s = B2 - B1;
298
299 // find t and u in:
300 // p + t*r == q + us
301 hilet crossRS = cross(r, s);
302 if (crossRS == 0.0f) {
303 // Parallel, other non, or a range of points intersect.
304 return {};
305
306 } else {
307 hilet q_min_p = q - p;
308 hilet t = cross(q_min_p, s) / crossRS;
309
310 return bezierPointAt(A1, A2, t);
311 }
312 }
313
314 } // namespace v1
315} // namespace hi::inline v1
DOXYGEN BUG.
Definition algorithm.hpp:16
hi_force_inline constexpr results< T, 1 > solvePolynomial(T const &a, T const &b) noexcept
Definition polynomial.hpp:144
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
constexpr results< float, 1 > bezierFindX(point2 P1, point2 P2, float y) noexcept
Definition bezier.hpp:146
hi_force_inline constexpr results< float, 1 > bezierFindTForNormalsIntersectingPoint(point2 P1, point2 P2, point2 P) noexcept
Find t on the line P1->P2 which is closest to P.
Definition bezier.hpp:106
std::optional< point2 > getIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
Definition bezier.hpp:260
std::optional< point2 > getExtrapolatedIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
Definition bezier.hpp:291
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
float bezierFlatness(point2 P1, point2 P2) noexcept
Definition bezier.hpp:213
A high-level geometric vector Part of the high-level vector, point, mat and color types.
Definition vector2.hpp:19
T max(T... args)
T min(T... args)