1use axiolid_core::{Frame3, Point3, Scalar, Vec3};
28
29#[derive(Debug, Clone, Copy, PartialEq, Default)]
31#[cfg_attr(
32 feature = "serde",
33 derive(serde::Serialize, serde::Deserialize),
34 serde(deny_unknown_fields)
35)]
36pub struct Trig2 {
37 pub constant: Scalar,
39 pub cos: Scalar,
41 pub sin: Scalar,
43 pub cos2: Scalar,
45 pub sin2: Scalar,
47}
48
49impl Trig2 {
50 #[must_use]
52 pub fn value(&self, t: Scalar) -> Scalar {
53 let (s, c) = t.sin_cos();
54 let (s2, c2) = (2.0 * t).sin_cos();
55 self.constant + self.cos * c + self.sin * s + self.cos2 * c2 + self.sin2 * s2
56 }
57
58 #[must_use]
60 pub fn derivative(&self, t: Scalar) -> Scalar {
61 let (s, c) = t.sin_cos();
62 let (s2, c2) = (2.0 * t).sin_cos();
63 -self.cos * s + self.sin * c - 2.0 * self.cos2 * s2 + 2.0 * self.sin2 * c2
64 }
65
66 #[must_use]
68 pub fn second(&self, t: Scalar) -> Scalar {
69 let (s, c) = t.sin_cos();
70 let (s2, c2) = (2.0 * t).sin_cos();
71 -self.cos * c - self.sin * s - 4.0 * self.cos2 * c2 - 4.0 * self.sin2 * s2
72 }
73
74 #[must_use]
76 pub fn is_finite(&self) -> bool {
77 self.constant.is_finite()
78 && self.cos.is_finite()
79 && self.sin.is_finite()
80 && self.cos2.is_finite()
81 && self.sin2.is_finite()
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87#[cfg_attr(
88 feature = "serde",
89 derive(serde::Serialize, serde::Deserialize),
90 serde(deny_unknown_fields)
91)]
92pub enum Branch {
93 Plus,
95 Minus,
97}
98
99impl Branch {
100 #[must_use]
102 pub fn sign(self) -> Scalar {
103 match self {
104 Self::Plus => 1.0,
105 Self::Minus => -1.0,
106 }
107 }
108}
109
110#[derive(Debug, Clone, Copy, PartialEq)]
124#[cfg_attr(
125 feature = "serde",
126 derive(serde::Serialize, serde::Deserialize),
127 serde(deny_unknown_fields)
128)]
129pub struct QuadraticGraph2 {
130 pub a: Trig2,
132 pub b: Trig2,
134 pub c: Trig2,
136 pub branch: Branch,
138}
139
140impl QuadraticGraph2 {
141 #[must_use]
143 pub fn discriminant(&self, t: Scalar) -> Scalar {
144 let b = self.b.value(t);
145 b * b - 4.0 * self.a.value(t) * self.c.value(t)
146 }
147
148 #[must_use]
150 pub fn height(&self, t: Scalar) -> Option<Scalar> {
151 let (a, b, c) = (self.a.value(t), self.b.value(t), self.c.value(t));
152 let d = b * b - 4.0 * a * c;
153 let size =
159 |t: &Trig2| t.constant.abs() + t.cos.abs() + t.sin.abs() + t.cos2.abs() + t.sin2.abs();
160 let scale = size(&self.b).powi(2) + 4.0 * size(&self.a) * size(&self.c);
161 if d < -1e-12 * scale {
162 return None;
163 }
164 let root = d.max(0.0).sqrt();
165 let q = -0.5 * (b + if b >= 0.0 { 1.0 } else { -1.0 } * root);
171 let v = if q == 0.0 {
172 if a == 0.0 {
174 return None;
175 }
176 0.0
177 } else if (self.branch == Branch::Plus) == (b >= 0.0) {
178 c / q
179 } else {
180 q / a
181 };
182 v.is_finite().then_some(v)
185 }
186
187 #[must_use]
191 pub fn slope(&self, t: Scalar) -> Option<Scalar> {
192 let v = self.height(t)?;
193 let f_t = self.a.derivative(t) * v * v + self.b.derivative(t) * v + self.c.derivative(t);
194 let f_v = 2.0 * self.a.value(t) * v + self.b.value(t);
195 let slope = -f_t / f_v;
196 slope.is_finite().then_some(slope)
197 }
198
199 #[must_use]
201 pub fn bend(&self, t: Scalar) -> Option<Scalar> {
202 let v = self.height(t)?;
203 let p = self.slope(t)?;
204 let f_v = 2.0 * self.a.value(t) * v + self.b.value(t);
205 let f_tt = self.a.second(t) * v * v + self.b.second(t) * v + self.c.second(t);
206 let f_tv = 2.0 * self.a.derivative(t) * v + self.b.derivative(t);
207 let f_vv = 2.0 * self.a.value(t);
208 let bend = -(f_tt + 2.0 * f_tv * p + f_vv * p * p) / f_v;
209 bend.is_finite().then_some(bend)
210 }
211
212 #[must_use]
214 pub fn is_finite(&self) -> bool {
215 self.a.is_finite() && self.b.is_finite() && self.c.is_finite()
216 }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq)]
224#[cfg_attr(
225 feature = "serde",
226 derive(serde::Serialize, serde::Deserialize),
227 serde(deny_unknown_fields)
228)]
229pub struct RuledCarrier {
230 pub frame: Frame3,
232 pub x_radius: Scalar,
234 pub y_radius: Scalar,
236 pub slope: Scalar,
238}
239
240impl RuledCarrier {
241 #[must_use]
243 pub fn point(&self, u: Scalar, v: Scalar) -> Point3 {
244 let (s, c) = u.sin_cos();
245 self.frame.origin
246 + self.frame.x * ((self.x_radius + self.slope * v) * c)
247 + self.frame.y * ((self.y_radius + self.slope * v) * s)
248 + self.frame.z * v
249 }
250
251 #[must_use]
253 pub fn partials(&self, u: Scalar, v: Scalar) -> (Vec3, Vec3) {
254 let (s, c) = u.sin_cos();
255 (
256 self.frame.x * (-(self.x_radius + self.slope * v) * s)
257 + self.frame.y * ((self.y_radius + self.slope * v) * c),
258 self.frame.x * (self.slope * c) + self.frame.y * (self.slope * s) + self.frame.z,
259 )
260 }
261
262 #[must_use]
264 pub fn is_finite(&self) -> bool {
265 self.frame.origin.is_finite()
266 && self.frame.x.is_finite()
267 && self.frame.y.is_finite()
268 && self.frame.z.is_finite()
269 && self.x_radius.is_finite()
270 && self.y_radius.is_finite()
271 && self.slope.is_finite()
272 }
273}
274
275#[derive(Debug, Clone, Copy, PartialEq)]
278#[cfg_attr(
279 feature = "serde",
280 derive(serde::Serialize, serde::Deserialize),
281 serde(deny_unknown_fields)
282)]
283pub struct RuledSection3 {
284 pub carrier: RuledCarrier,
286 pub graph: QuadraticGraph2,
288}
289
290impl RuledSection3 {
291 #[must_use]
293 pub fn point(&self, t: Scalar) -> Option<Point3> {
294 Some(self.carrier.point(t, self.graph.height(t)?))
295 }
296
297 #[must_use]
299 pub fn tangent(&self, t: Scalar) -> Option<Vec3> {
300 let v = self.graph.height(t)?;
301 let (pu, pv) = self.carrier.partials(t, v);
302 Some(pu + pv * self.graph.slope(t)?)
303 }
304
305 #[must_use]
308 pub fn bend(&self, t: Scalar) -> Option<Vec3> {
309 let v = self.graph.height(t)?;
310 let slope = self.graph.slope(t)?;
311 let bend = self.graph.bend(t)?;
312 let (s, c) = t.sin_cos();
313 let k = &self.carrier;
314 let p_uu = k.frame.x * (-(k.x_radius + k.slope * v) * c)
315 + k.frame.y * (-(k.y_radius + k.slope * v) * s);
316 let p_uv = k.frame.x * (-k.slope * s) + k.frame.y * (k.slope * c);
317 let (_, p_v) = k.partials(t, v);
318 Some(p_uu + p_uv * (2.0 * slope) + p_v * bend)
319 }
320}