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 <array>
11#include <optional>
12
13namespace hi::inline v1 {
14
15// B(t)=(P_{2}-P_{1})t+P_{1}
16template<typename T>
17constexpr std::array<T, 2> bezierToPolynomial(T P1, T P2) noexcept
18{
19 return {P2 - P1, P1};
20}
21
22// B(t)=(P_{1}-2C+P_{2})t^{2}+2(C-P_{1})t+P_{1}
23template<typename T>
24constexpr std::array<T, 3> bezierToPolynomial(T P1, T C, T P2) noexcept
25{
26 return {P1 - C * 2 + P2, (C - P1) * 2, P1};
27}
28
29// 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}
30template<typename T>
31constexpr std::array<T, 4> bezierToPolynomial(T P1, T C1, T C2, T P2) noexcept
32{
33 return {-P1 + C1 * 3 - C2 * 3 + P2, P1 * 3 - C1 * 6 + C2 * 3, P1 * -3 + C1 * 3, P1};
34}
35
36constexpr point2 bezierPointAt(point2 P1, point2 P2, float t) noexcept
37{
38 hilet[a, b] = bezierToPolynomial(static_cast<f32x4>(P1), static_cast<f32x4>(P2));
39 return point2{a * t + b};
40}
41
42constexpr point2
43bezierPointAt(point2 P1, point2 C, point2 P2, float t) noexcept
44{
45 hilet[a, b, c] = bezierToPolynomial(static_cast<f32x4>(P1), static_cast<f32x4>(C), static_cast<f32x4>(P2));
46 return point2{a * t * t + b * t + c};
47}
48
49constexpr point2 bezierPointAt(
50 point2 P1,
51 point2 C1,
52 point2 C2,
53 point2 P2,
54 float t) noexcept
55{
56 hilet[a, b, c, d] =
57 bezierToPolynomial(static_cast<f32x4>(P1), static_cast<f32x4>(C1), static_cast<f32x4>(C2), static_cast<f32x4>(P2));
58 return point2{a * t * t * t + b * t * t + c * t + d};
59}
60
61constexpr vector2 bezierTangentAt(point2 P1, point2 P2, float t) noexcept
62{
63 return P2 - P1;
64}
65
66constexpr vector2
67bezierTangentAt(point2 P1, point2 C, point2 P2, float t) noexcept
68{
69 hilet P1_ = static_cast<f32x4>(P1);
70 hilet C_ = static_cast<f32x4>(C);
71 hilet P2_ = static_cast<f32x4>(P2);
72
73 return vector2{2 * t * (P2_ - 2 * C_ + P1_) + 2 * (C_ - P1_)};
74}
75
76constexpr vector2 bezierTangentAt(
77 point2 P1,
78 point2 C1,
79 point2 C2,
80 point2 P2,
81 float t) noexcept
82{
83 hilet P1_ = static_cast<f32x4>(P1);
84 hilet C1_ = static_cast<f32x4>(C1);
85 hilet C2_ = static_cast<f32x4>(C2);
86 hilet P2_ = static_cast<f32x4>(P2);
87
88 return vector2{3 * t * t * (P2_ - 3 * C2_ + 3 * C1_ - P1_) + 6 * t * (C2_ - 2 * C1_ + P1_) + 3 * (C1_ - P1_)};
89}
90
91constexpr results<float, 1> bezierFindT(float P1, float P2, float x) noexcept
92{
93 hilet[a, b] = bezierToPolynomial(P1, P2);
94 return solvePolynomial(a, b - x);
95}
96
97constexpr results<float, 2> bezierFindT(float P1, float C, float P2, float x) noexcept
98{
99 hilet[a, b, c] = bezierToPolynomial(P1, C, P2);
100 return solvePolynomial(a, b, c - x);
101}
102
103hi_force_inline constexpr results<float, 3> bezierFindT(float P1, float C1, float C2, float P2, float x) noexcept
104{
105 hilet[a, b, c, d] = bezierToPolynomial(P1, C1, C2, P2);
106 return solvePolynomial(a, b, c, d - x);
107}
108
114{
115 hilet t_above = dot(P - P1, P2 - P1);
116 hilet t_below = dot(P2 - P1, P2 - P1);
117 if (t_below == 0.0) {
118 [[unlikely]] return {};
119 } else {
120 return {t_above / t_below};
121 }
122}
123
128hi_force_inline constexpr results<float, 3>
130{
131 hilet P1_ = static_cast<f32x4>(P1);
132 hilet P2_ = static_cast<f32x4>(P2);
133 hilet C_ = static_cast<f32x4>(C);
134
135 hilet p = P - P1;
136 hilet p1 = C - P1;
137 hilet p2 = vector2{P2_ - (2 * C_) + P1_};
138
139 hilet a = dot(p2, p2);
140 hilet b = 3 * dot(p1, p2);
141 hilet c = dot(2 * p1, p1) - dot(p2, p);
142 hilet d = -dot(p1, p);
143 return solvePolynomial(a, b, c, d);
144}
145
153constexpr results<float, 1> bezierFindX(point2 P1, point2 P2, float y) noexcept
154{
155 if (y < std::min({P1.y(), P2.y()}) || y > std::max({P1.y(), P2.y()})) {
156 return {};
157 }
158
160 for (hilet t : bezierFindT(P1.y(), P2.y(), y)) {
161 if (t >= 0.0f && t < 1.0f) {
162 r.add(bezierPointAt(P1, P2, t).x());
163 }
164 }
165
166 return r;
167}
168
176constexpr results<float, 2> bezierFindX(point2 P1, point2 C, point2 P2, float y) noexcept
177{
179
180 if (y < std::min({P1.y(), C.y(), P2.y()}) || y > std::max({P1.y(), C.y(), P2.y()})) {
181 return r;
182 }
183
184 for (hilet t : bezierFindT(P1.y(), C.y(), P2.y(), y)) {
185 if (t >= 0.0f && t <= 1.0f) {
186 r.add(bezierPointAt(P1, C, P2, t).x());
187 }
188 }
189
190 return r;
191}
192
200constexpr results<float, 3> bezierFindX(point2 P1, point2 C1, point2 C2, point2 P2, float y) noexcept
201{
203
204 if (y < std::min({P1.y(), C1.y(), C2.y(), P2.y()}) || y > std::max({P1.y(), C1.y(), C2.y(), P2.y()})) {
205 return r;
206 }
207
208 for (hilet t : bezierFindT(P1.y(), C1.y(), C2.y(), P2.y(), y)) {
209 if (t >= 0.0f && t <= 1.0f) {
210 r.add(bezierPointAt(P1, C1, C2, P2, t).x());
211 }
212 }
213
214 return r;
215}
216
220inline float bezierFlatness(point2 P1, point2 P2) noexcept
221{
222 return 1.0f;
223}
224
229inline float bezierFlatness(point2 P1, point2 C, point2 P2) noexcept
230{
231 hilet P1P2 = hypot(P2 - P1);
232 if (P1P2 == 0.0f) {
233 return 1.0;
234 }
235
236 hilet P1C1 = hypot(C - P1);
237 hilet C1P2 = hypot(P2 - C);
238 return P1P2 / (P1C1 + C1P2);
239}
240
245inline float bezierFlatness(point2 P1, point2 C1, point2 C2, point2 P2) noexcept
246{
247 hilet P1P2 = hypot(P2 - P1);
248 if (P1P2 == 0.0f) {
249 return 1.0;
250 }
251
252 hilet P1C1 = hypot(C1 - P1);
253 hilet C1C2 = hypot(C2 - C1);
254 hilet C2P2 = hypot(P2 - C2);
255 return P1P2 / (P1C1 + C1C2 + C2P2);
256}
257
258inline std::pair<point2, point2> parallelLine(point2 P1, point2 P2, float distance) noexcept
259{
260 hilet v = P2 - P1;
261 hilet n = normal(v);
262 return {P1 + n * distance, P2 + n * distance};
263}
264
267inline std::optional<point2> getIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
268{
269 // convert points to vectors.
270 hilet p = A1;
271 hilet r = A2 - A1;
272 hilet q = B1;
273 hilet s = B2 - B1;
274
275 // find t and u in:
276 // p + t*r == q + us
277 hilet crossRS = cross(r, s);
278 if (crossRS == 0.0f) {
279 // Parallel, other non, or a range of points intersect.
280 return {};
281
282 } else {
283 hilet q_min_p = q - p;
284 hilet t = cross(q_min_p, s) / crossRS;
285 hilet u = cross(q_min_p, r) / crossRS;
286
287 if (t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f) {
288 return bezierPointAt(A1, A2, t);
289 } else {
290 // The lines intersect outside of one or both of the segments.
291 return {};
292 }
293 }
294}
295
298inline std::optional<point2> getExtrapolatedIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
299{
300 // convert points to vectors.
301 hilet p = A1;
302 hilet r = A2 - A1;
303 hilet q = B1;
304 hilet s = B2 - B1;
305
306 // find t and u in:
307 // p + t*r == q + us
308 hilet crossRS = cross(r, s);
309 if (crossRS == 0.0f) {
310 // Parallel, other non, or a range of points intersect.
311 return {};
312
313 } else {
314 hilet q_min_p = q - p;
315 hilet t = cross(q_min_p, s) / crossRS;
316
317 return bezierPointAt(A1, A2, t);
318 }
319}
320
321} // namespace hi::inline v1
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:13
constexpr results< float, 1 > bezierFindX(point2 P1, point2 P2, float y) noexcept
Definition bezier.hpp:153
std::optional< point2 > getExtrapolatedIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
Definition bezier.hpp:298
float bezierFlatness(point2 P1, point2 P2) noexcept
Definition bezier.hpp:220
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:113
std::optional< point2 > getIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
Definition bezier.hpp:267
hi_force_inline constexpr results< T, 1 > solvePolynomial(T const &a, T const &b) noexcept
Definition polynomial.hpp:141
A high-level geometric point Part of the high-level vec, point, mat and color types.
Definition point2.hpp:23
constexpr float & x() noexcept
Access the x element from the point.
Definition point2.hpp:60
Definition polynomial.hpp:14
T max(T... args)
T min(T... args)