7#include "polynomial.hpp"
8#include "rapid/numeric_array.hpp"
9#include "geometry/point.hpp"
26 return {P1 - C * 2 + P2, (C - P1) * 2, P1};
31constexpr std::array<T, 4> bezierToPolynomial(T P1, T C1, T C2, T P2)
noexcept
33 return {-P1 + C1 * 3 - C2 * 3 + P2, P1 * 3 - C1 * 6 + C2 * 3, P1 * -3 + C1 * 3, P1};
37constexpr geo::point<D> bezierPointAt(geo::point<D> P1, geo::point<D> P2,
float t)
noexcept
39 ttlet[a, b] = bezierToPolynomial(
static_cast<f32x4
>(P1),
static_cast<f32x4
>(P2));
40 return geo::point<D>{a * t + b};
44constexpr geo::point<D> bezierPointAt(geo::point<D> P1, geo::point<D> C, geo::point<D> P2,
float t)
noexcept
46 ttlet[a, b, c] = bezierToPolynomial(
static_cast<f32x4
>(P1),
static_cast<f32x4
>(C),
static_cast<f32x4
>(P2));
47 return geo::point<D>{a * t * t + b * t + c};
51constexpr geo::point<D> bezierPointAt(geo::point<D> P1, geo::point<D> C1, geo::point<D> C2, geo::point<D> P2,
float t)
noexcept
54 bezierToPolynomial(
static_cast<f32x4
>(P1),
static_cast<f32x4
>(C1),
static_cast<f32x4
>(C2),
static_cast<f32x4
>(P2));
55 return geo::point<D>{a * t * t * t + b * t * t + c * t + d};
59inline geo::vector<D> bezierTangentAt(geo::point<D> P1, geo::point<D> P2,
float t)
noexcept
65inline geo::vector<D> bezierTangentAt(geo::point<D> P1, geo::point<D> C, geo::point<D> P2,
float t)
noexcept
67 ttlet P1_ =
static_cast<f32x4
>(P1);
68 ttlet C_ =
static_cast<f32x4
>(C);
69 ttlet P2_ =
static_cast<f32x4
>(P2);
71 return geo::vector<D>{2 * t * (P2_ - 2 * C_ + P1_) + 2 * (C_ - P1_)};
75inline geo::vector<D> bezierTangentAt(geo::point<D> P1, geo::point<D> C1, geo::point<D> C2, geo::point<D> P2,
float t)
noexcept
77 ttlet P1_ =
static_cast<f32x4
>(P1);
78 ttlet C1_ =
static_cast<f32x4
>(C1);
79 ttlet C2_ =
static_cast<f32x4
>(C2);
80 ttlet P2_ =
static_cast<f32x4
>(P2);
82 return geo::vector<D>{3 * t * t * (P2_ - 3 * C2_ + 3 * C1_ - P1_) + 6 * t * (C2_ - 2 * C1_ + P1_) + 3 * (C1_ - P1_)};
85inline results<float, 1> bezierFindT(
float P1,
float P2,
float x)
noexcept
87 ttlet[a, b] = bezierToPolynomial(P1, P2);
88 return solvePolynomial(a, b - x);
91inline results<float, 2> bezierFindT(
float P1,
float C,
float P2,
float x)
noexcept
93 ttlet[a, b, c] = bezierToPolynomial(P1, C, P2);
94 return solvePolynomial(a, b, c - x);
97inline results<float, 3> bezierFindT(
float P1,
float C1,
float C2,
float P2,
float x)
noexcept
99 ttlet[a, b, c, d] = bezierToPolynomial(P1, C1, C2, P2);
100 return solvePolynomial(a, b, c, d - x);
107inline results<float, 1> bezierFindTForNormalsIntersectingPoint(point2 P1, point2 P2, point2 P)
noexcept
109 auto t_above = dot(P - P1, P2 - P1);
110 auto t_below = dot(P2 - P1, P2 - P1);
111 if (t_below == 0.0) {
112 [[unlikely]]
return {};
114 return {t_above / t_below};
122inline results<float, 3> bezierFindTForNormalsIntersectingPoint(point2 P1, point2 C, point2 P2, point2 P)
noexcept
124 ttlet P1_ =
static_cast<f32x4
>(P1);
125 ttlet P2_ =
static_cast<f32x4
>(P2);
126 ttlet C_ =
static_cast<f32x4
>(C);
130 ttlet p2 = vector2{P2_ - (2 * C_) + P1_};
132 ttlet a = dot(p2, p2);
133 ttlet b = 3 * dot(p1, p2);
134 ttlet c = dot(2 * p1, p1) - dot(p2, p);
135 ttlet d = -dot(p1, p);
136 return solvePolynomial(a, b, c, d);
146inline results<float, 1> bezierFindX(point2 P1, point2 P2,
float y)
noexcept
153 for (ttlet t : bezierFindT(P1.y(), P2.y(), y)) {
154 if (t >= 0.0f && t < 1.0f) {
155 r.add(bezierPointAt(P1, P2, t).x());
169inline results<float, 2> bezierFindX(point2 P1, point2 C, point2 P2,
float y)
noexcept
171 results<float, 2> r{};
173 if (y <
std::min({P1.y(), C.y(), P2.y()}) || y >
std::max({P1.y(), C.y(), P2.y()})) {
177 for (ttlet 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());
193inline results<float, 3> bezierFindX(point2 P1, point2 C1, point2 C2, point2 P2,
float y)
noexcept
195 results<float, 3> r{};
197 if (y <
std::min({P1.y(), C1.y(), C2.y(), P2.y()}) || y >
std::max({P1.y(), C1.y(), C2.y(), P2.y()})) {
201 for (ttlet 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());
213inline float bezierFlatness(point2 P1, point2 P2)
noexcept
222inline float bezierFlatness(point2 P1, point2 C, point2 P2)
noexcept
224 ttlet P1P2 =
hypot(P2 - P1);
229 ttlet P1C1 =
hypot(C - P1);
230 ttlet C1P2 =
hypot(P2 - C);
231 return P1P2 / (P1C1 + C1P2);
238inline float bezierFlatness(point2 P1, point2 C1, point2 C2, point2 P2)
noexcept
240 ttlet P1P2 =
hypot(P2 - P1);
245 ttlet P1C1 =
hypot(C1 - P1);
246 ttlet C1C2 =
hypot(C2 - C1);
247 ttlet C2P2 =
hypot(P2 - C2);
248 return P1P2 / (P1C1 + C1C2 + C2P2);
260inline std::optional<point2> getIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2)
noexcept
270 ttlet crossRS = cross(r, s);
271 if (crossRS == 0.0f) {
276 ttlet q_min_p = q - p;
277 ttlet t = cross(q_min_p, s) / crossRS;
278 ttlet u = cross(q_min_p, r) / crossRS;
280 if (t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f) {
281 return bezierPointAt(A1, A2, t);
291inline std::optional<point2> getExtrapolatedIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2)
noexcept
301 ttlet crossRS = cross(r, s);
302 if (crossRS == 0.0f) {
307 ttlet q_min_p = q - p;
308 ttlet t = cross(q_min_p, s) / crossRS;
310 return bezierPointAt(A1, A2, t);