parry3d/shape/
segment_pseudo_normals.rs1use crate::math::Vector;
2
3#[cfg(feature = "alloc")]
4use crate::query::details::NormalConstraints;
5
6#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[derive(Clone, Debug)]
15pub struct SegmentPseudoNormals {
16 pub face: Vector,
18 pub edges: [Vector; 2],
20}
21
22#[cfg(feature = "alloc")]
23impl NormalConstraints for SegmentPseudoNormals {
24 fn project_local_normal_mut(&self, dir: &mut Vector) -> bool {
26 let closest_edge = if dir.dot(self.edges[0]) >= dir.dot(self.edges[1]) {
28 self.edges[0]
29 } else {
30 self.edges[1]
31 };
32 crate::shape::pseudo_normals::project_into_cone(self.face, closest_edge, dir)
33 }
34}
35
36#[cfg(test)]
37#[cfg(all(feature = "dim2", feature = "alloc"))]
38mod test {
39 use super::{NormalConstraints, SegmentPseudoNormals};
40 use crate::math::Vector;
41
42 fn bisector(v1: Vector, v2: Vector) -> Vector {
43 (v1 + v2).normalize()
44 }
45
46 #[test]
47 fn degenerate_cone_collapses_to_face() {
48 let pn = SegmentPseudoNormals {
49 face: Vector::Y,
50 edges: [Vector::Y, Vector::Y],
51 };
52
53 assert_eq!(
54 pn.project_local_normal(Vector::new(1.0, 1.0)),
55 Some(Vector::Y)
56 );
57 assert!(pn.project_local_normal(-Vector::Y).is_none());
58 }
59
60 #[test]
61 fn clamps_into_the_outward_cone() {
62 let ends = [
64 bisector(Vector::Y, Vector::X),
65 bisector(Vector::Y, -Vector::X),
66 ];
67 let edges = [bisector(Vector::Y, ends[0]), bisector(Vector::Y, ends[1])];
68 let pn = SegmentPseudoNormals {
69 face: Vector::Y,
70 edges,
71 };
72
73 assert_eq!(pn.project_local_normal(Vector::Y), Some(Vector::Y));
75 for edge in edges {
76 assert_eq!(pn.project_local_normal(edge), Some(edge));
77 }
78 let inside = Vector::new(0.2, 1.0).normalize();
79 assert!(pn
80 .project_local_normal(inside)
81 .unwrap()
82 .abs_diff_eq(inside, 1.0e-5));
83
84 let sideways = Vector::new(1.0, 0.2).normalize();
86 let clamped = pn.project_local_normal(sideways).unwrap();
87 assert!(clamped.dot(Vector::Y) > sideways.dot(Vector::Y));
88 assert!(clamped.abs_diff_eq(ends[0], 1.0e-5));
89
90 assert!(pn.project_local_normal(-Vector::Y).is_none());
92 assert!(pn
93 .project_local_normal(Vector::new(0.3, -1.0).normalize())
94 .is_none());
95 }
96}