HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
bezier.hpp
1// Copyright 2019, 2020 Pokitec
2// All rights reserved.
3
4#pragma once
5
6#include "TTauri/Foundation/polynomial.hpp"
7#include "TTauri/Foundation/vec.hpp"
8#include "TTauri/Foundation/mat.hpp"
9#include <array>
10#include <optional>
11
12namespace tt {
13
14// B(t)=(P_{2}-P_{1})t+P_{1}
15template<typename T>
16inline std::array<T,2> bezierToPolynomial(T P1, T P2) noexcept
17{
18 return {
19 P2 - P1,
20 P1
21 };
22}
23
24// B(t)=(P_{1}-2C+P_{2})t^{2}+2(C-P_{1})t+P_{1}
25template<typename T>
26inline std::array<T,3> bezierToPolynomial(T P1, T C, T P2) noexcept
27{
28 ttlet _2 = T{2.0};
29 return {
30 P1 - C * _2 + P2,
31 (C - P1) * _2,
32 P1
33 };
34}
35
36// 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}
37template<typename T>
38inline std::array<T,4> bezierToPolynomial(T P1, T C1, T C2, T P2) noexcept
39{
40 ttlet _3 = T{3.0};
41 ttlet min_3 = T{-3.0};
42 ttlet _6 = T{6.0};
43 return {
44 -P1 + C1 * _3 - C2 * _3 + P2,
45 P1 * _3 - C1 * _6 + C2 * _3,
46 P1 * min_3 + C1 * _3,
47 P1
48 };
49}
50
51inline vec bezierPointAt(vec P1, vec P2, float t) noexcept {
52 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
53
54 ttlet [a, b] = bezierToPolynomial(P1, P2);
55 ttlet t_ = vec{t};
56 return a * t_ + b;
57}
58
59inline vec bezierPointAt(vec P1, vec C, vec P2, float t) noexcept
60{
61 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f);
62
63 ttlet [a, b, c] = bezierToPolynomial(P1, C, P2);
64
65 ttlet t_ = vec{t};
66 return a*t_*t_ + b*t_ + c;
67}
68
69inline vec bezierPointAt(vec P1, vec C1, vec C2, vec P2, float t) noexcept
70{
71 tt_assume(P1.w() == 1.0f && C1.w() == 1.0f && C2.w() && P2.w() == 1.0f);
72
73 ttlet [a, b, c, d] = bezierToPolynomial(P1, C1, C2, P2);
74 ttlet t_ = vec{t};
75 return a*t_*t_*t_ + b*t_*t_ + c*t_ + d;
76}
77
78inline vec bezierTangentAt(vec P1, vec P2, float t) noexcept
79{
80 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
81
82 return P2 - P1;
83}
84
85inline vec bezierTangentAt(vec P1, vec C, vec P2, float t) noexcept
86{
87 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f);
88
89 ttlet _2 = vec{2.0};
90 ttlet _t = vec{t};
91 return _2 * _t * (P2 - _2 * C + P1) + _2 * (C - P1);
92}
93
94inline vec bezierTangentAt(vec P1, vec C1, vec C2, vec P2, float t) noexcept
95{
96 tt_assume(P1.w() == 1.0f && C1.w() == 1.0f && C2.w() && P2.w() == 1.0f);
97
98 ttlet _2 = vec{2.0};
99 ttlet _3 = vec{3.0};
100 ttlet _6 = vec{6.0};
101 ttlet _t = vec{t};
102 return
103 _3 * _t * _t * (P2 - _3 * C2 + _3 * C1 - P1) +
104 _6 * _t * (C2 - _2 * C1 + P1) +
105 _3 * (C1 - P1);
106}
107
108inline results<float,1> bezierFindT(float P1, float P2, float x) noexcept
109{
110 ttlet [a, b] = bezierToPolynomial(P1, P2);
111 return solvePolynomial(a, b - x);
112}
113
114inline results<float,2> bezierFindT(float P1, float C, float P2, float x) noexcept
115{
116 ttlet [a, b, c] = bezierToPolynomial(P1, C, P2);
117 return solvePolynomial(a, b, c - x);
118}
119
120inline results<float,3> bezierFindT(float P1, float C1, float C2, float P2, float x) noexcept
121{
122 ttlet [a, b, c, d] = bezierToPolynomial(P1, C1, C2, P2);
123 return solvePolynomial(a, b, c, d - x);
124}
125
130inline results<float,1> bezierFindTForNormalsIntersectingPoint(vec P1, vec P2, vec P) noexcept
131{
132 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f && P.w() == 1.0f);
133
134 auto t_above = dot(P - P1, P2 - P1);
135 auto t_below = dot(P2 - P1, P2 - P1);
136 if (tt_unlikely(t_below == 0.0)) {
137 return {};
138 } else {
139 return {t_above / t_below};
140 }
141}
142
147inline results<float,3> bezierFindTForNormalsIntersectingPoint(vec P1, vec C, vec P2, vec P) noexcept
148{
149 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f && P.w() == 1.0f);
150
151 ttlet _2 = vec{2.0};
152 ttlet p = P - P1;
153 ttlet p1 = C - P1;
154 ttlet p2 = P2 - (_2 * C) + P1;
155
156 ttlet a = dot(p2, p2);
157 ttlet b = 3.0f * dot(p1, p2);
158 ttlet c = dot(_2 * p1, p1) - dot(p2, p);
159 ttlet d = -dot(p1, p);
160 return solvePolynomial(a, b, c, d);
161}
162
170inline results<float,1> bezierFindX(vec P1, vec P2, float y) noexcept
171{
172 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
173
174 if (y < std::min({P1.y(), P2.y()}) || y > std::max({P1.y(), P2.y()})) {
175 return {};
176 }
177
178 results<float,1> r;
179 for (ttlet t: bezierFindT(P1.y(), P2.y(), y)) {
180 if (t >= 0.0f && t < 1.0f) {
181 r.add(bezierPointAt(P1, P2, t).x());
182 }
183 }
184
185 return r;
186}
187
195inline results<float,2> bezierFindX(vec P1, vec C, vec P2, float y) noexcept
196{
197 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f);
198
199 results<float,2> r{};
200
201 if (y < std::min({P1.y(), C.y(), P2.y()}) || y > std::max({P1.y(), C.y(), P2.y()})) {
202 return r;
203 }
204
205 for (ttlet t: bezierFindT(P1.y(), C.y(), P2.y(), y)) {
206 if (t >= 0.0f && t <= 1.0f) {
207 r.add(bezierPointAt(P1, C, P2, t).x());
208 }
209 }
210
211 return r;
212}
213
221inline results<float,3> bezierFindX(vec P1, vec C1, vec C2, vec P2, float y) noexcept
222{
223 tt_assume(P1.w() == 1.0f && C1.w() == 1.0f && C2.w() == 1.0f && P2.w() == 1.0f);
224
225 results<float,3> r{};
226
227 if (y < std::min({ P1.y(), C1.y(), C2.y(), P2.y() }) || y > std::max({ P1.y(), C1.y(), C2.y(), P2.y() })) {
228 return r;
229 }
230
231 for (ttlet t: bezierFindT(P1.y(), C1.y(), C2.y(), P2.y(), y)) {
232 if (t >= 0.0f && t <= 1.0f) {
233 r.add(bezierPointAt(P1, C1, C2, P2, t).x());
234 }
235 }
236
237 return r;
238}
239
243inline float bezierFlatness(vec P1, vec P2) noexcept
244{
245 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
246
247 return 1.0f;
248}
249
254inline float bezierFlatness(vec P1, vec C, vec P2) noexcept
255{
256 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f);
257
258 ttlet P1P2 = length(P2 - P1);
259 if (P1P2 == 0.0f) {
260 return 1.0;
261 }
262
263 ttlet P1C1 = length(C - P1);
264 ttlet C1P2 = length(P2 - C);
265 return P1P2 / (P1C1 + C1P2);
266}
267
272inline float bezierFlatness(vec P1, vec C1, vec C2, vec P2) noexcept
273{
274 tt_assume(P1.w() == 1.0f && C1.w() == 1.0f && C2.w() == 1.0f && P2.w() == 1.0f);
275
276 ttlet P1P2 = length(P2 - P1);
277 if (P1P2 == 0.0f) {
278 return 1.0;
279 }
280
281 ttlet P1C1 = length(C1 - P1);
282 ttlet C1C2 = length(C2 - C1);
283 ttlet C2P2 = length(P2 - C2);
284 return P1P2 / (P1C1 + C1C2 + C2P2);
285}
286
287inline std::pair<vec, vec> parrallelLine(vec P1, vec P2, float distance) noexcept
288{
289 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
290
291 ttlet _distance = vec{distance};
292 ttlet v = P2 - P1;
293 ttlet n = normal(v);
294 return {
295 P1 + n * _distance,
296 P2 + n * _distance
297 };
298}
299
302inline std::optional<vec> getIntersectionPoint(vec A1, vec A2, vec B1, vec B2) noexcept
303{
304 tt_assume(A1.w() == 1.0f && A2.w() == 1.0f && B1.w() == 1.0f && B2.w() == 1.0f);
305
306 // convert points to vectors.
307 ttlet p = A1;
308 ttlet r = A2 - A1;
309 ttlet q = B1;
310 ttlet s = B2 - B1;
311
312 // find t and u in:
313 // p + t*r == q + us
314 ttlet crossRS = viktor_cross(r, s);
315 if (crossRS == 0.0f) {
316 // Parallel, other non, or a range of points intersect.
317 return {};
318
319 } else {
320 ttlet q_min_p = q - p;
321 ttlet t = viktor_cross(q_min_p, s) / crossRS;
322 ttlet u = viktor_cross(q_min_p, r) / crossRS;
323
324 if (t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f) {
325 return bezierPointAt(A1, A2, t);
326 } else {
327 // The lines intersect outside of one or both of the segments.
328 return {};
329 }
330 }
331}
332
335inline std::optional<vec> getExtrapolatedIntersectionPoint(vec A1, vec A2, vec B1, vec B2) noexcept
336{
337 tt_assume(A1.w() == 1.0f && A2.w() == 1.0f && B1.w() == 1.0f && B2.w() == 1.0f);
338
339 // convert points to vectors.
340 ttlet p = A1;
341 ttlet r = A2 - A1;
342 ttlet q = B1;
343 ttlet s = B2 - B1;
344
345 // find t and u in:
346 // p + t*r == q + us
347 ttlet crossRS = viktor_cross(r, s);
348 if (crossRS == 0.0f) {
349 // Parallel, other non, or a range of points intersect.
350 return {};
351
352 } else {
353 ttlet q_min_p = q - p;
354 ttlet t = viktor_cross(q_min_p, s) / crossRS;
355
356 return bezierPointAt(A1, A2, t);
357 }
358}
359
360}
T distance(T... args)
T max(T... args)
T min(T... args)