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 "polynomial.hpp"
8#include "rapid/numeric_array.hpp"
9#include "geometry/point.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
36template<int D>
37constexpr geo::point<float, D> bezierPointAt(geo::point<float, D> P1, geo::point<float, D> P2, float t) noexcept
38{
39 hilet[a, b] = bezierToPolynomial(static_cast<f32x4>(P1), static_cast<f32x4>(P2));
40 return geo::point<float, D>{a * t + b};
41}
42
43template<int D>
44constexpr geo::point<float, D>
45bezierPointAt(geo::point<float, D> P1, geo::point<float, D> C, geo::point<float, D> P2, float t) noexcept
46{
47 hilet[a, b, c] = bezierToPolynomial(static_cast<f32x4>(P1), static_cast<f32x4>(C), static_cast<f32x4>(P2));
48 return geo::point<float, D>{a * t * t + b * t + c};
49}
50
51template<int D>
52constexpr geo::point<float, D> bezierPointAt(
53 geo::point<float, D> P1,
54 geo::point<float, D> C1,
55 geo::point<float, D> C2,
56 geo::point<float, D> P2,
57 float t) noexcept
58{
59 hilet[a, b, c, d] =
60 bezierToPolynomial(static_cast<f32x4>(P1), static_cast<f32x4>(C1), static_cast<f32x4>(C2), static_cast<f32x4>(P2));
61 return geo::point<float, D>{a * t * t * t + b * t * t + c * t + d};
62}
63
64template<int D>
65constexpr geo::vector<float, D> bezierTangentAt(geo::point<float, D> P1, geo::point<float, D> P2, float t) noexcept
66{
67 return P2 - P1;
68}
69
70template<int D>
71constexpr geo::vector<float, D>
72bezierTangentAt(geo::point<float, D> P1, geo::point<float, D> C, geo::point<float, D> P2, float t) noexcept
73{
74 hilet P1_ = static_cast<f32x4>(P1);
75 hilet C_ = static_cast<f32x4>(C);
76 hilet P2_ = static_cast<f32x4>(P2);
77
78 return geo::vector<float, D>{2 * t * (P2_ - 2 * C_ + P1_) + 2 * (C_ - P1_)};
79}
80
81template<int D>
82constexpr geo::vector<float, D> bezierTangentAt(
83 geo::point<float, D> P1,
84 geo::point<float, D> C1,
85 geo::point<float, D> C2,
86 geo::point<float, D> P2,
87 float t) noexcept
88{
89 hilet P1_ = static_cast<f32x4>(P1);
90 hilet C1_ = static_cast<f32x4>(C1);
91 hilet C2_ = static_cast<f32x4>(C2);
92 hilet P2_ = static_cast<f32x4>(P2);
93
94 return geo::vector<float, D>{3 * t * t * (P2_ - 3 * C2_ + 3 * C1_ - P1_) + 6 * t * (C2_ - 2 * C1_ + P1_) + 3 * (C1_ - P1_)};
95}
96
97constexpr results<float, 1> bezierFindT(float P1, float P2, float x) noexcept
98{
99 hilet[a, b] = bezierToPolynomial(P1, P2);
100 return solvePolynomial(a, b - x);
101}
102
103constexpr results<float, 2> bezierFindT(float P1, float C, float P2, float x) noexcept
104{
105 hilet[a, b, c] = bezierToPolynomial(P1, C, P2);
106 return solvePolynomial(a, b, c - x);
107}
108
109hi_force_inline constexpr results<float, 3> bezierFindT(float P1, float C1, float C2, float P2, float x) noexcept
110{
111 hilet[a, b, c, d] = bezierToPolynomial(P1, C1, C2, P2);
112 return solvePolynomial(a, b, c, d - x);
113}
114
120{
121 hilet t_above = dot(P - P1, P2 - P1);
122 hilet t_below = dot(P2 - P1, P2 - P1);
123 if (t_below == 0.0) {
124 [[unlikely]] return {};
125 } else {
126 return {t_above / t_below};
127 }
128}
129
134hi_force_inline constexpr results<float, 3>
136{
137 hilet P1_ = static_cast<f32x4>(P1);
138 hilet P2_ = static_cast<f32x4>(P2);
139 hilet C_ = static_cast<f32x4>(C);
140
141 hilet p = P - P1;
142 hilet p1 = C - P1;
143 hilet p2 = vector2{P2_ - (2 * C_) + P1_};
144
145 hilet a = dot(p2, p2);
146 hilet b = 3 * dot(p1, p2);
147 hilet c = dot(2 * p1, p1) - dot(p2, p);
148 hilet d = -dot(p1, p);
149 return solvePolynomial(a, b, c, d);
150}
151
159constexpr results<float, 1> bezierFindX(point2 P1, point2 P2, float y) noexcept
160{
161 if (y < std::min({P1.y(), P2.y()}) || y > std::max({P1.y(), P2.y()})) {
162 return {};
163 }
164
166 for (hilet t : bezierFindT(P1.y(), P2.y(), y)) {
167 if (t >= 0.0f && t < 1.0f) {
168 r.add(bezierPointAt(P1, P2, t).x());
169 }
170 }
171
172 return r;
173}
174
182constexpr results<float, 2> bezierFindX(point2 P1, point2 C, point2 P2, float y) noexcept
183{
185
186 if (y < std::min({P1.y(), C.y(), P2.y()}) || y > std::max({P1.y(), C.y(), P2.y()})) {
187 return r;
188 }
189
190 for (hilet t : bezierFindT(P1.y(), C.y(), P2.y(), y)) {
191 if (t >= 0.0f && t <= 1.0f) {
192 r.add(bezierPointAt(P1, C, P2, t).x());
193 }
194 }
195
196 return r;
197}
198
206constexpr results<float, 3> bezierFindX(point2 P1, point2 C1, point2 C2, point2 P2, float y) noexcept
207{
209
210 if (y < std::min({P1.y(), C1.y(), C2.y(), P2.y()}) || y > std::max({P1.y(), C1.y(), C2.y(), P2.y()})) {
211 return r;
212 }
213
214 for (hilet t : bezierFindT(P1.y(), C1.y(), C2.y(), P2.y(), y)) {
215 if (t >= 0.0f && t <= 1.0f) {
216 r.add(bezierPointAt(P1, C1, C2, P2, t).x());
217 }
218 }
219
220 return r;
221}
222
226inline float bezierFlatness(point2 P1, point2 P2) noexcept
227{
228 return 1.0f;
229}
230
235inline float bezierFlatness(point2 P1, point2 C, point2 P2) noexcept
236{
237 hilet P1P2 = hypot(P2 - P1);
238 if (P1P2 == 0.0f) {
239 return 1.0;
240 }
241
242 hilet P1C1 = hypot(C - P1);
243 hilet C1P2 = hypot(P2 - C);
244 return P1P2 / (P1C1 + C1P2);
245}
246
251inline float bezierFlatness(point2 P1, point2 C1, point2 C2, point2 P2) noexcept
252{
253 hilet P1P2 = hypot(P2 - P1);
254 if (P1P2 == 0.0f) {
255 return 1.0;
256 }
257
258 hilet P1C1 = hypot(C1 - P1);
259 hilet C1C2 = hypot(C2 - C1);
260 hilet C2P2 = hypot(P2 - C2);
261 return P1P2 / (P1C1 + C1C2 + C2P2);
262}
263
264inline std::pair<point2, point2> parallelLine(point2 P1, point2 P2, float distance) noexcept
265{
266 hilet v = P2 - P1;
267 hilet n = normal(v);
268 return {P1 + n * distance, P2 + n * distance};
269}
270
273inline std::optional<point2> getIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
274{
275 // convert points to vectors.
276 hilet p = A1;
277 hilet r = A2 - A1;
278 hilet q = B1;
279 hilet s = B2 - B1;
280
281 // find t and u in:
282 // p + t*r == q + us
283 hilet crossRS = cross(r, s);
284 if (crossRS == 0.0f) {
285 // Parallel, other non, or a range of points intersect.
286 return {};
287
288 } else {
289 hilet q_min_p = q - p;
290 hilet t = cross(q_min_p, s) / crossRS;
291 hilet u = cross(q_min_p, r) / crossRS;
292
293 if (t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f) {
294 return bezierPointAt(A1, A2, t);
295 } else {
296 // The lines intersect outside of one or both of the segments.
297 return {};
298 }
299 }
300}
301
304inline std::optional<point2> getExtrapolatedIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
305{
306 // convert points to vectors.
307 hilet p = A1;
308 hilet r = A2 - A1;
309 hilet q = B1;
310 hilet s = B2 - B1;
311
312 // find t and u in:
313 // p + t*r == q + us
314 hilet crossRS = cross(r, s);
315 if (crossRS == 0.0f) {
316 // Parallel, other non, or a range of points intersect.
317 return {};
318
319 } else {
320 hilet q_min_p = q - p;
321 hilet t = cross(q_min_p, s) / crossRS;
322
323 return bezierPointAt(A1, A2, t);
324 }
325}
326
327} // namespace hi::inline v1
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
constexpr results< float, 1 > bezierFindX(point2 P1, point2 P2, float y) noexcept
Definition bezier.hpp:159
std::optional< point2 > getExtrapolatedIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
Definition bezier.hpp:304
float bezierFlatness(point2 P1, point2 P2) noexcept
Definition bezier.hpp:226
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:119
std::optional< point2 > getIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
Definition bezier.hpp:273
hi_force_inline constexpr results< T, 1 > solvePolynomial(T const &a, T const &b) noexcept
Definition polynomial.hpp:141
Definition polynomial.hpp:14
T max(T... args)
T min(T... args)