1use crate::{ambient_space::common_space, simplex::Simplex, vector::DotProduct};
2use algebraeon_rings::{
3 linear::{
4 finitely_free_module::FinitelyFreeModuleStructure,
5 finitely_free_submodule::FinitelyFreeSubmoduleStructure,
6 },
7 matrix::{Matrix, MatrixStructure},
8 structure::{FieldSignature, OrderedRingSignature, ZeroEqSignature},
9};
10use itertools::Itertools;
11use std::collections::HashSet;
12use std::hash::Hash;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum SimplexOverlapResult {
16 Disjoint, Touching, Overlap, }
20
21fn simplex_overlap_impl<
34 'f,
35 FS: OrderedRingSignature + FieldSignature,
36 const RETURN_IF_TOUCHING: bool,
37>(
38 a: &Simplex<'f, FS>,
39 b: &Simplex<'f, FS>,
40) -> SimplexOverlapResult
41where
42 FS::Set: Hash,
43{
44 let space = common_space(a.ambient_space(), b.ambient_space()).unwrap();
45
46 if a.n() == 0 || b.n() == 0 {
48 return SimplexOverlapResult::Disjoint;
49 }
50 debug_assert!(space.linear_dimension().is_some()); let space_dim = space.linear_dimension().unwrap();
52 let field = space.field();
53
54 let a_root = a.point(0);
56 let b_root = b.point(0);
57
58 let a_vecs = (1..a.n()).map(|i| a.point(i) - a_root).collect::<Vec<_>>();
60 let b_vecs = (1..b.n()).map(|i| b.point(i) - b_root).collect::<Vec<_>>();
61
62 let a_to_b_vec = a_root - b_root;
64
65 let linear_span_foo = space.affine_subspace_from_root_and_linear_span(
68 &space.origin().unwrap(),
69 a_vecs.iter().chain(b_vecs.iter()).collect(),
70 );
71 let linear_span_foo_submodule = MatrixStructure::new(space.field().clone()).row_span(
72 Matrix::construct(a_vecs.len() + b_vecs.len(), space_dim, |r, c| {
73 if r < a_vecs.len() {
74 a_vecs[r].coordinate(c).clone()
75 } else {
76 b_vecs[r - a_vecs.len()].coordinate(c).clone()
77 }
78 }),
79 );
80 debug_assert_eq!(linear_span_foo_submodule.module_rank(), space_dim);
81
82 let linear_span_bar = MatrixStructure::new(space.field().clone()).row_span(Matrix::construct(
88 a_vecs.len() + b_vecs.len() + 1,
89 space_dim,
90 |r, c| {
91 if r < a_vecs.len() {
92 a_vecs[r].coordinate(c).clone()
93 } else if r < a_vecs.len() + b_vecs.len() {
94 b_vecs[r - a_vecs.len()].coordinate(c).clone()
95 } else {
96 a_to_b_vec.coordinate(c).clone()
97 }
98 },
99 ));
100 debug_assert_eq!(linear_span_bar.module_rank(), space_dim);
101 debug_assert!(
102 (linear_span_bar.rank() == linear_span_foo_submodule.rank())
103 || (linear_span_bar.rank() == linear_span_foo_submodule.rank() + 1)
104 );
105
106 let mut normals = HashSet::new();
108 let mut just_touching = false;
111
112 let n = linear_span_foo.embedded_space().linear_dimension().unwrap();
113
114 for normal in {
115 if linear_span_bar.rank() != linear_span_foo_submodule.rank() {
116 let normal_space =
119 FinitelyFreeSubmoduleStructure::new(
120 FinitelyFreeModuleStructure::<FS, &'f FS>::new(field, space_dim),
121 )
122 .intersect(
123 MatrixStructure::new(space.field().clone()).col_kernel(Matrix::construct(
124 a_vecs.len() + b_vecs.len(),
125 space_dim,
126 |r, c| {
127 if r < a_vecs.len() {
128 a_vecs[r].coordinate(c).clone()
129 } else {
130 b_vecs[r - a_vecs.len()].coordinate(c).clone()
131 }
132 },
133 )),
134 linear_span_bar.clone(),
135 );
136 debug_assert!(normal_space.rank() == 1);
137 let normal = space.vector(normal_space.basis().first().unwrap().iter().cloned());
138 for vec in &a_vecs {
139 debug_assert!(field.is_zero(&normal.dot(vec)));
140 }
141 for vec in &b_vecs {
142 debug_assert!(field.is_zero(&normal.dot(vec)));
143 }
144 Some(normal)
145 } else {
146 None
147 }
148 }
149 .into_iter()
150 .chain((0..n).map(|i| (i, n - i - 1)).flat_map(|(i, j)| {
152 (0..a.n())
153 .combinations(i + 1)
154 .cartesian_product((0..b.n()).combinations(j + 1))
155 .filter_map(|(a_pts, b_pts)| {
156 let i = a_pts.len() - 1;
157 let j = b_pts.len() - 1;
158
159 let a_sub = a.sub_simplex(a_pts.clone());
160 let b_sub = b.sub_simplex(b_pts);
161
162 let a_sub_root = a_sub.point(0);
163 let b_sub_root = b_sub.point(0);
164
165 let a_sub_vecs = (0..i)
166 .map(|k| a_sub.point(k + 1) - a_sub_root)
167 .collect::<Vec<_>>();
168 let b_sub_vecs = (0..j)
169 .map(|k| b_sub.point(k + 1) - b_sub_root)
170 .collect::<Vec<_>>();
171
172 let normal_space = FinitelyFreeSubmoduleStructure::new(
173 FinitelyFreeModuleStructure::<FS, &'f FS>::new(field, space_dim),
174 )
175 .intersect(
176 MatrixStructure::new(space.field().clone()).col_kernel(Matrix::construct(
177 i + j,
178 space_dim,
179 |r, c| {
180 if r < i {
181 a_sub_vecs[r].coordinate(c).clone()
182 } else {
183 b_sub_vecs[r - i].coordinate(c).clone()
184 }
185 },
186 )),
187 linear_span_foo_submodule.clone(),
188 );
189
190 debug_assert!(normal_space.rank() >= 1);
191
192 if normal_space.rank() == 1 {
194 let normal =
195 space.vector(normal_space.basis().first().unwrap().iter().cloned());
196 for vec in &a_sub_vecs {
197 debug_assert!(field.is_zero(&normal.dot(vec)));
198 }
199 for vec in &b_sub_vecs {
200 debug_assert!(field.is_zero(&normal.dot(vec)));
201 }
202 Some(normal)
203 } else {
204 None
205 }
206 })
207 })) {
208 if !normals.contains(&normal) {
209 let mut a_points = a.points().iter();
211 let a_proj = normal.dot(a_points.next().unwrap());
212 let mut min_a_proj = a_proj.clone();
213 let mut max_a_proj = a_proj;
214 for a_pt in a_points {
215 let proj = normal.dot(a_pt);
216 if field.cmp(&proj, &min_a_proj) == std::cmp::Ordering::Less {
217 min_a_proj = proj.clone();
218 }
219 if field.cmp(&proj, &max_a_proj) == std::cmp::Ordering::Greater {
220 max_a_proj = proj.clone();
221 }
222 }
223
224 let mut b_points = b.points().iter();
226 let b_proj = normal.dot(b_points.next().unwrap());
227 let mut min_b_proj = b_proj.clone();
228 let mut max_b_proj = b_proj;
229 for b_pt in b_points {
230 let proj = normal.dot(b_pt);
231 if field.cmp(&proj, &min_b_proj) == std::cmp::Ordering::Less {
232 min_b_proj = proj.clone();
233 }
234 if field.cmp(&proj, &max_b_proj) == std::cmp::Ordering::Greater {
235 max_b_proj = proj.clone();
236 }
237 }
238
239 match (
241 field.cmp(&max_a_proj, &min_b_proj),
242 field.cmp(&max_b_proj, &min_a_proj),
243 ) {
244 (std::cmp::Ordering::Less, _) | (_, std::cmp::Ordering::Less) => {
245 return SimplexOverlapResult::Disjoint;
246 }
247 (std::cmp::Ordering::Greater, std::cmp::Ordering::Greater) => {}
248 (std::cmp::Ordering::Equal, _) | (_, std::cmp::Ordering::Equal) => {
249 if RETURN_IF_TOUCHING {
250 return SimplexOverlapResult::Touching;
251 }
252 just_touching = true;
253 }
254 }
255
256 normals.insert(normal);
257 }
258 }
259
260 match just_touching {
261 false => SimplexOverlapResult::Overlap,
262 true => SimplexOverlapResult::Touching,
263 }
264}
265
266pub fn simplex_interior_overlap<'f, FS: OrderedRingSignature + FieldSignature>(
267 a: &Simplex<'f, FS>,
268 b: &Simplex<'f, FS>,
269) -> bool
270where
271 FS::Set: Hash,
272{
273 match simplex_overlap_impl::<FS, true>(a, b) {
274 SimplexOverlapResult::Disjoint => false,
275 SimplexOverlapResult::Touching => false,
276 SimplexOverlapResult::Overlap => true,
277 }
278}
279
280pub fn simplex_closure_overlap<'f, FS: OrderedRingSignature + FieldSignature>(
281 a: &Simplex<'f, FS>,
282 b: &Simplex<'f, FS>,
283) -> bool
284where
285 FS::Set: Hash,
286{
287 match simplex_overlap_impl::<FS, false>(a, b) {
288 SimplexOverlapResult::Disjoint => false,
289 SimplexOverlapResult::Touching => true,
290 SimplexOverlapResult::Overlap => true,
291 }
292}
293
294pub fn simplex_overlap<'f, FS: OrderedRingSignature + FieldSignature>(
295 a: &Simplex<'f, FS>,
296 b: &Simplex<'f, FS>,
297) -> SimplexOverlapResult
298where
299 FS::Set: Hash,
300{
301 simplex_overlap_impl::<FS, false>(a, b)
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307 use crate::ambient_space::AffineSpace;
308 use algebraeon_nzq::Rational;
309
310 #[test]
311 fn something_and_null() {
312 let space3 = AffineSpace::new_linear(Rational::structure_ref(), 3);
313
314 assert_eq!(
315 simplex_overlap(
316 &space3.simplex(vec![space3.vector([1, 2, 3])]).unwrap(),
317 &space3.simplex(vec![]).unwrap()
318 ),
319 SimplexOverlapResult::Disjoint
320 );
321
322 assert_eq!(
323 simplex_overlap(
324 &space3
325 .simplex(vec![space3.vector([1, 2, 3]), space3.vector([4, 5, 6])])
326 .unwrap(),
327 &space3.simplex(vec![]).unwrap()
328 ),
329 SimplexOverlapResult::Disjoint
330 );
331 }
332
333 #[test]
334 fn two_points() {
335 let space3 = AffineSpace::new_linear(Rational::structure_ref(), 3);
336
337 assert_eq!(
339 simplex_overlap(
340 &space3.simplex(vec![space3.vector([1, 2, 3])]).unwrap(),
341 &space3.simplex(vec![space3.vector([1, 3, 2])]).unwrap()
342 ),
343 SimplexOverlapResult::Disjoint
344 );
345
346 assert_eq!(
348 simplex_overlap(
349 &space3.simplex(vec![space3.vector([1, 2, 3])]).unwrap(),
350 &space3.simplex(vec![space3.vector([1, 2, 3])]).unwrap()
351 ),
352 SimplexOverlapResult::Overlap
353 );
354 }
355
356 #[test]
357 fn point_and_line() {
358 let space3 = AffineSpace::new_linear(Rational::structure_ref(), 3);
359
360 assert_eq!(
362 simplex_overlap(
363 &space3
364 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([2, 0, 0])])
365 .unwrap(),
366 &space3.simplex(vec![space3.vector([1, 0, 0])]).unwrap()
367 ),
368 SimplexOverlapResult::Overlap
369 );
370
371 assert_eq!(
373 simplex_overlap(
374 &space3
375 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([2, 0, 0])])
376 .unwrap(),
377 &space3.simplex(vec![space3.vector([2, 0, 0])]).unwrap()
378 ),
379 SimplexOverlapResult::Touching
380 );
381
382 assert_eq!(
384 simplex_overlap(
385 &space3
386 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([2, 0, 0])])
387 .unwrap(),
388 &space3.simplex(vec![space3.vector([0, 1, 0])]).unwrap()
389 ),
390 SimplexOverlapResult::Disjoint
391 );
392 assert_eq!(
393 simplex_overlap(
394 &space3
395 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([2, 0, 0])])
396 .unwrap(),
397 &space3.simplex(vec![space3.vector([1, 1, 0])]).unwrap()
398 ),
399 SimplexOverlapResult::Disjoint
400 );
401 assert_eq!(
402 simplex_overlap(
403 &space3
404 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([2, 0, 0])])
405 .unwrap(),
406 &space3.simplex(vec![space3.vector([3, 0, 0])]).unwrap()
407 ),
408 SimplexOverlapResult::Disjoint
409 );
410 assert_eq!(
411 simplex_overlap(
412 &space3
413 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([2, 0, 0])])
414 .unwrap(),
415 &space3.simplex(vec![space3.vector([2, 1, 0])]).unwrap()
416 ),
417 SimplexOverlapResult::Disjoint
418 );
419 assert_eq!(
420 simplex_overlap(
421 &space3
422 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([2, 0, 0])])
423 .unwrap(),
424 &space3.simplex(vec![space3.vector([3, 1, 0])]).unwrap()
425 ),
426 SimplexOverlapResult::Disjoint
427 );
428 }
429
430 #[test]
431 fn point_and_triangle() {
432 let space3 = AffineSpace::new_linear(Rational::structure_ref(), 3);
433
434 let triangle = space3
435 .simplex(vec![
436 space3.vector([6, 0, 0]),
437 space3.vector([0, 6, 0]),
438 space3.vector([0, 0, 6]),
439 ])
440 .unwrap();
441
442 assert_eq!(
443 simplex_overlap(
444 &triangle,
445 &space3.simplex(vec![space3.vector([0, 0, 0])]).unwrap()
446 ),
447 SimplexOverlapResult::Disjoint
448 );
449 assert_eq!(
450 simplex_overlap(
451 &triangle,
452 &space3.simplex(vec![space3.vector([4, 4, 4])]).unwrap()
453 ),
454 SimplexOverlapResult::Disjoint
455 );
456
457 assert_eq!(
458 simplex_overlap(
459 &triangle,
460 &space3.simplex(vec![space3.vector([2, 2, 2])]).unwrap()
461 ),
462 SimplexOverlapResult::Overlap
463 );
464
465 assert_eq!(
466 simplex_overlap(
467 &triangle,
468 &space3.simplex(vec![space3.vector([0, 6, 0])]).unwrap()
469 ),
470 SimplexOverlapResult::Touching
471 );
472
473 assert_eq!(
474 simplex_overlap(
475 &triangle,
476 &space3.simplex(vec![space3.vector([3, 3, 0])]).unwrap()
477 ),
478 SimplexOverlapResult::Touching
479 );
480 }
481
482 #[test]
483 fn line_and_triangle() {
484 let space3 = AffineSpace::new_linear(Rational::structure_ref(), 3);
485
486 let triangle = space3
487 .simplex(vec![
488 space3.vector([6, 0, 0]),
489 space3.vector([0, 6, 0]),
490 space3.vector([0, 0, 6]),
491 ])
492 .unwrap();
493
494 assert_eq!(
495 simplex_overlap(
496 &triangle,
497 &space3
498 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([1, 1, 1])])
499 .unwrap()
500 ),
501 SimplexOverlapResult::Disjoint
502 );
503 assert_eq!(
504 simplex_overlap(
505 &triangle,
506 &space3
507 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([2, 2, 2])])
508 .unwrap()
509 ),
510 SimplexOverlapResult::Touching
511 );
512 assert_eq!(
513 simplex_overlap(
514 &triangle,
515 &space3
516 .simplex(vec![space3.vector([0, 0, 0]), space3.vector([3, 3, 3])])
517 .unwrap()
518 ),
519 SimplexOverlapResult::Overlap
520 );
521
522 assert_eq!(
523 simplex_overlap(
524 &triangle,
525 &space3
526 .simplex(vec![space3.vector([6, 0, 0]), space3.vector([7, 0, 0])])
527 .unwrap()
528 ),
529 SimplexOverlapResult::Touching
530 );
531
532 assert_eq!(
533 simplex_overlap(
534 &triangle,
535 &space3
536 .simplex(vec![space3.vector([0, -6, 0]), space3.vector([6, 0, 0])])
537 .unwrap()
538 ),
539 SimplexOverlapResult::Touching
540 );
541
542 assert_eq!(
543 simplex_overlap(
544 &triangle,
545 &space3
546 .simplex(vec![space3.vector([6, -12, 0]), space3.vector([12, -6, 0])])
547 .unwrap()
548 ),
549 SimplexOverlapResult::Disjoint
550 );
551 }
552
553 #[test]
554 fn triangle_and_triangle() {
555 let space3 = AffineSpace::new_linear(Rational::structure_ref(), 3);
556
557 assert_eq!(
558 simplex_overlap(
559 &space3
560 .simplex(vec![
561 space3.vector([0, -1, 0]),
562 space3.vector([0, 1, 0]),
563 space3.vector([1, 0, 0])
564 ])
565 .unwrap(),
566 &space3
567 .simplex(vec![
568 space3.vector([0, 0, -1]),
569 space3.vector([0, 0, 1]),
570 space3.vector([-1, 0, 0])
571 ])
572 .unwrap()
573 ),
574 SimplexOverlapResult::Touching
575 );
576
577 assert_eq!(
578 simplex_overlap(
579 &space3
580 .simplex(vec![
581 space3.vector([0, -1, 0]),
582 space3.vector([0, 1, 0]),
583 space3.vector([1, 0, 0])
584 ])
585 .unwrap(),
586 &space3
587 .simplex(vec![
588 space3.vector([1, 0, -1]),
589 space3.vector([1, 0, 1]),
590 space3.vector([0, 0, 0])
591 ])
592 .unwrap()
593 ),
594 SimplexOverlapResult::Overlap
595 );
596
597 assert_eq!(
598 simplex_overlap(
599 &space3
600 .simplex(vec![
601 space3.vector([0, -1, 0]),
602 space3.vector([0, 1, 0]),
603 space3.vector([1, 0, 0])
604 ])
605 .unwrap(),
606 &space3
607 .simplex(vec![
608 space3.vector([-1, 0, -1]),
609 space3.vector([-1, 0, 1]),
610 space3.vector([-2, 0, 0])
611 ])
612 .unwrap()
613 ),
614 SimplexOverlapResult::Disjoint
615 );
616 }
617}