1use axiolid_core::{Point2, Point3, Scalar, Vec3};
14
15use crate::implicit::{Carrier, SurfaceJet};
16
17#[derive(Debug, Clone, Copy, PartialEq)]
19#[cfg_attr(
20 feature = "serde",
21 derive(serde::Serialize, serde::Deserialize),
22 serde(deny_unknown_fields)
23)]
24pub struct PairNode {
25 pub point: Point3,
27 pub first: Point2,
29 pub second: Point2,
31}
32
33#[derive(Debug, Clone, PartialEq)]
36#[cfg_attr(
37 feature = "serde",
38 derive(serde::Serialize, serde::Deserialize),
39 serde(deny_unknown_fields)
40)]
41pub struct PairSection3 {
42 pub first: Carrier,
44 pub second: Carrier,
46 pub nodes: Vec<PairNode>,
48}
49
50#[must_use]
53#[allow(clippy::needless_range_loop)]
54pub fn solve4(mut m: [[Scalar; 4]; 4], mut r: [Scalar; 4]) -> Option<[Scalar; 4]> {
55 for col in 0..4 {
56 let pivot = (col..4).max_by(|&a, &b| m[a][col].abs().total_cmp(&m[b][col].abs()))?;
57 if m[pivot][col] == 0.0 || !m[pivot][col].is_finite() {
58 return None;
59 }
60 m.swap(col, pivot);
61 r.swap(col, pivot);
62 for row in col + 1..4 {
63 let f = m[row][col] / m[col][col];
64 for k in col..4 {
65 m[row][k] -= f * m[col][k];
66 }
67 r[row] -= f * r[col];
68 }
69 }
70 let mut x = [0.0; 4];
71 for row in (0..4).rev() {
72 let mut acc = r[row];
73 for k in row + 1..4 {
74 acc -= m[row][k] * x[k];
75 }
76 x[row] = acc / m[row][row];
77 }
78 x.iter().all(|v| v.is_finite()).then_some(x)
79}
80
81fn matrix(j1: &SurfaceJet, j2: &SurfaceJet, d: Vec3) -> [[Scalar; 4]; 4] {
83 [
84 [j1.u.x, j1.v.x, -j2.u.x, -j2.v.x],
85 [j1.u.y, j1.v.y, -j2.u.y, -j2.v.y],
86 [j1.u.z, j1.v.z, -j2.u.z, -j2.v.z],
87 [d.dot(j1.u), d.dot(j1.v), 0.0, 0.0],
88 ]
89}
90
91impl PairSection3 {
92 #[must_use]
94 pub fn end(&self) -> Scalar {
95 self.nodes.len().saturating_sub(1) as Scalar
96 }
97
98 fn locate(&self, t: Scalar) -> Option<(usize, Scalar)> {
99 let n = self.nodes.len();
100 if n < 2 || !t.is_finite() {
101 return None;
102 }
103 let slack = 1e-12 * (1.0 + self.end());
104 if t < -slack || t > self.end() + slack {
105 return None;
106 }
107 let i = (t.floor().max(0.0) as usize).min(n - 2);
108 Some((i, (t - i as Scalar).clamp(0.0, 1.0)))
109 }
110
111 #[must_use]
113 pub fn solve(&self, t: Scalar) -> Option<(Point2, Point2, Point3)> {
114 let (i, s) = self.locate(t)?;
115 let (n0, n1) = (self.nodes[i], self.nodes[i + 1]);
116 if s == 0.0 {
117 return Some((n0.first, n0.second, n0.point));
118 }
119 if s == 1.0 {
120 return Some((n1.first, n1.second, n1.point));
121 }
122 let d = n1.point - n0.point;
123 let target = n0.point + d * s;
124 let mut a = n0.first + (n1.first - n0.first) * s;
125 let mut b = n0.second + (n1.second - n0.second) * s;
126 let scale = 1.0 + n0.point.length() + d.length();
127 for _ in 0..50 {
128 let j1 = self.first.jet(a.x, a.y);
129 let j2 = self.second.jet(b.x, b.y);
130 let gap = j1.point - j2.point;
131 let plane = d.dot(j1.point - target);
132 let x = solve4(matrix(&j1, &j2, d), [-gap.x, -gap.y, -gap.z, -plane])?;
133 a += Point2::new(x[0], x[1]);
134 b += Point2::new(x[2], x[3]);
135 let step = x.iter().map(|v| v.abs()).fold(0.0, Scalar::max);
136 if step <= 4.0 * Scalar::EPSILON * (1.0 + a.length() + b.length()) {
137 break;
138 }
139 }
140 let (j1, j2) = (self.first.jet(a.x, a.y), self.second.jet(b.x, b.y));
141 ((j1.point - j2.point).length() <= 1e-9 * scale).then_some((a, b, j1.point))
142 }
143
144 #[must_use]
146 pub fn point(&self, t: Scalar) -> Option<Point3> {
147 self.solve(t).map(|(_, _, p)| p)
148 }
149
150 #[must_use]
153 pub fn rates(&self, t: Scalar) -> Option<(Point2, Point2, Vec3)> {
154 let (i, _) = self.locate(t)?;
155 let d = self.nodes[i + 1].point - self.nodes[i].point;
156 let (a, b, _) = self.solve(t)?;
157 let (j1, j2) = (self.first.jet(a.x, a.y), self.second.jet(b.x, b.y));
158 let x = solve4(matrix(&j1, &j2, d), [0.0, 0.0, 0.0, d.dot(d)])?;
159 let (da, db) = (Point2::new(x[0], x[1]), Point2::new(x[2], x[3]));
160 Some((da, db, j1.u * da.x + j1.v * da.y))
161 }
162
163 #[must_use]
165 pub fn tangent(&self, t: Scalar) -> Option<Vec3> {
166 self.rates(t).map(|(_, _, d)| d)
167 }
168
169 #[must_use]
172 pub fn second_rates(&self, t: Scalar) -> Option<(Point2, Point2, Vec3)> {
173 let (i, s) = self.locate(t)?;
174 let h = 1e-5;
175 let (lo, hi) = ((s - h).max(0.0), (s + h).min(1.0));
176 let base = i as Scalar;
177 let (p, q) = (self.rates(base + lo)?, self.rates(base + hi)?);
178 let w = hi - lo;
179 Some(((q.0 - p.0) / w, (q.1 - p.1) / w, (q.2 - p.2) / w))
180 }
181
182 #[must_use]
184 pub fn bend(&self, t: Scalar) -> Option<Vec3> {
185 self.second_rates(t).map(|(_, _, d)| d)
186 }
187
188 #[must_use]
191 pub fn side(&self, carrier: &Carrier) -> Option<bool> {
192 if *carrier == self.first {
193 Some(true)
194 } else if *carrier == self.second {
195 Some(false)
196 } else {
197 None
198 }
199 }
200
201 #[must_use]
205 pub fn sub(&self, t0: Scalar, t1: Scalar) -> Option<Self> {
206 let (a0, b0, p0) = self.solve(t0)?;
207 let (a1, b1, p1) = self.solve(t1)?;
208 let mut nodes = vec![PairNode {
209 point: p0,
210 first: a0,
211 second: b0,
212 }];
213 let first = t0.floor() as usize + 1;
214 let last = t1.ceil() as usize;
215 for i in first..last.min(self.nodes.len()) {
216 let t = i as Scalar;
217 if t > t0 + 1e-9 && t < t1 - 1e-9 {
218 nodes.push(self.nodes[i]);
219 }
220 }
221 nodes.push(PairNode {
222 point: p1,
223 first: a1,
224 second: b1,
225 });
226 Some(Self {
227 first: self.first.clone(),
228 second: self.second.clone(),
229 nodes,
230 })
231 }
232
233 #[must_use]
235 pub fn reversed(&self) -> Self {
236 let mut nodes = self.nodes.clone();
237 nodes.reverse();
238 Self {
239 first: self.first.clone(),
240 second: self.second.clone(),
241 nodes,
242 }
243 }
244
245 #[must_use]
249 pub fn parameter_of(&self, p: Point3) -> Option<Scalar> {
250 let mut best: Option<(Scalar, usize, Scalar)> = None;
251 for (i, w) in self.nodes.windows(2).enumerate() {
252 let d = w[1].point - w[0].point;
253 let l2 = d.length_squared();
254 if l2 == 0.0 {
255 continue;
256 }
257 let s = ((p - w[0].point).dot(d) / l2).clamp(0.0, 1.0);
258 let miss = (w[0].point + d * s - p).length();
259 if best.is_none_or(|(m, _, _)| miss < m) {
260 best = Some((miss, i, s));
261 }
262 }
263 let (_, i, mut s) = best?;
264 let base = i as Scalar;
265 for _ in 0..30 {
266 let t = base + s;
267 let (q, d) = (self.point(t)?, self.tangent(t)?);
268 let l2 = d.length_squared();
269 if l2 == 0.0 {
270 break;
271 }
272 let next = (s - (q - p).dot(d) / l2).clamp(0.0, 1.0);
273 let step = (next - s).abs();
274 s = next;
275 if step <= 4.0 * Scalar::EPSILON * (1.0 + base) {
276 break;
277 }
278 }
279 Some(base + s)
280 }
281
282 #[must_use]
284 pub fn is_finite(&self) -> bool {
285 self.first.is_finite()
286 && self.second.is_finite()
287 && self
288 .nodes
289 .iter()
290 .all(|n| n.point.is_finite() && n.first.is_finite() && n.second.is_finite())
291 }
292}