parry3d_f64/query/sat/
sat_triangle_segment.rs

1use crate::math::{Pose, Real, Vector};
2use crate::query::sat;
3use crate::shape::{Segment, SupportMap, Triangle};
4
5/// Finds the best separating axis by testing a triangle's face normal against a segment (3D only).
6///
7/// In 3D, a triangle has a face normal (perpendicular to its plane). This function tests both
8/// directions of this normal (+normal and -normal) to find the maximum separation from the segment.
9///
10/// # How It Works
11///
12/// The function computes support points on the segment in both the positive and negative normal
13/// directions, then measures which direction gives greater separation from the triangle's surface.
14///
15/// # Parameters
16///
17/// - `triangle1`: The triangle whose face normal will be tested
18/// - `segment2`: The line segment
19/// - `pos12`: The position of the segment relative to the triangle
20///
21/// # Returns
22///
23/// A tuple containing:
24/// - `Real`: The separation distance along the triangle's face normal
25///   - **Positive**: Shapes are separated
26///   - **Negative**: Shapes are overlapping
27///   - **Very negative** if the triangle has no normal (degenerate triangle)
28/// - `Vector`: The face normal direction (or its negation) that gives this separation
29///
30/// # Degenerate Triangles
31///
32/// If the triangle is degenerate (all three points are collinear), it has no valid normal.
33/// In this case, the function returns `-Real::MAX` for separation and a zero vector for the normal.
34///
35/// # Example
36///
37/// ```rust
38/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
39/// use parry3d::shape::{Triangle, Segment};
40/// use parry3d::query::sat::triangle_segment_find_local_separating_normal_oneway;
41/// use parry3d::math::{Vector, Pose};
42///
43/// // Triangle in the XY plane
44/// let triangle = Triangle::new(
45///     Vector::ZERO,
46///     Vector::new(2.0, 0.0, 0.0),
47///     Vector::new(1.0, 2.0, 0.0)
48/// );
49///
50/// // Vertical segment above the triangle
51/// let segment = Segment::new(
52///     Vector::new(1.0, 1.0, 1.0),
53///     Vector::new(1.0, 1.0, 3.0)
54/// );
55///
56/// let pos12 = Pose::identity();
57///
58/// let (separation, normal) = triangle_segment_find_local_separating_normal_oneway(
59///     &triangle,
60///     &segment,
61///     &pos12
62/// );
63///
64/// if separation > 0.0 {
65///     println!("Separated by {} along triangle normal", separation);
66/// }
67/// # }
68/// ```
69///
70/// # Usage in Complete SAT
71///
72/// For a complete triangle-segment collision test, you must also test:
73/// 1. Triangle face normal (this function)
74/// 2. Segment normal (in 2D) or edge-edge axes (in 3D)
75/// 3. Edge-edge cross products (see [`segment_triangle_find_local_separating_edge_twoway`])
76pub fn triangle_segment_find_local_separating_normal_oneway(
77    triangle1: &Triangle,
78    segment2: &Segment,
79    pos12: &Pose,
80) -> (Real, Vector) {
81    if let Some(dir) = triangle1.normal() {
82        let p2a = segment2.support_point_toward(pos12, -dir);
83        let p2b = segment2.support_point_toward(pos12, dir);
84        let sep_a = (p2a - triangle1.a).dot(dir);
85        let sep_b = -(p2b - triangle1.a).dot(dir);
86
87        if sep_a >= sep_b {
88            (sep_a, dir)
89        } else {
90            (sep_b, -dir)
91        }
92    } else {
93        (-Real::MAX, Vector::ZERO)
94    }
95}
96
97/// Finds the best separating axis by testing edge-edge combinations between a segment and a triangle (3D only).
98///
99/// In 3D, when a line segment and triangle collide, the contact might occur along an axis
100/// perpendicular to both the segment and one of the triangle's edges. This function tests all
101/// such axes (cross products) to find the one with maximum separation.
102///
103/// # Parameters
104///
105/// - `segment1`: The line segment
106/// - `triangle2`: The triangle
107/// - `pos12`: The position of the triangle relative to the segment
108///
109/// # Returns
110///
111/// A tuple containing:
112/// - `Real`: The maximum separation found across all edge-edge axes
113///   - **Positive**: Shapes are separated
114///   - **Negative**: Shapes are overlapping
115/// - `Vector`: The axis direction that gives this separation
116///
117/// # The Axes Tested
118///
119/// The function computes cross products between:
120/// - The segment's direction (B - A)
121/// - Each of the 3 triangle edges (AB, BC, CA)
122///
123/// This gives 3 base axes. The function tests both each axis and its negation (6 total),
124/// finding which gives the maximum separation.
125///
126/// # Example
127///
128/// ```rust
129/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
130/// use parry3d::shape::{Segment, Triangle};
131/// use parry3d::query::sat::segment_triangle_find_local_separating_edge_twoway;
132/// use parry3d::math::{Vector, Pose};
133///
134/// let segment = Segment::new(
135///     Vector::ZERO,
136///     Vector::new(0.0, 0.0, 2.0)
137/// );
138///
139/// let triangle = Triangle::new(
140///     Vector::new(1.0, 0.0, 1.0),
141///     Vector::new(3.0, 0.0, 1.0),
142///     Vector::new(2.0, 2.0, 1.0)
143/// );
144///
145/// let pos12 = Pose::identity();
146///
147/// let (separation, axis) = segment_triangle_find_local_separating_edge_twoway(
148///     &segment,
149///     &triangle,
150///     &pos12
151/// );
152///
153/// if separation > 0.0 {
154///     println!("Separated by {} along edge-edge axis", separation);
155/// }
156/// # }
157/// ```
158///
159/// # Implementation Details
160///
161/// - Axes with near-zero length (parallel edges) are skipped
162/// - The function uses [`support_map_support_map_compute_separation`](super::support_map_support_map_compute_separation)
163///   to compute the actual separation along each axis
164/// - Both positive and negative directions are tested for each cross product
165///
166/// # Usage in Complete SAT
167///
168/// For a complete segment-triangle collision test, you must also test:
169/// 1. Triangle face normal ([`triangle_segment_find_local_separating_normal_oneway`])
170/// 2. Segment-specific axes (depends on whether the segment has associated normals)
171/// 3. Edge-edge cross products (this function)
172pub fn segment_triangle_find_local_separating_edge_twoway(
173    segment1: &Segment,
174    triangle2: &Triangle,
175    pos12: &Pose,
176) -> (Real, Vector) {
177    let x2 = pos12.rotation * (triangle2.b - triangle2.a);
178    let y2 = pos12.rotation * (triangle2.c - triangle2.b);
179    let z2 = pos12.rotation * (triangle2.a - triangle2.c);
180    let dir1 = segment1.scaled_direction();
181
182    let crosses1 = [dir1.cross(x2), dir1.cross(y2), dir1.cross(z2)];
183    let axes1 = [
184        crosses1[0],
185        crosses1[1],
186        crosses1[2],
187        -crosses1[0],
188        -crosses1[1],
189        -crosses1[2],
190    ];
191    let mut max_separation = -Real::MAX;
192    let mut sep_dir = axes1[0];
193
194    for axis1 in &axes1 {
195        if let Some(axis1) = (*axis1).try_normalize() {
196            let sep =
197                sat::support_map_support_map_compute_separation(segment1, triangle2, pos12, axis1);
198
199            if sep > max_separation {
200                max_separation = sep;
201                sep_dir = axis1;
202            }
203        }
204    }
205
206    (max_separation, sep_dir)
207}