1use axiolid_core::{Point3, Scalar, Vec3};
10
11use crate::implicit::SurfaceJet;
12use crate::spline::KnotSpec;
13
14#[derive(Debug, Clone, PartialEq)]
16pub struct BSplineSurface {
17 pub u_degree: u16,
19 pub v_degree: u16,
21 pub control_points: Vec<Vec<Point3>>,
23 pub u_knots: Vec<Scalar>,
25 pub u_multiplicities: Vec<u32>,
27 pub v_knots: Vec<Scalar>,
29 pub v_multiplicities: Vec<u32>,
31 pub weights: Option<Vec<Vec<Scalar>>>,
33 pub u_closed: bool,
35 pub v_closed: bool,
37 pub knot_spec: KnotSpec,
39 pub self_intersect: Option<bool>,
41}
42
43fn expand(knots: &[Scalar], multiplicities: &[u32]) -> Vec<Scalar> {
45 let mut out = Vec::new();
46 for (&k, &m) in knots.iter().zip(multiplicities) {
47 out.extend(core::iter::repeat_n(k, m as usize));
48 }
49 out
50}
51
52fn span(knots: &[Scalar], degree: usize, count: usize, t: Scalar) -> usize {
55 let mut s = degree;
56 for (k, knot) in knots.iter().enumerate().take(count).skip(degree) {
57 if *knot <= t {
58 s = k;
59 } else {
60 break;
61 }
62 }
63 s
64}
65
66#[allow(clippy::needless_range_loop)] fn basis(knots: &[Scalar], p: usize, s: usize, t: Scalar) -> [Vec<Scalar>; 3] {
71 let mut ndu = vec![vec![0.0; p + 1]; p + 1];
72 let mut left = vec![0.0; p + 1];
73 let mut right = vec![0.0; p + 1];
74 ndu[0][0] = 1.0;
75 for j in 1..=p {
76 left[j] = t - knots[s + 1 - j];
77 right[j] = knots[s + j] - t;
78 let mut saved = 0.0;
79 for r in 0..j {
80 ndu[j][r] = right[r + 1] + left[j - r];
81 let temp = if ndu[j][r] == 0.0 {
82 0.0
83 } else {
84 ndu[r][j - 1] / ndu[j][r]
85 };
86 ndu[r][j] = saved + right[r + 1] * temp;
87 saved = left[j - r] * temp;
88 }
89 ndu[j][j] = saved;
90 }
91 let orders = 2.min(p);
92 let mut ders = [vec![0.0; p + 1], vec![0.0; p + 1], vec![0.0; p + 1]];
93 for j in 0..=p {
94 ders[0][j] = ndu[j][p];
95 }
96 let mut a = [vec![0.0; p + 1], vec![0.0; p + 1]];
97 for r in 0..=p {
98 let (mut s1, mut s2) = (0usize, 1usize);
99 a[0][0] = 1.0;
100 for k in 1..=orders {
101 let mut d = 0.0;
102 let rk = r as isize - k as isize;
103 let pk = p as isize - k as isize;
104 if r >= k {
105 let denom = ndu[(pk + 1) as usize][rk as usize];
106 a[s2][0] = if denom == 0.0 { 0.0 } else { a[s1][0] / denom };
107 d = a[s2][0] * ndu[rk as usize][pk as usize];
108 }
109 let j1 = if rk >= -1 { 1 } else { (-rk) as usize };
110 let j2 = if (r as isize - 1) <= pk { k - 1 } else { p - r };
111 for j in j1..=j2 {
112 let denom = ndu[(pk + 1) as usize][(rk + j as isize) as usize];
113 a[s2][j] = if denom == 0.0 {
114 0.0
115 } else {
116 (a[s1][j] - a[s1][j - 1]) / denom
117 };
118 d += a[s2][j] * ndu[(rk + j as isize) as usize][pk as usize];
119 }
120 if r as isize <= pk {
121 let denom = ndu[(pk + 1) as usize][r];
122 a[s2][k] = if denom == 0.0 {
123 0.0
124 } else {
125 -a[s1][k - 1] / denom
126 };
127 d += a[s2][k] * ndu[r][pk as usize];
128 }
129 ders[k][r] = d;
130 core::mem::swap(&mut s1, &mut s2);
131 }
132 }
133 let mut factor = p as Scalar;
134 for k in 1..=orders {
135 for value in &mut ders[k] {
136 *value *= factor;
137 }
138 factor *= (p - k) as Scalar;
139 }
140 ders
141}
142
143impl BSplineSurface {
144 #[must_use]
147 pub fn domain(&self) -> Option<((Scalar, Scalar), (Scalar, Scalar))> {
148 let (p, q) = (usize::from(self.u_degree), usize::from(self.v_degree));
149 let rows = self.control_points.len();
150 let cols = self.control_points.first()?.len();
151 let (ku, kv) = (
152 expand(&self.u_knots, &self.u_multiplicities),
153 expand(&self.v_knots, &self.v_multiplicities),
154 );
155 if p == 0 || q == 0 || ku.len() != rows + p + 1 || kv.len() != cols + q + 1 {
156 return None;
157 }
158 Some(((ku[p], ku[rows]), (kv[q], kv[cols])))
159 }
160
161 #[must_use]
164 #[allow(clippy::needless_range_loop)]
165 pub fn jet(&self, u: Scalar, v: Scalar) -> Option<SurfaceJet> {
166 let (p, q) = (usize::from(self.u_degree), usize::from(self.v_degree));
167 let ((u0, u1), (v0, v1)) = self.domain()?;
168 let (rows, cols) = (self.control_points.len(), self.control_points[0].len());
169 let (ku, kv) = (
170 expand(&self.u_knots, &self.u_multiplicities),
171 expand(&self.v_knots, &self.v_multiplicities),
172 );
173 let (u, v) = (u.clamp(u0, u1), v.clamp(v0, v1));
174 let (su, sv) = (span(&ku, p, rows, u), span(&kv, q, cols, v));
175 let bu = basis(&ku, p, su, u);
176 let bv = basis(&kv, q, sv, v);
177 let mut a = [[Vec3::ZERO; 3]; 3];
179 let mut w = [[0.0; 3]; 3];
180 for i in 0..=p {
181 for j in 0..=q {
182 let (row, col) = (su - p + i, sv - q + j);
183 let weight = self.weights.as_ref().map_or(1.0, |net| net[row][col]);
184 let point = self.control_points[row][col] * weight;
185 for k in 0..3 {
186 for l in 0..3 - k {
187 let n = bu[k][i] * bv[l][j];
188 a[k][l] += point * n;
189 w[k][l] += weight * n;
190 }
191 }
192 }
193 }
194 if w[0][0] == 0.0 || !w[0][0].is_finite() {
195 return None;
196 }
197 let inv = 1.0 / w[0][0];
198 let s = a[0][0] * inv;
199 let s_u = (a[1][0] - s * w[1][0]) * inv;
200 let s_v = (a[0][1] - s * w[0][1]) * inv;
201 let s_uu = (a[2][0] - s_u * (2.0 * w[1][0]) - s * w[2][0]) * inv;
202 let s_vv = (a[0][2] - s_v * (2.0 * w[0][1]) - s * w[0][2]) * inv;
203 let s_uv = (a[1][1] - s_u * w[0][1] - s_v * w[1][0] - s * w[1][1]) * inv;
204 let jet = SurfaceJet {
205 point: s,
206 u: s_u,
207 v: s_v,
208 uu: s_uu,
209 uv: s_uv,
210 vv: s_vv,
211 };
212 (jet.point.is_finite() && jet.u.is_finite() && jet.v.is_finite()).then_some(jet)
213 }
214}