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}