parry2d/query/sat/sat_cuboid_point.rs
1use crate::math::{Pose, Real, Vector};
2use crate::shape::{Cuboid, SupportMap};
3
4/// Computes the separation distance between a point and a cuboid along a specified normal direction.
5///
6/// This function is used in SAT (Separating Axis Theorem) implementations for shapes that can be
7/// treated as having a single representative point with an associated normal vector. Examples include:
8/// - Segments (in 2D, using one endpoint and the segment's normal)
9/// - Triangles (in 3D, using one vertex and the triangle's face normal)
10///
11/// # Why This Works
12///
13/// For a cuboid centered at the origin with symmetry, we only need to test one direction of the
14/// normal (not both +normal and -normal) because the cuboid looks the same from both directions.
15/// This optimization makes the function more efficient than the general support map approach.
16///
17/// # Parameters
18///
19/// - `point1`: A point in the first shape's local coordinate space
20/// - `normal1`: Optional unit normal vector associated with the point (e.g., triangle face normal)
21/// - If `None`, the function returns maximum negative separation (indicating overlap)
22/// - `shape2`: The cuboid to test against
23/// - `pos12`: The position of `shape2` (cuboid) relative to `point1`'s coordinate frame
24///
25/// # Returns
26///
27/// A tuple containing:
28/// - `Real`: The separation distance along the normal direction
29/// - **Positive**: The point and cuboid are separated
30/// - **Negative**: The point penetrates the cuboid (or normal is None)
31/// - **Zero**: The point exactly touches the cuboid surface
32/// - `Vector`: The oriented normal direction used for the test (pointing from point toward cuboid)
33///
34/// # Example
35///
36/// ```rust
37/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
38/// use parry2d::shape::Cuboid;
39/// use parry2d::query::sat::point_cuboid_find_local_separating_normal_oneway;
40/// use parry2d::math::{Vector, Pose};
41///
42/// let point = Vector::ZERO;
43/// let normal = Some((Vector::X.normalize()));
44/// let cuboid = Cuboid::new(Vector::new(1.0, 1.0));
45///
46/// // Position cuboid 3 units to the right
47/// let pos12 = Pose::translation(3.0, 0.0);
48///
49/// let (separation, _dir) = point_cuboid_find_local_separating_normal_oneway(
50/// point,
51/// normal,
52/// &cuboid,
53/// &pos12
54/// );
55///
56/// // Should be separated by 1.0 (distance 3.0 - cuboid extent 1.0 - point distance 0.0)
57/// assert!(separation > 0.0);
58/// # }
59/// ```
60///
61/// # Implementation Note
62///
63/// This function only works correctly when the **cuboid is on the right-hand side** (as shape2)
64/// because it exploits the cuboid's symmetry around the origin. The cuboid must be centered at
65/// its local origin for this optimization to be valid.
66pub fn point_cuboid_find_local_separating_normal_oneway(
67 point1: Vector,
68 normal1: Option<Vector>,
69 shape2: &Cuboid,
70 pos12: &Pose,
71) -> (Real, Vector) {
72 let mut best_separation = -Real::MAX;
73 let mut best_dir = Vector::ZERO;
74
75 if let Some(normal1) = normal1 {
76 let axis1 = if (pos12.translation - point1).dot(normal1) >= 0.0 {
77 normal1
78 } else {
79 -normal1
80 };
81
82 let pt2 = shape2.support_point_toward(pos12, -axis1);
83 let separation = (pt2 - point1).dot(axis1);
84
85 if separation > best_separation {
86 best_separation = separation;
87 best_dir = axis1;
88 }
89 }
90
91 (best_separation, best_dir)
92}