parry3d/query/split/
split.rs

1/// The result of a plane-splitting operation.
2///
3/// This enum represents the three possible outcomes when splitting a geometric shape with a plane.
4/// It efficiently handles all cases without unnecessary allocations when the shape doesn't need
5/// to be split.
6///
7/// # Type Parameter
8///
9/// The generic type `T` represents the shape being split. Common types include:
10/// - [`Aabb`](crate::bounding_volume::Aabb) - Axis-aligned bounding box
11/// - [`Segment`](crate::shape::Segment) - Line segment
12/// - [`TriMesh`](crate::shape::TriMesh) - Triangle mesh
13///
14/// # Half-Space Definition
15///
16/// Given a plane defined by a normal vector `n` and bias `b`, a point `p` lies in:
17/// - **Negative half-space** if `n · p < b` (behind the plane)
18/// - **Positive half-space** if `n · p > b` (in front of the plane)
19/// - **On the plane** if `n · p ≈ b` (within epsilon tolerance)
20///
21/// # Examples
22///
23/// ## Splitting an AABB
24///
25/// ```rust
26/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
27/// use parry3d::bounding_volume::Aabb;
28/// use parry3d::math::Point;
29/// use parry3d::query::SplitResult;
30///
31/// let aabb = Aabb::new(Point::new(0.0, 0.0, 0.0), Point::new(10.0, 10.0, 10.0));
32///
33/// // Split along X-axis at x = 5.0
34/// match aabb.canonical_split(0, 5.0, 1e-6) {
35///     SplitResult::Pair(left, right) => {
36///         println!("AABB split into two pieces");
37///         println!("Left AABB: {:?}", left);
38///         println!("Right AABB: {:?}", right);
39///     }
40///     SplitResult::Negative => {
41///         println!("AABB is entirely on the negative side (x < 5.0)");
42///     }
43///     SplitResult::Positive => {
44///         println!("AABB is entirely on the positive side (x > 5.0)");
45///     }
46/// }
47/// # }
48/// ```
49///
50/// ## Splitting a Segment
51///
52/// ```rust
53/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
54/// use parry3d::shape::Segment;
55/// use parry3d::math::Point;
56/// use parry3d::query::SplitResult;
57///
58/// let segment = Segment::new(Point::new(0.0, 0.0, 0.0), Point::new(10.0, 0.0, 0.0));
59///
60/// // Split along X-axis at x = 3.0
61/// match segment.canonical_split(0, 3.0, 1e-6) {
62///     SplitResult::Pair(seg1, seg2) => {
63///         println!("Segment split at x = 3.0");
64///         println!("First segment: {:?} to {:?}", seg1.a, seg1.b);
65///         println!("Second segment: {:?} to {:?}", seg2.a, seg2.b);
66///     }
67///     SplitResult::Negative => {
68///         println!("Entire segment is on the negative side");
69///     }
70///     SplitResult::Positive => {
71///         println!("Entire segment is on the positive side");
72///     }
73/// }
74/// # }
75/// ```
76pub enum SplitResult<T> {
77    /// The split operation yielded two results: one lying on the negative half-space of the plane
78    /// and the second lying on the positive half-space of the plane.
79    ///
80    /// The first element is the shape piece on the **negative side** of the plane (where `n · p < b`).
81    /// The second element is the shape piece on the **positive side** of the plane (where `n · p > b`).
82    ///
83    /// For closed meshes, both pieces are typically "capped" with new geometry on the cutting plane
84    /// to ensure the result consists of valid closed shapes.
85    Pair(T, T),
86
87    /// The shape being split is fully contained in the negative half-space of the plane.
88    ///
89    /// This means all points of the shape satisfy `n · p < b` (or are within epsilon of the plane).
90    /// No splitting occurred because the shape doesn't cross the plane.
91    Negative,
92
93    /// The shape being split is fully contained in the positive half-space of the plane.
94    ///
95    /// This means all points of the shape satisfy `n · p > b` (or are within epsilon of the plane).
96    /// No splitting occurred because the shape doesn't cross the plane.
97    Positive,
98}
99
100/// The result of a plane-intersection operation.
101///
102/// This enum represents the outcome when computing the intersection between a geometric shape
103/// and a plane. Unlike [`SplitResult`] which produces pieces of the original shape, this produces
104/// the geometry that lies exactly on the plane (within epsilon tolerance).
105///
106/// # Type Parameter
107///
108/// The generic type `T` represents the result of the intersection. Common types include:
109/// - [`Polyline`](crate::shape::Polyline) - For mesh-plane intersections, representing the
110///   cross-section outline
111///
112/// # Use Cases
113///
114/// Plane-intersection operations are useful for:
115/// - **Cross-sectional analysis**: Computing 2D slices of 3D geometry
116/// - **Contour generation**: Finding outlines at specific heights
117/// - **Visualization**: Displaying cutting planes through complex geometry
118/// - **CAD/CAM**: Generating toolpaths or analyzing part geometry
119///
120/// # Examples
121///
122/// ## Computing a Mesh Cross-Section
123///
124/// ```rust
125/// # #[cfg(all(feature = "dim3", feature = "f32", feature = "spade"))] {
126/// use parry3d::shape::TriMesh;
127/// use parry3d::math::Point;
128/// use parry3d::query::IntersectResult;
129///
130/// // Create a simple tetrahedron mesh
131/// let vertices = vec![
132///     Point::new(0.0, 0.0, 0.0),
133///     Point::new(1.0, 0.0, 0.0),
134///     Point::new(0.5, 1.0, 0.0),
135///     Point::new(0.5, 0.5, 1.0),
136/// ];
137/// let indices = vec![
138///     [0u32, 1, 2],  // Bottom face
139///     [0, 1, 3],     // Front face
140///     [1, 2, 3],     // Right face
141///     [2, 0, 3],     // Left face
142/// ];
143/// let mesh = TriMesh::new(vertices, indices).unwrap();
144///
145/// // Compute cross-section at z = 0.5
146/// match mesh.canonical_intersection_with_plane(2, 0.5, 1e-6) {
147///     IntersectResult::Intersect(polyline) => {
148///         println!("Cross-section computed!");
149///         println!("Number of vertices: {}", polyline.vertices().len());
150///         // The polyline represents the outline of the mesh at z = 0.5
151///         // It may consist of multiple disconnected loops if the mesh
152///         // has multiple separate pieces at this height
153///     }
154///     IntersectResult::Negative => {
155///         println!("Mesh is entirely below z = 0.5");
156///     }
157///     IntersectResult::Positive => {
158///         println!("Mesh is entirely above z = 0.5");
159///     }
160/// }
161/// # }
162/// ```
163///
164/// ## Handling Multiple Connected Components
165///
166/// ```rust
167/// # #[cfg(all(feature = "dim3", feature = "f32", feature = "spade"))] {
168/// use parry3d::shape::TriMesh;
169/// use parry3d::query::IntersectResult;
170///
171/// # let vertices = vec![
172/// #     nalgebra::Point3::origin(),
173/// #     nalgebra::Point3::new(1.0, 0.0, 0.0),
174/// #     nalgebra::Point3::new(0.5, 1.0, 0.5),
175/// # ];
176/// # let indices = vec![[0u32, 1, 2]];
177/// # let mesh = TriMesh::new(vertices, indices).unwrap();
178/// // When intersecting a mesh with holes or multiple separate parts,
179/// // the resulting polyline may have multiple connected components
180/// match mesh.canonical_intersection_with_plane(2, 0.5, 1e-6) {
181///     IntersectResult::Intersect(polyline) => {
182///         // The polyline contains all intersection curves
183///         // You can identify separate components by analyzing connectivity
184///         // through the polyline's segment indices
185///         let indices = polyline.indices();
186///         println!("Number of edges in cross-section: {}", indices.len());
187///     }
188///     _ => println!("No intersection"),
189/// }
190/// # }
191/// ```
192pub enum IntersectResult<T> {
193    /// The intersect operation yielded a result, lying on the plane (within epsilon tolerance).
194    ///
195    /// For triangle meshes, this is typically a [`Polyline`](crate::shape::Polyline) representing
196    /// the outline where the mesh intersects the plane. The polyline may consist of multiple
197    /// disconnected loops if the mesh has holes or multiple separate pieces at the intersection
198    /// height.
199    ///
200    /// The intersection geometry lies on the plane, meaning all points `p` satisfy `n · p ≈ b`
201    /// (within the specified epsilon tolerance).
202    Intersect(T),
203
204    /// The shape being intersected is fully contained in the negative half-space of the plane.
205    ///
206    /// This means all points of the shape satisfy `n · p < b` (the shape is entirely "behind"
207    /// or "below" the plane). The plane doesn't intersect the shape at all.
208    Negative,
209
210    /// The shape being intersected is fully contained in the positive half-space of the plane.
211    ///
212    /// This means all points of the shape satisfy `n · p > b` (the shape is entirely "in front of"
213    /// or "above" the plane). The plane doesn't intersect the shape at all.
214    Positive,
215}