geop_core_geometry/contains/
surface_bisect.rs1use std::collections::VecDeque;
6
7use crate::{aabb::aabb_could_contain, nurb_surface::NurbSurface};
8use geop_core_math::{geop_error::GeopResult, scalars::Scalar, vector::Vector3};
9
10fn union_uv<S: Scalar>(solution: Option<(S, S)>, uv: (S, S)) -> (S, S) {
15 match solution {
16 Some((eu, ev)) => (eu.union(uv.0), ev.union(uv.1)),
17 None => uv,
18 }
19}
20
21pub fn surface_could_contain<S: Scalar>(
41 surface: &NurbSurface<S, 4>,
42 point: &Vector3<S>,
43 max_nodes: usize,
44 min_subdivision_size: S,
45) -> GeopResult<Option<(S, S)>> {
46 let mut queue: VecDeque<NurbSurface<S, 4>> = VecDeque::new();
47 queue.push_back(surface.clone());
48
49 let mut explored = 0usize;
50 let mut solution: Option<(S, S)> = None;
51
52 while let Some(patch) = queue.pop_front() {
53 if explored >= max_nodes {
54 break;
55 }
56 explored += 1;
57
58 let patch_uv = || -> (S, S) {
65 let (u_min, u_max) = patch.domain_u();
66 let (v_min, v_max) = patch.domain_v();
67 (u_min.union(u_max), v_min.union(v_max))
68 };
69
70 if !aabb_could_contain(&patch.aabb, point) {
75 continue;
76 }
77
78 let hull = match patch.convex_hull() {
79 Ok(hull) => hull,
80 Err(_) => {
81 solution = Some(union_uv(solution, patch_uv()));
85 continue;
86 }
87 };
88
89 if !hull.could_contain(point) {
90 continue;
91 }
92
93 let nu = patch.num_u();
95 let nv = patch.num_v();
96 let p00 = hull.points[0];
97 let pn0 = hull.points[(nu - 1) * nv];
98 let p0m = hull.points[nv - 1];
99 let u_size = pn0.sub(&p00).norm();
100 let v_size = p0m.sub(&p00).norm();
101 let max_size = if v_size.definitely_greater(u_size) {
102 v_size
103 } else {
104 u_size
105 };
106 if !max_size.definitely_greater(min_subdivision_size) {
107 solution = Some(union_uv(solution, patch_uv()));
108 continue;
109 }
110
111 let (left, right) = if v_size.definitely_greater(u_size) {
113 let (v_min, v_max) = patch.domain_v();
114 let mid_v = v_min.add(v_max).div(S::TWO)?.sharpen();
118 match patch.split_v(mid_v) {
119 Ok(halves) => halves,
120 Err(_) => {
121 solution = Some(union_uv(solution, patch_uv()));
122 continue;
123 }
124 }
125 } else {
126 let (u_min, u_max) = patch.domain_u();
127 let mid_u = u_min.add(u_max).div(S::TWO)?.sharpen();
128 match patch.split_u(mid_u) {
129 Ok(halves) => halves,
130 Err(_) => {
131 solution = Some(union_uv(solution, patch_uv()));
132 continue;
133 }
134 }
135 };
136
137 queue.push_back(left);
138 queue.push_back(right);
139 }
140
141 Ok(solution)
142}
143
144pub fn surface_definitely_not_contains<S: Scalar>(
146 surface: &NurbSurface<S, 4>,
147 point: &Vector3<S>,
148 max_nodes: usize,
149 epsilon: S,
150) -> GeopResult<bool> {
151 Ok(surface_could_contain(surface, point, max_nodes, epsilon)?.is_none())
152}
153
154#[cfg(test)]
155mod tests {
156 use super::{surface_could_contain, surface_definitely_not_contains};
157 use crate::nurb_surface::NurbSurface;
158 use geop_core_math::for_all_scalars;
159 use geop_core_math::{
160 scalars::Scalar,
161 vector::{Vector3, Vector4},
162 };
163
164 fn pt<S: Scalar>(x: f64, y: f64, z: f64, w: f64) -> Vector4<S> {
165 Vector4::from_array([
166 S::from_f64(x),
167 S::from_f64(y),
168 S::from_f64(z),
169 S::from_f64(w),
170 ])
171 }
172
173 fn v3<S: Scalar>(x: f64, y: f64, z: f64) -> Vector3<S> {
174 Vector3::from_array([S::from_f64(x), S::from_f64(y), S::from_f64(z)])
175 }
176
177 const MAX: usize = 2000;
178 const EPS: f64 = 1e-3;
179
180 fn flat_patch<S: Scalar>() -> NurbSurface<S, 4> {
181 let f = S::from_f64;
182 NurbSurface::try_new(
183 1,
184 1,
185 vec![
186 pt(0., 0., 0., 1.),
187 pt(0., 1., 0., 1.),
188 pt(1., 0., 0., 1.),
189 pt(1., 1., 0., 1.),
190 ],
191 vec![f(0.), f(0.), f(1.), f(1.)],
192 vec![f(0.), f(0.), f(1.), f(1.)],
193 )
194 .unwrap()
195 }
196
197 fn lifted_patch<S: Scalar>() -> NurbSurface<S, 4> {
199 let f = S::from_f64;
200 NurbSurface::try_new(
201 1,
202 1,
203 vec![
204 pt(0., 0., 0., 1.),
205 pt(0., 1., 0., 1.),
206 pt(1., 0., 0., 1.),
207 pt(1., 1., 1., 1.),
208 ],
209 vec![f(0.), f(0.), f(1.), f(1.)],
210 vec![f(0.), f(0.), f(1.), f(1.)],
211 )
212 .unwrap()
213 }
214
215 fn check_flat_surface_contains_corner<S: Scalar>() {
218 let s = flat_patch::<S>();
219 let p = s.evaluate(S::ZERO, S::ZERO).unwrap();
220 assert!(
221 surface_could_contain(&s, &p, MAX, S::from_f64(EPS))
222 .unwrap()
223 .is_some()
224 );
225 }
226 #[test]
227 fn flat_surface_contains_corner() {
228 for_all_scalars!(check_flat_surface_contains_corner);
229 }
230
231 fn check_flat_surface_contains_center<S: Scalar>() {
232 let s = flat_patch::<S>();
233 let p = s.evaluate(S::from_f64(0.5), S::from_f64(0.5)).unwrap();
234 assert!(
235 surface_could_contain(&s, &p, MAX, S::from_f64(EPS))
236 .unwrap()
237 .is_some()
238 );
239 }
240 #[test]
241 fn flat_surface_contains_center() {
242 for_all_scalars!(check_flat_surface_contains_center);
243 }
244
245 fn check_flat_surface_contains_midedge<S: Scalar>() {
246 let s = flat_patch::<S>();
247 let p = s.evaluate(S::from_f64(0.5), S::ZERO).unwrap();
248 assert!(
249 surface_could_contain(&s, &p, MAX, S::from_f64(EPS))
250 .unwrap()
251 .is_some()
252 );
253 }
254 #[test]
255 fn flat_surface_contains_midedge() {
256 for_all_scalars!(check_flat_surface_contains_midedge);
257 }
258
259 fn check_lifted_surface_contains_corner<S: Scalar>() {
260 let s = lifted_patch::<S>();
261 let p = s.evaluate(S::ZERO, S::ZERO).unwrap();
262 assert!(
263 surface_could_contain(&s, &p, MAX, S::from_f64(EPS))
264 .unwrap()
265 .is_some()
266 );
267 }
268 #[test]
269 fn lifted_surface_contains_corner() {
270 for_all_scalars!(check_lifted_surface_contains_corner);
271 }
272
273 fn check_lifted_surface_contains_midpoint<S: Scalar>() {
274 let s = lifted_patch::<S>();
275 let p = s.evaluate(S::from_f64(0.5), S::from_f64(0.5)).unwrap();
276 assert!(
277 surface_could_contain(&s, &p, MAX, S::from_f64(EPS))
278 .unwrap()
279 .is_some()
280 );
281 }
282 #[test]
283 fn lifted_surface_contains_midpoint() {
284 for_all_scalars!(check_lifted_surface_contains_midpoint);
285 }
286
287 fn check_flat_surface_excludes_above<S: Scalar>() {
290 let s = flat_patch::<S>();
291 assert!(
292 surface_definitely_not_contains(&s, &v3(0.5, 0.5, 5.), MAX, S::from_f64(EPS)).unwrap()
293 );
294 }
295 #[test]
296 fn flat_surface_excludes_above() {
297 for_all_scalars!(check_flat_surface_excludes_above);
298 }
299
300 fn check_flat_surface_excludes_below<S: Scalar>() {
301 let s = flat_patch::<S>();
302 assert!(
303 surface_definitely_not_contains(&s, &v3(0.5, 0.5, -5.), MAX, S::from_f64(EPS)).unwrap()
304 );
305 }
306 #[test]
307 fn flat_surface_excludes_below() {
308 for_all_scalars!(check_flat_surface_excludes_below);
309 }
310
311 fn check_flat_surface_excludes_outside_uv<S: Scalar>() {
312 let s = flat_patch::<S>();
313 assert!(
314 surface_definitely_not_contains(&s, &v3(5., 5., 0.), MAX, S::from_f64(EPS)).unwrap()
315 );
316 }
317 #[test]
318 fn flat_surface_excludes_outside_uv() {
319 for_all_scalars!(check_flat_surface_excludes_outside_uv);
320 }
321
322 fn check_lifted_surface_excludes_far_above<S: Scalar>() {
323 let s = lifted_patch::<S>();
324 assert!(
325 surface_definitely_not_contains(&s, &v3(0.5, 0.5, 10.), MAX, S::from_f64(EPS)).unwrap()
326 );
327 }
328 #[test]
329 fn lifted_surface_excludes_far_above() {
330 for_all_scalars!(check_lifted_surface_excludes_far_above);
331 }
332
333 fn check_lifted_surface_excludes_outside_uv<S: Scalar>() {
334 let s = lifted_patch::<S>();
335 assert!(
336 surface_definitely_not_contains(&s, &v3(5., 5., 0.), MAX, S::from_f64(EPS)).unwrap()
337 );
338 }
339 #[test]
340 fn lifted_surface_excludes_outside_uv() {
341 for_all_scalars!(check_lifted_surface_excludes_outside_uv);
342 }
343
344 fn check_zero_budget_always_false<S: Scalar>() {
347 let s = flat_patch::<S>();
348 let p = s.evaluate(S::from_f64(0.5), S::from_f64(0.5)).unwrap();
349 assert!(
350 surface_could_contain(&s, &p, 0, S::from_f64(EPS))
351 .unwrap()
352 .is_none()
353 );
354 }
355 #[test]
356 fn zero_budget_always_false() {
357 for_all_scalars!(check_zero_budget_always_false);
358 }
359}