6#include "TTauri/Foundation/polynomial.hpp"
7#include "TTauri/Foundation/vec.hpp"
8#include "TTauri/Foundation/mat.hpp"
38inline std::array<T,4> bezierToPolynomial(T P1, T C1, T C2, T P2)
noexcept
41 ttlet min_3 = T{-3.0};
44 -P1 + C1 * _3 - C2 * _3 + P2,
45 P1 * _3 - C1 * _6 + C2 * _3,
51inline vec bezierPointAt(vec P1, vec P2,
float t)
noexcept {
52 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
54 ttlet [a, b] = bezierToPolynomial(P1, P2);
59inline vec bezierPointAt(vec P1, vec C, vec P2,
float t)
noexcept
61 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f);
63 ttlet [a, b, c] = bezierToPolynomial(P1, C, P2);
66 return a*t_*t_ + b*t_ + c;
69inline vec bezierPointAt(vec P1, vec C1, vec C2, vec P2,
float t)
noexcept
71 tt_assume(P1.w() == 1.0f && C1.w() == 1.0f && C2.w() && P2.w() == 1.0f);
73 ttlet [a, b, c, d] = bezierToPolynomial(P1, C1, C2, P2);
75 return a*t_*t_*t_ + b*t_*t_ + c*t_ + d;
78inline vec bezierTangentAt(vec P1, vec P2,
float t)
noexcept
80 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
85inline vec bezierTangentAt(vec P1, vec C, vec P2,
float t)
noexcept
87 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f);
91 return _2 * _t * (P2 - _2 * C + P1) + _2 * (C - P1);
94inline vec bezierTangentAt(vec P1, vec C1, vec C2, vec P2,
float t)
noexcept
96 tt_assume(P1.w() == 1.0f && C1.w() == 1.0f && C2.w() && P2.w() == 1.0f);
103 _3 * _t * _t * (P2 - _3 * C2 + _3 * C1 - P1) +
104 _6 * _t * (C2 - _2 * C1 + P1) +
108inline results<float,1> bezierFindT(
float P1,
float P2,
float x)
noexcept
110 ttlet [a, b] = bezierToPolynomial(P1, P2);
111 return solvePolynomial(a, b - x);
114inline results<float,2> bezierFindT(
float P1,
float C,
float P2,
float x)
noexcept
116 ttlet [a, b, c] = bezierToPolynomial(P1, C, P2);
117 return solvePolynomial(a, b, c - x);
120inline results<float,3> bezierFindT(
float P1,
float C1,
float C2,
float P2,
float x)
noexcept
122 ttlet [a, b, c, d] = bezierToPolynomial(P1, C1, C2, P2);
123 return solvePolynomial(a, b, c, d - x);
130inline results<float,1> bezierFindTForNormalsIntersectingPoint(vec P1, vec P2, vec P)
noexcept
132 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f && P.w() == 1.0f);
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)) {
139 return {t_above / t_below};
147inline results<float,3> bezierFindTForNormalsIntersectingPoint(vec P1, vec C, vec P2, vec P)
noexcept
149 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f && P.w() == 1.0f);
154 ttlet p2 = P2 - (_2 * C) + P1;
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);
170inline results<float,1> bezierFindX(vec P1, vec P2,
float y)
noexcept
172 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
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());
195inline results<float,2> bezierFindX(vec P1, vec C, vec P2,
float y)
noexcept
197 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f);
199 results<float,2> r{};
201 if (y <
std::min({P1.y(), C.y(), P2.y()}) || y >
std::max({P1.y(), C.y(), P2.y()})) {
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());
221inline results<float,3> bezierFindX(vec P1, vec C1, vec C2, vec P2,
float y)
noexcept
223 tt_assume(P1.w() == 1.0f && C1.w() == 1.0f && C2.w() == 1.0f && P2.w() == 1.0f);
225 results<float,3> r{};
227 if (y <
std::min({ P1.y(), C1.y(), C2.y(), P2.y() }) || y >
std::max({ P1.y(), C1.y(), C2.y(), P2.y() })) {
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());
243inline float bezierFlatness(vec P1, vec P2)
noexcept
245 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
254inline float bezierFlatness(vec P1, vec C, vec P2)
noexcept
256 tt_assume(P1.w() == 1.0f && C.w() == 1.0f && P2.w() == 1.0f);
258 ttlet P1P2 = length(P2 - P1);
263 ttlet P1C1 = length(C - P1);
264 ttlet C1P2 = length(P2 - C);
265 return P1P2 / (P1C1 + C1P2);
272inline float bezierFlatness(vec P1, vec C1, vec C2, vec P2)
noexcept
274 tt_assume(P1.w() == 1.0f && C1.w() == 1.0f && C2.w() == 1.0f && P2.w() == 1.0f);
276 ttlet P1P2 = length(P2 - P1);
281 ttlet P1C1 = length(C1 - P1);
282 ttlet C1C2 = length(C2 - C1);
283 ttlet C2P2 = length(P2 - C2);
284 return P1P2 / (P1C1 + C1C2 + C2P2);
289 tt_assume(P1.w() == 1.0f && P2.w() == 1.0f);
302inline std::optional<vec> getIntersectionPoint(vec A1, vec A2, vec B1, vec B2)
noexcept
304 tt_assume(A1.w() == 1.0f && A2.w() == 1.0f && B1.w() == 1.0f && B2.w() == 1.0f);
314 ttlet crossRS = viktor_cross(r, s);
315 if (crossRS == 0.0f) {
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;
324 if (t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f) {
325 return bezierPointAt(A1, A2, t);
335inline std::optional<vec> getExtrapolatedIntersectionPoint(vec A1, vec A2, vec B1, vec B2)
noexcept
337 tt_assume(A1.w() == 1.0f && A2.w() == 1.0f && B1.w() == 1.0f && B2.w() == 1.0f);
347 ttlet crossRS = viktor_cross(r, s);
348 if (crossRS == 0.0f) {
353 ttlet q_min_p = q - p;
354 ttlet t = viktor_cross(q_min_p, s) / crossRS;
356 return bezierPointAt(A1, A2, t);