parry3d_f64/query/split/
mod.rs

1//! Shape splitting and plane intersection operations.
2//!
3//! # Overview
4//!
5//! This module provides functionality for splitting geometric shapes with planes and computing
6//! plane-shape intersections. These operations are fundamental for various geometric algorithms
7//! including:
8//!
9//! - **Spatial partitioning**: Dividing space with axis-aligned or oriented planes
10//! - **CSG (Constructive Solid Geometry)**: Boolean operations on meshes
11//! - **Mesh processing**: Cutting and slicing operations on 3D models
12//! - **Cross-sections**: Computing 2D slices of 3D geometry
13//! - **BVH construction**: Building spatial acceleration structures
14//!
15//! # What is Shape Splitting?
16//!
17//! Shape splitting is the process of dividing a geometric shape into two or more pieces using
18//! a plane. The plane acts as a "cutting tool" that partitions the shape based on which side
19//! of the plane each part lies on:
20//!
21//! - **Negative half-space**: Vectors where the dot product with the plane normal is less than the bias
22//! - **Positive half-space**: Vectors where the dot product with the plane normal is greater than the bias
23//! - **On the plane**: Vectors that lie exactly on the plane (within an epsilon tolerance)
24//!
25//! ## Plane Definition
26//!
27//! A plane is defined by:
28//! - A **normal vector** (unit vector perpendicular to the plane)
29//! - A **bias** (signed distance from the origin along the normal)
30//!
31//! The plane equation is: `normal ยท point = bias`
32//!
33//! For canonical splits, the normal is aligned with a coordinate axis (e.g., X, Y, or Z axis),
34//! making the operation axis-aligned.
35//!
36//! # When to Use Shape Splitting
37//!
38//! Shape splitting is useful in many scenarios:
39//!
40//! ## 1. Spatial Partitioning
41//! When building spatial data structures like octrees or k-d trees, you need to divide space
42//! recursively. Shape splitting determines which objects or parts of objects belong in each
43//! partition.
44//!
45//! ```rust
46//! # #[cfg(all(feature = "dim3", feature = "f32"))] {
47//! use parry3d::bounding_volume::Aabb;
48//! use parry3d::math::Vector;
49//! use parry3d::query::SplitResult;
50//!
51//! // Split an AABB along the X-axis at x = 5.0
52//! let aabb = Aabb::new(Vector::new(0.0, 0.0, 0.0), Vector::new(10.0, 10.0, 10.0));
53//! match aabb.canonical_split(0, 5.0, 1e-6) {
54//!     SplitResult::Pair(left, right) => {
55//!         // AABB was split into two pieces
56//!         // left contains points with x < 5.0
57//!         // right contains points with x > 5.0
58//!     }
59//!     SplitResult::Negative => {
60//!         // Entire AABB is on the negative side (x < 5.0)
61//!     }
62//!     SplitResult::Positive => {
63//!         // Entire AABB is on the positive side (x > 5.0)
64//!     }
65//! }
66//! # }
67//! ```
68//!
69//! ## 2. Mesh Slicing
70//! For 3D modeling, CAD applications, or game engines, you might need to cut meshes with
71//! planes. This is useful for effects like slicing objects with swords, laser cuts, or
72//! architectural cross-sections.
73//!
74//! ```rust
75//! # #[cfg(all(feature = "dim3", feature = "spade", feature = "f32"))]
76//! # {
77//! use parry3d::shape::TriMesh;
78//! use parry3d::math::Vector;
79//! use parry3d::query::SplitResult;
80//!
81//! # let vertices = vec![
82//! #     Vector::new(0.0, 0.0, 0.0),
83//! #     Vector::new(1.0, 0.0, 0.0),
84//! #     Vector::new(0.0, 1.0, 0.0),
85//! # ];
86//! # let indices = vec![[0u32, 1, 2]];
87//! # let mesh = TriMesh::new(vertices, indices).unwrap();
88//! // Split a mesh along the Z-axis at z = 0.5
89//! match mesh.canonical_split(2, 0.5, 1e-6) {
90//!     SplitResult::Pair(bottom_half, top_half) => {
91//!         // Mesh was split into two separate meshes
92//!         // bottom_half contains the part with z < 0.5
93//!         // top_half contains the part with z > 0.5
94//!         // Both meshes are "capped" with new triangles on the cutting plane
95//!     }
96//!     SplitResult::Negative => {
97//!         // Entire mesh is below z = 0.5
98//!     }
99//!     SplitResult::Positive => {
100//!         // Entire mesh is above z = 0.5
101//!     }
102//! }
103//! # }
104//! ```
105//!
106//! ## 3. Cross-Section Computation
107//! Computing the intersection of a mesh with a plane gives you a 2D cross-section, useful
108//! for visualization, analysis, or generating contours.
109//!
110// FIXME: this loops indefinitely.
111// //! ```rust no_run
112// //! # #[cfg(all(feature = "dim3", feature = "spade", feature = "f32"))]
113// //! # {
114// //! use parry3d::shape::TriMesh;
115// //! use parry3d::query::IntersectResult;
116// //!
117// //! # let vertices = vec![
118// //! #     parry3d::math::Vector::ZERO,
119// //! #     parry3d::math::Vector::new(1.0, 0.0, 0.0),
120// //! #     parry3d::math::Vector::new(0.5, 1.0, 0.5),
121// //! #     parry3d::math::Vector::new(0.5, 0.0, 1.0),
122// //! # ];
123// //! # let indices = vec![[0u32, 1, 2], [0, 1, 3]];
124// //! # let mesh = TriMesh::new(vertices, indices).unwrap();
125// //! // Get the cross-section of a mesh at z = 0.5
126// //! match mesh.canonical_intersection_with_plane(2, 0.5, 1e-6) {
127// //!     IntersectResult::Intersect(polyline) => {
128// //!         // polyline contains the 2D outline of the mesh at z = 0.5
129// //!         // This can have multiple connected components if the mesh
130// //!         // has holes or multiple separate pieces at this height
131// //!     }
132// //!     IntersectResult::Negative => {
133// //!         // Mesh doesn't intersect the plane; it's entirely on the negative side
134// //!     }
135// //!     IntersectResult::Positive => {
136// //!         // Mesh doesn't intersect the plane; it's entirely on the positive side
137// //!     }
138// //! }
139// //! # }
140// //! ```
141//!
142//! # Supported Shapes
143//!
144//! Currently, the following shapes support splitting operations:
145//!
146//! - [`Aabb`](crate::bounding_volume::Aabb) - Axis-aligned bounding boxes
147//! - [`Segment`](crate::shape::Segment) - Line segments (in 2D and 3D)
148//! - [`TriMesh`](crate::shape::TriMesh) - Triangle meshes (3D only, requires `spade` feature)
149//!
150//! # Result Types
151//!
152//! The module provides two result types to represent the outcomes of splitting operations:
153//!
154//! - [`SplitResult`]: For operations that split a shape into pieces
155//! - [`IntersectResult`]: For operations that compute the intersection with a plane
156//!
157//! Both types use an enum to efficiently represent the three possible outcomes without
158//! unnecessary allocations when a split doesn't occur.
159//!
160//! # Epsilon Tolerance
161//!
162//! All splitting operations accept an `epsilon` parameter to handle floating-point precision
163//! issues. Vectors within `epsilon` distance of the plane are considered to lie on the plane.
164//! A typical value is `1e-6` for single precision (`f32`) or `1e-10` for double precision (`f64`).
165//!
166//! Choosing an appropriate epsilon is important:
167//! - **Too small**: May cause numerical instability or incorrect classifications
168//! - **Too large**: May merge distinct features or produce incorrect topology
169//!
170//! # Implementation Notes
171//!
172//! - **Segment splitting** is implemented for both 2D and 3D
173//! - **AABB splitting** preserves axis-alignment and is very efficient
174//! - **TriMesh splitting** (3D only) uses Delaunay triangulation to properly cap the mesh
175//!   where it's cut, ensuring the result is a valid closed mesh if the input was closed
176//! - All operations are deterministic given the same input and epsilon value
177
178pub use self::split::{IntersectResult, SplitResult};
179
180mod split;
181mod split_aabb;
182mod split_segment;
183
184#[cfg(all(feature = "dim3", feature = "spade"))]
185mod split_trimesh;