geop_core_geometry/intersection/mod.rs
1mod coincidence;
2pub mod curve_curve;
3pub mod curve_curve_bisect;
4pub mod curve_surface;
5pub mod curve_surface_bisect;
6
7pub use curve_curve::curve_curve_intersect;
8pub use curve_curve::refine_crossing as refine_curve_curve_crossing;
9pub use curve_surface::{curve_surface_intersect, refine_crossing};
10
11/// Result of a subdivision-based intersection search: either the complete,
12/// finite set of distinct solutions (fewer than the caller's requested
13/// `max_solutions`), or a signal that the two shapes coincide/overlap over a
14/// shared region rather than meeting at finitely many isolated points.
15///
16/// Reaching exactly `max_solutions` (`> 0`) is the established signal for
17/// coincidence throughout this module — see [`curve_curve::curve_curve_intersect`]'s
18/// own doc comment for why the search's breadth-first-by-level queue
19/// ordering specifically makes that reliable (an evenly-spread set of
20/// leaves across the *whole* shared region, not just a lucky cluster of
21/// adjacent ones). This enum makes that convention explicit and
22/// type-checked, in place of a `result.len() >= max_solutions` comparison
23/// every caller previously had to remember to make.
24///
25/// [`Intersections::Coincident`] still carries the (capped, evenly-spread)
26/// solutions found before the cap was hit — some callers detect coincidence
27/// some other way and just want every point regardless (see
28/// `geop-ops-booleans::remesh::remesh_edges_x_edges`); others want to reuse
29/// them for a follow-up geometric test instead of re-deriving them (see
30/// `remesh_edges_x_faces::find_coincident_pair`). Use [`Intersections::into_vec`]
31/// to get the solutions either way, or match to tell the two cases apart.
32#[derive(Debug, Clone)]
33pub enum Intersections<T> {
34 /// The complete set of distinct solutions — strictly fewer than the
35 /// requested `max_solutions` (or `max_solutions` was `0`).
36 Found(Vec<T>),
37 /// `max_solutions` (`> 0`) distinct solutions were found before the
38 /// search exhausted the domain: read as coincidence (or, for a partial
39 /// overlap, an extended shared region), not a finite crossing count.
40 Coincident(Vec<T>),
41}
42
43impl<T> Intersections<T> {
44 /// `true` for [`Intersections::Coincident`].
45 pub fn is_coincident(&self) -> bool {
46 matches!(self, Intersections::Coincident(_))
47 }
48
49 /// The solutions found, regardless of variant.
50 pub fn into_vec(self) -> Vec<T> {
51 match self {
52 Intersections::Found(v) | Intersections::Coincident(v) => v,
53 }
54 }
55
56 /// The solutions found, regardless of variant.
57 pub fn as_slice(&self) -> &[T] {
58 match self {
59 Intersections::Found(v) | Intersections::Coincident(v) => v,
60 }
61 }
62
63 pub fn len(&self) -> usize {
64 self.as_slice().len()
65 }
66
67 pub fn is_empty(&self) -> bool {
68 self.as_slice().is_empty()
69 }
70}