ncollide2d/shape/
convex_polygon.rs1use 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#[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 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(); Self::try_new(vertices)
27 }
28
29 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 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 if normals[0].dot(&*normals[normals.len() - 1]) > N::one() - eps {
46 nremoved = 1;
47 }
48
49 for i2 in 1..points.len() {
52 let i1 = i2 - 1;
53 if normals[i1].dot(&*normals[i2]) > N::one() - eps {
54 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 #[inline]
75 pub fn points(&self) -> &[Point<N>] {
76 &self.points
77 }
78
79 #[inline]
81 pub fn normals(&self) -> &[Unit<Vector<N>>] {
82 &self.normals
83 }
84
85 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 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 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 FeatureId::Vertex(utils::point_cloud_support_point_id(
200 local_dir.as_ref(),
201 &self.points,
202 ))
203 }
204}