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;