1use geop_core_math::{geop_error::GeopResult, scalars::Scalar, vector::Vector};
2
3use super::NurbSurface;
4use crate::{aabb::compute_aabb, knot_insertion};
5
6#[derive(Clone, Copy)]
8enum Dir {
9 U,
10 V,
11}
12
13impl<S: Scalar, const D: usize> NurbSurface<S, D> {
14 fn degree_in(&self, dir: Dir) -> usize {
15 match dir {
16 Dir::U => self.degree_u,
17 Dir::V => self.degree_v,
18 }
19 }
20
21 fn knots_in(&self, dir: Dir) -> &Vec<S> {
22 match dir {
23 Dir::U => &self.knot_vector_u,
24 Dir::V => &self.knot_vector_v,
25 }
26 }
27
28 fn rows_along(&self, dir: Dir) -> Vec<Vec<Vector<S, D>>> {
32 let (nu, nv) = (self.num_u, self.num_v);
33 match dir {
34 Dir::U => (0..nv)
35 .map(|j| (0..nu).map(|i| self.control_points[i * nv + j]).collect())
36 .collect(),
37 Dir::V => self.control_points.chunks(nv).map(<[_]>::to_vec).collect(),
38 }
39 }
40
41 fn with_rows(&self, dir: Dir, knots: Vec<S>, rows: Vec<Vec<Vector<S, D>>>) -> Self {
44 let along = rows[0].len();
45 let (num_u, num_v, control_points): (usize, usize, Vec<Vector<S, D>>) = match dir {
46 Dir::U => (
47 along,
48 rows.len(),
49 (0..along)
50 .flat_map(|i| rows.iter().map(move |col| col[i]))
51 .collect(),
52 ),
53 Dir::V => (rows.len(), along, rows.into_iter().flatten().collect()),
54 };
55 let (knot_vector_u, knot_vector_v) = match dir {
56 Dir::U => (knots, self.knot_vector_v.clone()),
57 Dir::V => (self.knot_vector_u.clone(), knots),
58 };
59 NurbSurface {
60 degree_u: self.degree_u,
61 degree_v: self.degree_v,
62 num_u,
63 num_v,
64 aabb: compute_aabb(&control_points),
65 control_points,
66 knot_vector_u,
67 knot_vector_v,
68 }
69 }
70
71 fn split_in(&self, dir: Dir, t: S) -> GeopResult<(Self, Self)> {
72 let mut knots = self.knots_in(dir).clone();
73 let mut rows = self.rows_along(dir);
74 let (right_knots, right_rows) =
75 knot_insertion::split(&mut knots, &mut rows, self.degree_in(dir), t)?;
76 Ok((
77 self.with_rows(dir, knots, rows),
78 self.with_rows(dir, right_knots, right_rows),
79 ))
80 }
81
82 fn restrict_in(&self, dir: Dir, t0: S, t1: S) -> GeopResult<Self> {
83 let mut knots = self.knots_in(dir).clone();
84 let mut rows = self.rows_along(dir);
85 knot_insertion::restrict(&mut knots, &mut rows, self.degree_in(dir), t0, t1)?;
86 Ok(self.with_rows(dir, knots, rows))
87 }
88
89 pub fn split_u(&self, t: S) -> GeopResult<(NurbSurface<S, D>, NurbSurface<S, D>)> {
102 self.split_in(Dir::U, t)
103 }
104
105 pub fn split_v(&self, t: S) -> GeopResult<(NurbSurface<S, D>, NurbSurface<S, D>)> {
110 self.split_in(Dir::V, t)
111 }
112
113 fn split_in_mid(&self, dir: Dir) -> GeopResult<(Self, Self)> {
114 let knots = self.knots_in(dir);
115 let n = match dir {
116 Dir::U => self.num_u,
117 Dir::V => self.num_v,
118 };
119 let (t0, t1) = (knots[self.degree_in(dir)], knots[n]);
120 let mid = t0.add(t1).div(S::TWO)?.sharpen();
124 self.split_in(dir, mid)
125 }
126
127 pub fn split_u_mid(&self) -> GeopResult<(NurbSurface<S, D>, NurbSurface<S, D>)> {
129 self.split_in_mid(Dir::U)
130 }
131
132 pub fn split_v_mid(&self) -> GeopResult<(NurbSurface<S, D>, NurbSurface<S, D>)> {
134 self.split_in_mid(Dir::V)
135 }
136
137 pub fn sub_surface(&self, (u0, u1): (S, S), (v0, v1): (S, S)) -> GeopResult<Self> {
142 self.restrict_in(Dir::U, u0, u1)?
143 .restrict_in(Dir::V, v0, v1)
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::super::NurbSurface;
150 use geop_core_math::for_all_scalars;
151 use geop_core_math::{scalars::Scalar, vector::Vector4};
152
153 fn pt<S: Scalar>(x: f64, y: f64, z: f64, w: f64) -> Vector4<S> {
154 Vector4::from_array([
155 S::from_f64(x),
156 S::from_f64(y),
157 S::from_f64(z),
158 S::from_f64(w),
159 ])
160 }
161
162 fn bilinear<S: Scalar>() -> NurbSurface<S, 4> {
163 let f = S::from_f64;
164 NurbSurface::try_new(
165 1,
166 1,
167 vec![
168 pt(0., 0., 0., 1.),
169 pt(0., 1., 0., 1.),
170 pt(1., 0., 0., 1.),
171 pt(1., 1., 0., 1.),
172 ],
173 vec![f(0.), f(0.), f(1.), f(1.)],
174 vec![f(0.), f(0.), f(1.), f(1.)],
175 )
176 .unwrap()
177 }
178
179 fn check_split_u_junction_matches<S: Scalar>() {
180 let orig = bilinear::<S>();
181 let (left, right) = orig.split_u(S::from_f64(0.5)).unwrap();
182 for v_val in [0.0_f64, 0.5, 1.0] {
183 let v = S::from_f64(v_val);
184 let o = orig.evaluate(S::from_f64(0.5), v).unwrap();
185 let l = left.evaluate(S::from_f64(0.5), v).unwrap();
186 let r = right.evaluate(S::from_f64(0.5), v).unwrap();
187 for c in 0..3 {
188 assert!(
189 o[c].could_be_equal(l[c]),
190 "split_u left mismatch v={v_val} c={c}"
191 );
192 assert!(
193 o[c].could_be_equal(r[c]),
194 "split_u right mismatch v={v_val} c={c}"
195 );
196 }
197 }
198 }
199 #[test]
200 fn split_u_junction_matches() {
201 for_all_scalars!(check_split_u_junction_matches);
202 }
203
204 fn check_split_u_left_start_matches<S: Scalar>() {
205 let orig = bilinear::<S>();
206 let (left, _) = orig.split_u(S::from_f64(0.5)).unwrap();
207 for v_val in [0.0_f64, 0.5, 1.0] {
208 let v = S::from_f64(v_val);
209 let o = orig.evaluate(S::ZERO, v).unwrap();
210 let l = left.evaluate(S::ZERO, v).unwrap();
211 for c in 0..3 {
212 assert!(o[c].could_be_equal(l[c]), "c={c} v={v_val}");
213 }
214 }
215 }
216 #[test]
217 fn split_u_left_start_matches() {
218 for_all_scalars!(check_split_u_left_start_matches);
219 }
220
221 fn check_split_u_right_end_matches<S: Scalar>() {
222 let orig = bilinear::<S>();
223 let (_, right) = orig.split_u(S::from_f64(0.5)).unwrap();
224 for v_val in [0.0_f64, 0.5, 1.0] {
225 let v = S::from_f64(v_val);
226 let o = orig.evaluate(S::ONE, v).unwrap();
227 let r = right.evaluate(S::ONE, v).unwrap();
228 for c in 0..3 {
229 assert!(o[c].could_be_equal(r[c]), "c={c} v={v_val}");
230 }
231 }
232 }
233 #[test]
234 fn split_u_right_end_matches() {
235 for_all_scalars!(check_split_u_right_end_matches);
236 }
237
238 fn check_split_u_at_boundary_returns_err<S: Scalar>() {
239 let s = bilinear::<S>();
240 assert!(s.split_u(S::ZERO).is_err());
241 assert!(s.split_u(S::ONE).is_err());
242 }
243 #[test]
244 fn split_u_at_boundary_returns_err() {
245 for_all_scalars!(check_split_u_at_boundary_returns_err);
246 }
247
248 fn check_split_v_junction_matches<S: Scalar>() {
249 let orig = bilinear::<S>();
250 let (left, right) = orig.split_v(S::from_f64(0.5)).unwrap();
251 for u_val in [0.0_f64, 0.5, 1.0] {
252 let u = S::from_f64(u_val);
253 let o = orig.evaluate(u, S::from_f64(0.5)).unwrap();
254 let l = left.evaluate(u, S::from_f64(0.5)).unwrap();
255 let r = right.evaluate(u, S::from_f64(0.5)).unwrap();
256 for c in 0..3 {
257 assert!(
258 o[c].could_be_equal(l[c]),
259 "split_v left mismatch u={u_val} c={c}"
260 );
261 assert!(
262 o[c].could_be_equal(r[c]),
263 "split_v right mismatch u={u_val} c={c}"
264 );
265 }
266 }
267 }
268 #[test]
269 fn split_v_junction_matches() {
270 for_all_scalars!(check_split_v_junction_matches);
271 }
272
273 fn check_split_v_left_start_matches<S: Scalar>() {
274 let orig = bilinear::<S>();
275 let (left, _) = orig.split_v(S::from_f64(0.5)).unwrap();
276 for u_val in [0.0_f64, 0.5, 1.0] {
277 let u = S::from_f64(u_val);
278 let o = orig.evaluate(u, S::ZERO).unwrap();
279 let l = left.evaluate(u, S::ZERO).unwrap();
280 for c in 0..3 {
281 assert!(o[c].could_be_equal(l[c]), "c={c} u={u_val}");
282 }
283 }
284 }
285 #[test]
286 fn split_v_left_start_matches() {
287 for_all_scalars!(check_split_v_left_start_matches);
288 }
289
290 fn check_split_v_right_end_matches<S: Scalar>() {
291 let orig = bilinear::<S>();
292 let (_, right) = orig.split_v(S::from_f64(0.5)).unwrap();
293 for u_val in [0.0_f64, 0.5, 1.0] {
294 let u = S::from_f64(u_val);
295 let o = orig.evaluate(u, S::ONE).unwrap();
296 let r = right.evaluate(u, S::ONE).unwrap();
297 for c in 0..3 {
298 assert!(o[c].could_be_equal(r[c]), "c={c} u={u_val}");
299 }
300 }
301 }
302 #[test]
303 fn split_v_right_end_matches() {
304 for_all_scalars!(check_split_v_right_end_matches);
305 }
306
307 fn check_split_v_at_boundary_returns_err<S: Scalar>() {
308 let s = bilinear::<S>();
309 assert!(s.split_v(S::ZERO).is_err());
310 assert!(s.split_v(S::ONE).is_err());
311 }
312 #[test]
313 fn split_v_at_boundary_returns_err() {
314 for_all_scalars!(check_split_v_at_boundary_returns_err);
315 }
316
317 fn check_split_u_domains<S: Scalar>() {
318 let orig = bilinear::<S>();
319 let (left, right) = orig.split_u(S::from_f64(0.5)).unwrap();
320 let (lu_min, lu_max) = left.domain_u();
321 let (ru_min, ru_max) = right.domain_u();
322 assert!(lu_min.could_be_equal(S::ZERO));
323 assert!(lu_max.could_be_equal(S::from_f64(0.5)));
324 assert!(ru_min.could_be_equal(S::from_f64(0.5)));
325 assert!(ru_max.could_be_equal(S::ONE));
326 }
327 #[test]
328 fn split_u_domains() {
329 for_all_scalars!(check_split_u_domains);
330 }
331
332 fn check_split_v_domains<S: Scalar>() {
333 let orig = bilinear::<S>();
334 let (left, right) = orig.split_v(S::from_f64(0.5)).unwrap();
335 let (lv_min, lv_max) = left.domain_v();
336 let (rv_min, rv_max) = right.domain_v();
337 assert!(lv_min.could_be_equal(S::ZERO));
338 assert!(lv_max.could_be_equal(S::from_f64(0.5)));
339 assert!(rv_min.could_be_equal(S::from_f64(0.5)));
340 assert!(rv_max.could_be_equal(S::ONE));
341 }
342 #[test]
343 fn split_v_domains() {
344 for_all_scalars!(check_split_v_domains);
345 }
346}