ncollide2d/shape/
convex_polygon.rs

1use crate::math::{Isometry, Point, Vector};
2use crate::shape::{ConvexPolygonalFeature, ConvexPolyhedron, FeatureId, SupportMap};
3use crate::transformation;
4use crate::utils;
5use na::{self, RealField, Unit};
6use std::f64;
7
8/// A 2D convex polygon.
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10#[derive(Clone, Debug)]
11pub struct ConvexPolygon<N: RealField + Copy> {
12    points: Vec<Point<N>>,
13    normals: Vec<Unit<Vector<N>>>,
14}
15
16impl<N: RealField + Copy> ConvexPolygon<N> {
17    /// Creates a new 2D convex polygon from an arbitrary set of points.
18    ///
19    /// This explicitly computes the convex hull of the given set of points. Use
20    /// Returns `None` if the convex hull computation failed.
21    pub fn try_from_points(points: &[Point<N>]) -> Option<Self> {
22        let hull = transformation::convex_hull(points);
23        let mut vertices = hull.unwrap().0;
24        vertices.reverse(); // FIXME: it is unfortunate to have to do this reverse.
25
26        Self::try_new(vertices)
27    }
28
29    /// Creates a new 2D convex polygon from a set of points assumed to describe a counter-clockwise convex polyline.
30    ///
31    /// Convexity of the input polyline is not checked.
32    /// Returns `None` if some consecutive points are identical (or too close to being so).
33    pub fn try_new(mut points: Vec<Point<N>>) -> Option<Self> {
34        let eps = N::default_epsilon().sqrt();
35        let mut normals = Vec::with_capacity(points.len());
36
37        // First, compute all normals.
38        for i1 in 0..points.len() {
39            let i2 = (i1 + 1) % points.len();
40            normals.push(utils::ccw_face_normal([&points[i1], &points[i2]])?);
41        }
42
43        let mut nremoved = 0;
44        // See if the first vexrtex must be removed.
45        if normals[0].dot(&*normals[normals.len() - 1]) > N::one() - eps {
46            nremoved = 1;
47        }
48
49        // Second, find vertices that can be removed because
50        // of collinearity of adjascent faces.
51        for i2 in 1..points.len() {
52            let i1 = i2 - 1;
53            if normals[i1].dot(&*normals[i2]) > N::one() - eps {
54                // Remove
55                nremoved += 1;
56            } else {
57                points[i2 - nremoved] = points[i2];
58                normals[i2 - nremoved] = normals[i2];
59            }
60        }
61
62        let new_length = points.len() - nremoved;
63        points.truncate(new_length);
64        normals.truncate(new_length);
65
66        if points.len() != 0 {
67            Some(ConvexPolygon { points, normals })
68        } else {
69            None
70        }
71    }
72
73    /// The vertices of this convex polygon.
74    #[inline]
75    pub fn points(&self) -> &[Point<N>] {
76        &self.points
77    }
78
79    /// The normals of the edges of this convex polygon.
80    #[inline]
81    pub fn normals(&self) -> &[Unit<Vector<N>>] {
82        &self.normals
83    }
84
85    /// Checks that the given direction in world-space is on the tangent cone of the given `feature`.
86    pub fn tangent_cone_contains_dir(
87        &self,
88        feature: FeatureId,
89        m: &Isometry<N>,
90        dir: &Unit<Vector<N>>,
91    ) -> bool {
92        let local_dir = m.inverse_transform_unit_vector(dir);
93
94        match feature {
95            FeatureId::Face(id) => self.normals[id].dot(&local_dir) <= N::zero(),
96            FeatureId::Vertex(id2) => {
97                let id1 = if id2 == 0 {
98                    self.normals.len() - 1
99                } else {
100                    id2 - 1
101                };
102
103                self.normals[id1].dot(&local_dir) <= N::zero()
104                    && self.normals[id2].dot(&local_dir) <= N::zero()
105            }
106            _ => unreachable!(),
107        }
108    }
109}
110
111impl<N: RealField + Copy> SupportMap<N> for ConvexPolygon<N> {
112    #[inline]
113    fn local_support_point(&self, dir: &Vector<N>) -> Point<N> {
114        utils::point_cloud_support_point(dir, self.points())
115    }
116}
117
118impl<N: RealField + Copy> ConvexPolyhedron<N> for ConvexPolygon<N> {
119    fn vertex(&self, id: FeatureId) -> Point<N> {
120        self.points[id.unwrap_vertex()]
121    }
122
123    fn face(&self, id: FeatureId, out: &mut ConvexPolygonalFeature<N>) {
124        out.clear();
125
126        let ia = id.unwrap_face();
127        let ib = (ia + 1) % self.points.len();
128        out.push(self.points[ia], FeatureId::Vertex(ia));
129        out.push(self.points[ib], FeatureId::Vertex(ib));
130
131        out.set_normal(self.normals[ia]);
132        out.set_feature_id(FeatureId::Face(ia));
133    }
134
135    fn feature_normal(&self, feature: FeatureId) -> Unit<Vector<N>> {
136        match feature {
137            FeatureId::Face(id) => self.normals[id],
138            FeatureId::Vertex(id2) => {
139                let id1 = if id2 == 0 {
140                    self.normals.len() - 1
141                } else {
142                    id2 - 1
143                };
144                Unit::new_normalize(*self.normals[id1] + *self.normals[id2])
145            }
146            _ => panic!("Invalid feature ID: {:?}", feature),
147        }
148    }
149
150    fn support_face_toward(
151        &self,
152        m: &Isometry<N>,
153        dir: &Unit<Vector<N>>,
154        out: &mut ConvexPolygonalFeature<N>,
155    ) {
156        let ls_dir = m.inverse_transform_vector(dir);
157        let mut best_face = 0;
158        let mut max_dot = self.normals[0].dot(&ls_dir);
159
160        for i in 1..self.points.len() {
161            let dot = self.normals[i].dot(&ls_dir);
162
163            if dot > max_dot {
164                max_dot = dot;
165                best_face = i;
166            }
167        }
168
169        self.face(FeatureId::Face(best_face), out);
170        out.transform_by(m);
171    }
172
173    fn support_feature_toward(
174        &self,
175        transform: &Isometry<N>,
176        dir: &Unit<Vector<N>>,
177        _angle: N,
178        out: &mut ConvexPolygonalFeature<N>,
179    ) {
180        out.clear();
181        // FIXME: actualy find the support feature.
182        self.support_face_toward(transform, dir, out)
183    }
184
185    fn support_feature_id_toward(&self, local_dir: &Unit<Vector<N>>) -> FeatureId {
186        let eps: N = na::convert(f64::consts::PI / 180.0);
187        let ceps = eps.cos();
188
189        // Check faces.
190        for i in 0..self.normals.len() {
191            let normal = &self.normals[i];
192
193            if normal.dot(local_dir.as_ref()) >= ceps {
194                return FeatureId::Face(i);
195            }
196        }
197
198        // Support vertex.
199        FeatureId::Vertex(utils::point_cloud_support_point_id(
200            local_dir.as_ref(),
201            &self.points,
202        ))
203    }
204}