axiolid_curve/
quadric_section.rs1use axiolid_core::{Frame3, Point3, Scalar, Vec3};
28
29#[derive(Debug, Clone, Copy, PartialEq, Default)]
31pub struct Trig2 {
32 pub constant: Scalar,
34 pub cos: Scalar,
36 pub sin: Scalar,
38 pub cos2: Scalar,
40 pub sin2: Scalar,
42}
43
44impl Trig2 {
45 #[must_use]
47 pub fn value(&self, t: Scalar) -> Scalar {
48 let (s, c) = t.sin_cos();
49 let (s2, c2) = (2.0 * t).sin_cos();
50 self.constant + self.cos * c + self.sin * s + self.cos2 * c2 + self.sin2 * s2
51 }
52
53 #[must_use]
55 pub fn derivative(&self, t: Scalar) -> Scalar {
56 let (s, c) = t.sin_cos();
57 let (s2, c2) = (2.0 * t).sin_cos();
58 -self.cos * s + self.sin * c - 2.0 * self.cos2 * s2 + 2.0 * self.sin2 * c2
59 }
60
61 #[must_use]
63 pub fn second(&self, t: Scalar) -> Scalar {
64 let (s, c) = t.sin_cos();
65 let (s2, c2) = (2.0 * t).sin_cos();
66 -self.cos * c - self.sin * s - 4.0 * self.cos2 * c2 - 4.0 * self.sin2 * s2
67 }
68
69 #[must_use]
71 pub fn is_finite(&self) -> bool {
72 self.constant.is_finite()
73 && self.cos.is_finite()
74 && self.sin.is_finite()
75 && self.cos2.is_finite()
76 && self.sin2.is_finite()
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum Branch {
83 Plus,
85 Minus,
87}
88
89impl Branch {
90 #[must_use]
92 pub fn sign(self) -> Scalar {
93 match self {
94 Self::Plus => 1.0,
95 Self::Minus => -1.0,
96 }
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq)]
113pub struct QuadraticGraph2 {
114 pub a: Trig2,
116 pub b: Trig2,
118 pub c: Trig2,
120 pub branch: Branch,
122}
123
124impl QuadraticGraph2 {
125 #[must_use]
127 pub fn discriminant(&self, t: Scalar) -> Scalar {
128 let b = self.b.value(t);
129 b * b - 4.0 * self.a.value(t) * self.c.value(t)
130 }
131
132 #[must_use]
134 pub fn height(&self, t: Scalar) -> Option<Scalar> {
135 let (a, b, c) = (self.a.value(t), self.b.value(t), self.c.value(t));
136 let d = b * b - 4.0 * a * c;
137 let size =
143 |t: &Trig2| t.constant.abs() + t.cos.abs() + t.sin.abs() + t.cos2.abs() + t.sin2.abs();
144 let scale = size(&self.b).powi(2) + 4.0 * size(&self.a) * size(&self.c);
145 if d < -1e-12 * scale {
146 return None;
147 }
148 let root = d.max(0.0).sqrt();
149 let denominator = -b - self.branch.sign() * root;
150 if denominator != 0.0 {
151 let v = 2.0 * c / denominator;
152 if v.is_finite() {
153 return Some(v);
154 }
155 }
156 if a != 0.0 {
159 let v = (-b + self.branch.sign() * root) / (2.0 * a);
160 return v.is_finite().then_some(v);
161 }
162 None
163 }
164
165 #[must_use]
169 pub fn slope(&self, t: Scalar) -> Option<Scalar> {
170 let v = self.height(t)?;
171 let f_t = self.a.derivative(t) * v * v + self.b.derivative(t) * v + self.c.derivative(t);
172 let f_v = 2.0 * self.a.value(t) * v + self.b.value(t);
173 let slope = -f_t / f_v;
174 slope.is_finite().then_some(slope)
175 }
176
177 #[must_use]
179 pub fn bend(&self, t: Scalar) -> Option<Scalar> {
180 let v = self.height(t)?;
181 let p = self.slope(t)?;
182 let f_v = 2.0 * self.a.value(t) * v + self.b.value(t);
183 let f_tt = self.a.second(t) * v * v + self.b.second(t) * v + self.c.second(t);
184 let f_tv = 2.0 * self.a.derivative(t) * v + self.b.derivative(t);
185 let f_vv = 2.0 * self.a.value(t);
186 let bend = -(f_tt + 2.0 * f_tv * p + f_vv * p * p) / f_v;
187 bend.is_finite().then_some(bend)
188 }
189
190 #[must_use]
192 pub fn is_finite(&self) -> bool {
193 self.a.is_finite() && self.b.is_finite() && self.c.is_finite()
194 }
195}
196
197#[derive(Debug, Clone, Copy, PartialEq)]
202pub struct RuledCarrier {
203 pub frame: Frame3,
205 pub x_radius: Scalar,
207 pub y_radius: Scalar,
209 pub slope: Scalar,
211}
212
213impl RuledCarrier {
214 #[must_use]
216 pub fn point(&self, u: Scalar, v: Scalar) -> Point3 {
217 let (s, c) = u.sin_cos();
218 self.frame.origin
219 + self.frame.x * ((self.x_radius + self.slope * v) * c)
220 + self.frame.y * ((self.y_radius + self.slope * v) * s)
221 + self.frame.z * v
222 }
223
224 #[must_use]
226 pub fn partials(&self, u: Scalar, v: Scalar) -> (Vec3, Vec3) {
227 let (s, c) = u.sin_cos();
228 (
229 self.frame.x * (-(self.x_radius + self.slope * v) * s)
230 + self.frame.y * ((self.y_radius + self.slope * v) * c),
231 self.frame.x * (self.slope * c) + self.frame.y * (self.slope * s) + self.frame.z,
232 )
233 }
234
235 #[must_use]
237 pub fn is_finite(&self) -> bool {
238 self.frame.origin.is_finite()
239 && self.frame.x.is_finite()
240 && self.frame.y.is_finite()
241 && self.frame.z.is_finite()
242 && self.x_radius.is_finite()
243 && self.y_radius.is_finite()
244 && self.slope.is_finite()
245 }
246}
247
248#[derive(Debug, Clone, Copy, PartialEq)]
251pub struct RuledSection3 {
252 pub carrier: RuledCarrier,
254 pub graph: QuadraticGraph2,
256}
257
258impl RuledSection3 {
259 #[must_use]
261 pub fn point(&self, t: Scalar) -> Option<Point3> {
262 Some(self.carrier.point(t, self.graph.height(t)?))
263 }
264
265 #[must_use]
267 pub fn tangent(&self, t: Scalar) -> Option<Vec3> {
268 let v = self.graph.height(t)?;
269 let (pu, pv) = self.carrier.partials(t, v);
270 Some(pu + pv * self.graph.slope(t)?)
271 }
272
273 #[must_use]
276 pub fn bend(&self, t: Scalar) -> Option<Vec3> {
277 let v = self.graph.height(t)?;
278 let slope = self.graph.slope(t)?;
279 let bend = self.graph.bend(t)?;
280 let (s, c) = t.sin_cos();
281 let k = &self.carrier;
282 let p_uu = k.frame.x * (-(k.x_radius + k.slope * v) * c)
283 + k.frame.y * (-(k.y_radius + k.slope * v) * s);
284 let p_uv = k.frame.x * (-k.slope * s) + k.frame.y * (k.slope * c);
285 let (_, p_v) = k.partials(t, v);
286 Some(p_uu + p_uv * (2.0 * slope) + p_v * bend)
287 }
288}