parry2d/shape/
polygonal_feature_map.rs

1use crate::math::Vector;
2#[cfg(feature = "dim3")]
3use crate::shape::{Cone, Cylinder, PackedFeatureId};
4use crate::shape::{Cuboid, PolygonalFeature, Segment, SupportMap, Triangle};
5
6/// Trait implemented by convex shapes with features with polyhedral approximations.
7pub trait PolygonalFeatureMap: SupportMap {
8    /// Compute the support polygonal face of `self` towards the `dir`.
9    fn local_support_feature(&self, dir: Vector, out_feature: &mut PolygonalFeature);
10
11    // TODO: this is currently just a workaround for https://github.com/dimforge/rapier/issues/417
12    //       until we get a better way to deal with the issue without breaking internal edges
13    //       handling.
14    /// Is this shape a `ConvexPolyhedron`?
15    fn is_convex_polyhedron(&self) -> bool {
16        false
17    }
18}
19
20impl PolygonalFeatureMap for Segment {
21    fn local_support_feature(&self, _: Vector, out_feature: &mut PolygonalFeature) {
22        *out_feature = PolygonalFeature::from(*self);
23    }
24}
25
26impl PolygonalFeatureMap for Triangle {
27    fn local_support_feature(&self, dir: Vector, out_feature: &mut PolygonalFeature) {
28        *out_feature = self.support_face(dir);
29    }
30}
31
32impl PolygonalFeatureMap for Cuboid {
33    fn local_support_feature(&self, dir: Vector, out_feature: &mut PolygonalFeature) {
34        *out_feature = self.support_face(dir);
35    }
36}
37
38#[cfg(feature = "dim3")]
39impl PolygonalFeatureMap for Cylinder {
40    fn local_support_feature(&self, dir: Vector, out_features: &mut PolygonalFeature) {
41        use crate::math::Vector2;
42
43        // About feature ids.
44        // At all times, we consider our cylinder to be approximated as follows:
45        // - The curved part is approximated by a single segment.
46        // - Each flat cap of the cylinder is approximated by a square.
47        // - The curved-part segment has a feature ID of 0, and its endpoint with negative
48        //   `y` coordinate has an ID of 1.
49        // - The bottom cap has its vertices with feature ID of 1,3,5,7 (in counter-clockwise order
50        //   when looking at the cap with an eye looking towards +y).
51        // - The bottom cap has its four edge feature IDs of 2,4,6,8, in counter-clockwise order.
52        // - The bottom cap has its face feature ID of 9.
53        // - The feature IDs of the top cap are the same as the bottom cap to which we add 10.
54        //   So its vertices have IDs 11,13,15,17, its edges 12,14,16,18, and its face 19.
55        // - Note that at all times, one of each cap's vertices are the same as the curved-part
56        //   segment endpoints.
57        let dir2 = Vector2::new(dir.x, dir.z)
58            .try_normalize()
59            .unwrap_or(Vector2::X);
60
61        if dir.y.abs() < 0.5 {
62            // We return a segment lying on the cylinder's curved part.
63            out_features.vertices[0] = Vector::new(
64                dir2.x * self.radius,
65                -self.half_height,
66                dir2.y * self.radius,
67            );
68            out_features.vertices[1] =
69                Vector::new(dir2.x * self.radius, self.half_height, dir2.y * self.radius);
70            out_features.eids = PackedFeatureId::edges([0, 0, 0, 0]);
71            out_features.fid = PackedFeatureId::face(0);
72            out_features.num_vertices = 2;
73            out_features.vids = PackedFeatureId::vertices([1, 11, 11, 11]);
74        } else {
75            // We return a square approximation of the cylinder cap.
76            let y = self.half_height.copysign(dir.y);
77            out_features.vertices[0] = Vector::new(dir2.x * self.radius, y, dir2.y * self.radius);
78            out_features.vertices[1] = Vector::new(-dir2.y * self.radius, y, dir2.x * self.radius);
79            out_features.vertices[2] = Vector::new(-dir2.x * self.radius, y, -dir2.y * self.radius);
80            out_features.vertices[3] = Vector::new(dir2.y * self.radius, y, -dir2.x * self.radius);
81
82            if dir.y < 0.0 {
83                out_features.eids = PackedFeatureId::edges([2, 4, 6, 8]);
84                out_features.fid = PackedFeatureId::face(9);
85                out_features.num_vertices = 4;
86                out_features.vids = PackedFeatureId::vertices([1, 3, 5, 7]);
87            } else {
88                out_features.eids = PackedFeatureId::edges([12, 14, 16, 18]);
89                out_features.fid = PackedFeatureId::face(19);
90                out_features.num_vertices = 4;
91                out_features.vids = PackedFeatureId::vertices([11, 13, 15, 17]);
92            }
93        }
94    }
95}
96
97#[cfg(feature = "dim3")]
98impl PolygonalFeatureMap for Cone {
99    fn local_support_feature(&self, dir: Vector, out_features: &mut PolygonalFeature) {
100        use crate::math::Vector2;
101
102        // About feature ids. It is very similar to the feature ids of cylinders.
103        // At all times, we consider our cone to be approximated as follows:
104        // - The curved part is approximated by a single segment.
105        // - The flat cap of the cone is approximated by a square.
106        // - The curved-part segment has a feature ID of 0, and its endpoint with negative
107        //   `y` coordinate has an ID of 1.
108        // - The bottom cap has its vertices with feature ID of 1,3,5,7 (in counter-clockwise order
109        //   when looking at the cap with an eye looking towards +y).
110        // - The bottom cap has its four edge feature IDs of 2,4,6,8, in counter-clockwise order.
111        // - The bottom cap has its face feature ID of 9.
112        // - Note that at all times, one of the cap's vertices are the same as the curved-part
113        //   segment endpoints.
114        let dir2 = Vector2::new(dir.x, dir.z)
115            .try_normalize()
116            .unwrap_or(Vector2::X);
117
118        if dir.y > 0.0 {
119            // We return a segment lying on the cone's curved part.
120            out_features.vertices[0] = Vector::new(
121                dir2.x * self.radius,
122                -self.half_height,
123                dir2.y * self.radius,
124            );
125            out_features.vertices[1] = Vector::new(0.0, self.half_height, 0.0);
126            out_features.eids = PackedFeatureId::edges([0, 0, 0, 0]);
127            out_features.fid = PackedFeatureId::face(0);
128            out_features.num_vertices = 2;
129            out_features.vids = PackedFeatureId::vertices([1, 11, 11, 11]);
130        } else {
131            // We return a square approximation of the cone cap.
132            let y = -self.half_height;
133            out_features.vertices[0] = Vector::new(dir2.x * self.radius, y, dir2.y * self.radius);
134            out_features.vertices[1] = Vector::new(-dir2.y * self.radius, y, dir2.x * self.radius);
135            out_features.vertices[2] = Vector::new(-dir2.x * self.radius, y, -dir2.y * self.radius);
136            out_features.vertices[3] = Vector::new(dir2.y * self.radius, y, -dir2.x * self.radius);
137
138            out_features.eids = PackedFeatureId::edges([2, 4, 6, 8]);
139            out_features.fid = PackedFeatureId::face(9);
140            out_features.num_vertices = 4;
141            out_features.vids = PackedFeatureId::vertices([1, 3, 5, 7]);
142        }
143    }
144}