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**: Points where the dot product with the plane normal is less than the bias
22//! - **Positive half-space**: Points where the dot product with the plane normal is greater than the bias
23//! - **On the plane**: Points 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::Point;
49//! use parry3d::query::SplitResult;
50//!
51//! // Split an AABB along the X-axis at x = 5.0
52//! let aabb = Aabb::new(Point::new(0.0, 0.0, 0.0), Point::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::{Point, Vector};
79//! use parry3d::query::SplitResult;
80//! use parry3d::na::Unit;
81//!
82//! # let vertices = vec![
83//! # Point::new(0.0, 0.0, 0.0),
84//! # Point::new(1.0, 0.0, 0.0),
85//! # Point::new(0.0, 1.0, 0.0),
86//! # ];
87//! # let indices = vec![[0u32, 1, 2]];
88//! # let mesh = TriMesh::new(vertices, indices).unwrap();
89//! // Split a mesh along the Z-axis at z = 0.5
90//! match mesh.canonical_split(2, 0.5, 1e-6) {
91//! SplitResult::Pair(bottom_half, top_half) => {
92//! // Mesh was split into two separate meshes
93//! // bottom_half contains the part with z < 0.5
94//! // top_half contains the part with z > 0.5
95//! // Both meshes are "capped" with new triangles on the cutting plane
96//! }
97//! SplitResult::Negative => {
98//! // Entire mesh is below z = 0.5
99//! }
100//! SplitResult::Positive => {
101//! // Entire mesh is above z = 0.5
102//! }
103//! }
104//! # }
105//! ```
106//!
107//! ## 3. Cross-Section Computation
108//! Computing the intersection of a mesh with a plane gives you a 2D cross-section, useful
109//! for visualization, analysis, or generating contours.
110//!
111// FIXME: this loops indefinitely.
112// //! ```rust no_run
113// //! # #[cfg(all(feature = "dim3", feature = "spade", feature = "f32"))]
114// //! # {
115// //! use parry3d::shape::TriMesh;
116// //! use parry3d::query::IntersectResult;
117// //!
118// //! # let vertices = vec![
119// //! # parry3d::na::Point3::origin(),
120// //! # parry3d::na::Point3::new(1.0, 0.0, 0.0),
121// //! # parry3d::na::Point3::new(0.5, 1.0, 0.5),
122// //! # parry3d::na::Point3::new(0.5, 0.0, 1.0),
123// //! # ];
124// //! # let indices = vec![[0u32, 1, 2], [0, 1, 3]];
125// //! # let mesh = TriMesh::new(vertices, indices).unwrap();
126// //! // Get the cross-section of a mesh at z = 0.5
127// //! match mesh.canonical_intersection_with_plane(2, 0.5, 1e-6) {
128// //! IntersectResult::Intersect(polyline) => {
129// //! // polyline contains the 2D outline of the mesh at z = 0.5
130// //! // This can have multiple connected components if the mesh
131// //! // has holes or multiple separate pieces at this height
132// //! }
133// //! IntersectResult::Negative => {
134// //! // Mesh doesn't intersect the plane; it's entirely on the negative side
135// //! }
136// //! IntersectResult::Positive => {
137// //! // Mesh doesn't intersect the plane; it's entirely on the positive side
138// //! }
139// //! }
140// //! # }
141// //! ```
142//!
143//! # Supported Shapes
144//!
145//! Currently, the following shapes support splitting operations:
146//!
147//! - [`Aabb`](crate::bounding_volume::Aabb) - Axis-aligned bounding boxes
148//! - [`Segment`](crate::shape::Segment) - Line segments (in 2D and 3D)
149//! - [`TriMesh`](crate::shape::TriMesh) - Triangle meshes (3D only, requires `spade` feature)
150//!
151//! # Result Types
152//!
153//! The module provides two result types to represent the outcomes of splitting operations:
154//!
155//! - [`SplitResult`]: For operations that split a shape into pieces
156//! - [`IntersectResult`]: For operations that compute the intersection with a plane
157//!
158//! Both types use an enum to efficiently represent the three possible outcomes without
159//! unnecessary allocations when a split doesn't occur.
160//!
161//! # Epsilon Tolerance
162//!
163//! All splitting operations accept an `epsilon` parameter to handle floating-point precision
164//! issues. Points within `epsilon` distance of the plane are considered to lie on the plane.
165//! A typical value is `1e-6` for single precision (`f32`) or `1e-10` for double precision (`f64`).
166//!
167//! Choosing an appropriate epsilon is important:
168//! - **Too small**: May cause numerical instability or incorrect classifications
169//! - **Too large**: May merge distinct features or produce incorrect topology
170//!
171//! # Implementation Notes
172//!
173//! - **Segment splitting** is implemented for both 2D and 3D
174//! - **AABB splitting** preserves axis-alignment and is very efficient
175//! - **TriMesh splitting** (3D only) uses Delaunay triangulation to properly cap the mesh
176//! where it's cut, ensuring the result is a valid closed mesh if the input was closed
177//! - All operations are deterministic given the same input and epsilon value
178
179pub use self::split::{IntersectResult, SplitResult};
180
181mod split;
182mod split_aabb;
183mod split_segment;
184
185#[cfg(all(feature = "dim3", feature = "spade"))]
186mod split_trimesh;