Skip to main content

oxiphysics_collision/
lib.rs

1// Copyright 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3
4//! Collision detection algorithms including broad-phase and narrow-phase.
5//!
6//! Provides broadphase (brute force, sweep-and-prune, BVH), narrowphase
7//! (GJK, EPA, specialized sphere/box/capsule tests), and contact manifold
8//! generation.
9
10mod error;
11pub use error::*;
12
13pub mod types;
14pub use types::{
15    CollisionEvent, CollisionFilter, CollisionPair, Contact, ContactManifold, FeatureId,
16    MAX_CONTACTS, PhysicsMaterial, RichContact, RichContactManifold,
17};
18
19pub mod broadphase;
20pub use broadphase::{BroadPhase, BruteForceBroadPhase, BvhBroadphase, SweepAndPrune};
21
22pub mod dbvt;
23pub use dbvt::{BvhAabb, DynamicBvh};
24
25pub mod narrowphase;
26pub use narrowphase::{
27    BatchNarrowPhase, ContactFilter, NarrowPhaseContact, ShapeKind, shape_shape_contact,
28};
29pub use narrowphase::{Epa, Gjk, NarrowPhaseDispatcher};
30
31pub mod ccd;
32pub use ccd::{
33    CcdBodyEntry, CcdPair, CcdPipeline, CcdPipelineConfig, ConservativeAdvancement, RigidBodyState,
34    ToiResult, advance_body_to, integrate_angular, integrate_linear, quat_mul, quat_normalize,
35    quat_rotate, slerp_quat, time_of_impact,
36};
37
38pub mod sweep;
39pub use sweep::{
40    RayHit, SweepResult, aabb_aabb_overlap, point_aabb_dist_sq, ray_aabb, ray_sphere, ray_triangle,
41    sphere_aabb_overlap, swept_aabb_aabb, swept_sphere_aabb, swept_sphere_sphere,
42};
43
44pub mod sap;
45
46pub mod query;
47
48pub mod gjk_epa;
49pub use gjk_epa::{
50    ConvexShape, EpaFace, EpaResult, GjkBox, GjkCapsule, GjkSphere, Simplex, add3, cross3,
51    do_simplex, dot3, epa, gjk_distance, gjk_epa_contact, gjk_intersect, len3, minkowski_support,
52    normalize3, scale3, sub3,
53};
54
55pub mod contact_graph;
56pub use contact_graph::{
57    ContactCache, ContactGraph, ContactKey, PersistedContact, SpeculativeContact,
58    speculative_contact, speculative_impulse,
59};
60
61/// Persistent contact manifold caching and warm-starting for stable simulation.
62pub mod manifold_cache;
63pub use manifold_cache::{
64    CachingStrategy, ContactIsland, ContactManifoldCache, ContactPointId, ManifoldCompressor,
65    ManifoldLifetimeManager, ManifoldMetrics, ManifoldPointMatcher, ManifoldReduction,
66    PersistentManifold, age_cache_warm_start, age_manifold_warm_start, age_warm_start,
67    apply_position_corrections, baumgarte_correction, baumgarte_correction_slop,
68    build_contact_islands, compute_manifold_metrics, find_match_with_strategy,
69};
70
71/// Separating Axis Theorem (SAT) collision detection for OBBs and convex shapes.
72pub mod sat_collision;
73pub use sat_collision::{
74    Capsule as SatCapsule, ContactFeature, ContactManifoldBuilder, ConvexPolyhedraSat,
75    ConvexPolyhedron, ConvexPolytope, Obb, ObbBvh, ObbBvhNode, ObbCapsuleCollision, ObbCollision,
76    ObbTree, ObbTriangleCollision, PolyhedraContact, PolytopeCollision, SatAxisCache, SatContact,
77    SatContactPointGenerator, closest_points_on_segments, edge_edge_contact, obb_bvh_pair_query,
78    obb_overlap_on_axis, obb_project,
79};
80
81#[cfg(test)]
82mod prop_tests {
83
84    use oxiphysics_core::Transform;
85    use oxiphysics_core::math::Vec3;
86    use oxiphysics_geometry::Sphere;
87    use proptest::prelude::*;
88
89    use crate::broadphase::{BroadPhase, BruteForceBroadPhase};
90    use crate::narrowphase::{Gjk, sphere_sphere};
91    use oxiphysics_core::Aabb;
92
93    fn positive_f64() -> impl Strategy<Value = f64> {
94        0.01_f64..20.0_f64
95    }
96
97    fn coord_f64() -> impl Strategy<Value = f64> {
98        -50.0_f64..50.0_f64
99    }
100
101    fn aabb_strategy() -> impl Strategy<Value = Aabb> {
102        (
103            coord_f64(),
104            coord_f64(),
105            coord_f64(),
106            positive_f64(),
107            positive_f64(),
108            positive_f64(),
109        )
110            .prop_map(|(x, y, z, ex, ey, ez)| {
111                Aabb::new(Vec3::new(x, y, z), Vec3::new(x + ex, y + ey, z + ez))
112            })
113    }
114
115    proptest! {
116        #[test]
117        fn prop_gjk_separated_spheres_no_intersection(
118            r1 in positive_f64(),
119            r2 in positive_f64(),
120            // separation > sum of radii
121            extra_sep in 0.01_f64..10.0_f64,
122        ) {
123            let s1 = Sphere::new(r1);
124            let s2 = Sphere::new(r2);
125            let t1 = Transform::from_position(Vec3::zeros());
126            let sep = r1 + r2 + extra_sep;
127            let t2 = Transform::from_position(Vec3::new(sep, 0.0, 0.0));
128            let intersects = Gjk::intersect(&s1, &t1, &s2, &t2);
129            prop_assert!(!intersects, "separated spheres (r1={}, r2={}, sep={}) reported intersecting", r1, r2, sep);
130        }
131
132        #[test]
133        fn prop_gjk_overlapping_spheres_intersect(
134            r1 in positive_f64(),
135            r2 in positive_f64(),
136            // overlap: distance < sum of radii
137            dist_frac in 0.0_f64..0.99_f64,
138        ) {
139            let s1 = Sphere::new(r1);
140            let s2 = Sphere::new(r2);
141            let t1 = Transform::from_position(Vec3::zeros());
142            let dist = (r1 + r2) * dist_frac;
143            let t2 = Transform::from_position(Vec3::new(dist, 0.0, 0.0));
144            let intersects = Gjk::intersect(&s1, &t1, &s2, &t2);
145            prop_assert!(intersects, "overlapping spheres (r1={}, r2={}, dist={}) not reported intersecting", r1, r2, dist);
146        }
147
148        #[test]
149        fn prop_broadphase_pairs_have_overlapping_aabbs(
150            aabbs in proptest::collection::vec(aabb_strategy(), 1..8),
151        ) {
152            let pairs = BruteForceBroadPhase.find_pairs(&aabbs);
153            for pair in &pairs {
154                let a = &aabbs[pair.a];
155                let b = &aabbs[pair.b];
156                prop_assert!(
157                    a.intersects(b),
158                    "pair ({},{}) reported but AABBs don't overlap", pair.a, pair.b
159                );
160            }
161        }
162
163        #[test]
164        fn prop_contact_normal_unit_length(
165            r1 in positive_f64(),
166            r2 in positive_f64(),
167            dist_frac in 0.01_f64..0.99_f64,
168        ) {
169            let s1 = Sphere::new(r1);
170            let s2 = Sphere::new(r2);
171            let t1 = Transform::from_position(Vec3::zeros());
172            let dist = (r1 + r2) * dist_frac;
173            let t2 = Transform::from_position(Vec3::new(dist, 0.0, 0.0));
174            if let Some(contact) = sphere_sphere(&s1, &t1, &s2, &t2) {
175                let nlen = contact.normal.norm();
176                prop_assert!((nlen - 1.0).abs() < 1e-6, "contact normal length={}", nlen);
177            }
178        }
179
180        #[test]
181        fn prop_contact_depth_non_negative(
182            r1 in positive_f64(),
183            r2 in positive_f64(),
184            dist_frac in 0.01_f64..0.99_f64,
185        ) {
186            let s1 = Sphere::new(r1);
187            let s2 = Sphere::new(r2);
188            let t1 = Transform::from_position(Vec3::zeros());
189            let dist = (r1 + r2) * dist_frac;
190            let t2 = Transform::from_position(Vec3::new(dist, 0.0, 0.0));
191            if let Some(contact) = sphere_sphere(&s1, &t1, &s2, &t2) {
192                prop_assert!(contact.depth >= 0.0, "contact depth={} is negative", contact.depth);
193            }
194        }
195    }
196}
197pub mod recast;
198pub use recast::{RecastBuilder, RecastConfig, RecastError};
199
200pub mod compound_shapes;
201pub mod contact_generation;
202pub mod contact_manifold;
203pub mod deformable_collision;
204pub mod fluid_collision;
205pub mod gjk_enhanced;
206pub mod gjk_extended;
207pub mod joint_collision;
208pub mod kdtree_collision;
209pub mod mesh_collision;
210pub mod parallel_collision;
211pub mod proximity;
212pub mod proximity_query;
213pub mod ray_casting;
214pub mod shape_cast;
215pub mod soft_body_collision;
216pub mod softbody_collision;
217pub mod spatial_queries;
218pub mod terrain_collision;
219pub mod voxel_collision;