ncollide3d/shape/
convex_polygonal_feature3.rs

1use na::{self, Point2, RealField, Unit};
2use std::iter;
3
4use crate::math::{Isometry, Point, Vector};
5use crate::query::ContactPreprocessor;
6use crate::query::{
7    self, Contact, ContactKinematic, ContactManifold, ContactPrediction, NeighborhoodGeometry,
8};
9use crate::shape::{FeatureId, Segment, SegmentPointLocation};
10use crate::utils;
11
12/// A cache used for polygonal clipping.
13#[derive(Clone)]
14pub struct ClippingCache<N: RealField + Copy> {
15    poly1: Vec<Point2<N>>,
16    poly2: Vec<Point2<N>>,
17}
18
19impl<N: RealField + Copy> ClippingCache<N> {
20    /// Initializes an empty clipping cache.
21    pub fn new() -> Self {
22        ClippingCache {
23            poly1: Vec::with_capacity(4),
24            poly2: Vec::with_capacity(4),
25        }
26    }
27
28    /// Clears the clipping cache.
29    pub fn clear(&mut self) {
30        self.poly1.clear();
31        self.poly2.clear();
32    }
33}
34
35/// Represents a convex polygonal approximation of a face of a solid.
36///
37/// It is never checked if the vertices actually form a convex polygon.
38/// If they do not, results of any geometric query may end up being invalid.
39#[derive(Clone, Debug)]
40pub struct ConvexPolygonalFeature<N: RealField + Copy> {
41    // FIXME: don't keep all those public.
42    /// The vertices of this face.
43    pub vertices: Vec<Point<N>>,
44    /// The outward normal of the edges if it is a face.
45    pub edge_normals: Vec<Vector<N>>,
46    /// The normal of this feature if it is a face.
47    pub normal: Option<Unit<Vector<N>>>,
48    /// The shape-dependent identifier of this feature.
49    pub feature_id: FeatureId,
50    /// The shape-dependent indentifier of each vertex of this feature.
51    pub vertices_id: Vec<FeatureId>,
52    /// The shape-dependent indentifier of each edge of this feature.
53    pub edges_id: Vec<FeatureId>,
54}
55
56impl<N: RealField + Copy> ConvexPolygonalFeature<N> {
57    /// Creates a new empty convex polygonal faces.
58    pub fn new() -> Self {
59        ConvexPolygonalFeature {
60            vertices: Vec::new(),
61            edge_normals: Vec::new(),
62            normal: None,
63            feature_id: FeatureId::Unknown,
64            vertices_id: Vec::new(),
65            edges_id: Vec::new(),
66        }
67    }
68
69    /// Creates a new convex polygonal feature with all field initialized with `n` zero elements.
70    pub fn with_size(n: usize) -> Self {
71        ConvexPolygonalFeature {
72            vertices: iter::repeat(Point::origin()).take(n).collect(),
73            edge_normals: iter::repeat(Vector::zeros()).take(n).collect(),
74            normal: None,
75            feature_id: FeatureId::Unknown,
76            vertices_id: iter::repeat(FeatureId::Unknown).take(n).collect(),
77            edges_id: iter::repeat(FeatureId::Unknown).take(n).collect(),
78        }
79    }
80
81    /// Removes all the vertices, normals, and feature IDs of this feature.
82    pub fn clear(&mut self) {
83        self.vertices.clear();
84        self.edge_normals.clear();
85        self.vertices_id.clear();
86        self.edges_id.clear();
87        self.normal = None;
88        self.feature_id = FeatureId::Unknown;
89    }
90
91    /// Transforms all the vertices and normals of this feature by the given isometry.
92    pub fn transform_by(&mut self, m: &Isometry<N>) {
93        for p in &mut self.vertices {
94            *p = m * *p;
95        }
96
97        for n in &mut self.edge_normals {
98            *n = m * *n;
99        }
100
101        if let Some(ref mut n) = self.normal {
102            *n = m * *n;
103        }
104    }
105
106    /// Adds a vertex to this face.
107    ///
108    /// It is not checked whether `pt` breaks the convexity of the polyhedral face.
109    pub fn push(&mut self, pt: Point<N>, id: FeatureId) {
110        self.vertices.push(pt);
111        self.vertices_id.push(id);
112    }
113
114    /// The number of vertices of this face.
115    pub fn nvertices(&self) -> usize {
116        self.vertices.len()
117    }
118
119    /// The vertices of this convex polygonal face.
120    pub fn vertices(&self) -> &[Point<N>] {
121        &self.vertices[..]
122    }
123
124    /// The number of edges of this convex polygonal face.
125    pub fn nedges(&self) -> usize {
126        match self.vertices.len() {
127            1 => 0,
128            2 => 1,
129            l => l,
130        }
131    }
132
133    /// Retrieves the edge with the given feature id.
134    pub fn edge(&self, edge_id: FeatureId) -> Option<Segment<N>> {
135        for i1 in 0..self.vertices.len() {
136            if self.edges_id[i1] == edge_id {
137                let i2 = (i1 + 1) % self.vertices.len();
138                return Some(Segment::new(self.vertices[i1], self.vertices[i2]));
139            }
140        }
141
142        None
143    }
144
145    /// Adds a scaled edge normal to this face.
146    pub fn push_scaled_edge_normal(&mut self, normal: Vector<N>) {
147        if let Some(normal) = normal.try_normalize(N::default_epsilon()) {
148            self.edge_normals.push(normal)
149        } else {
150            self.edge_normals.push(na::zero())
151        }
152    }
153
154    /// Adds an edge normal to this face.
155    pub fn push_edge_normal(&mut self, normal: Unit<Vector<N>>) {
156        self.edge_normals.push(normal.into_inner())
157    }
158
159    /// Automatically recomputes the scaled edge normals (3D only).
160    ///
161    /// Panics if the ambient space is not 3D.
162    pub fn recompute_edge_normals(&mut self) {
163        self.edge_normals.clear();
164
165        for i1 in 0..self.vertices.len() {
166            let i2 = (i1 + 1) % self.vertices.len();
167            let dpt = self.vertices[i2] - self.vertices[i1];
168            let scaled_normal = dpt.cross(
169                self.normal
170                    .as_ref()
171                    .expect("The face normal must be set before computing edge normals."),
172            );
173            self.push_scaled_edge_normal(scaled_normal)
174        }
175    }
176
177    /// Transforms all the vertices of this feature by the given isometry.
178    pub fn project_point(&self, pt: &Point<N>) -> Option<Contact<N>> {
179        if let Some(n) = self.normal {
180            let dpt = *pt - self.vertices[0];
181            let dist = n.dot(&dpt);
182            let proj = *pt + (-n.into_inner() * dist);
183
184            for i in 0..self.edge_normals.len() {
185                let dpt = proj - self.vertices[i];
186
187                if dpt.dot(&self.edge_normals[i]) > na::zero() {
188                    return None;
189                }
190            }
191
192            Some(Contact::new(proj, *pt, n, -dist))
193        } else {
194            None
195        }
196    }
197
198    /// Sets the outward normal of this convex polygonal face.
199    pub fn set_normal(&mut self, normal: Unit<Vector<N>>) {
200        self.normal = Some(normal)
201    }
202
203    /// Add the shape-dependent identifier of a edge of this feature (if it is a face).
204    pub fn push_edge_feature_id(&mut self, id: FeatureId) {
205        self.edges_id.push(id)
206    }
207
208    /// Add the shape-dependent identifier of this feature.
209    pub fn set_feature_id(&mut self, id: FeatureId) {
210        self.feature_id = id
211    }
212
213    /// Generate contacts between `self` and `other` using polygonal clipping, iif. they both have at least
214    /// three vertices.
215    ///
216    /// If either `self` or `other` has less than three vertices, this does nothing.
217    pub fn clip(
218        &self,
219        other: &Self,
220        normal: &Unit<Vector<N>>,
221        prediction: &ContactPrediction<N>,
222        cache: &mut ClippingCache<N>,
223        out: &mut Vec<(Contact<N>, FeatureId, FeatureId)>,
224    ) {
225        // FIXME: don't compute contacts further than the prediction.
226
227        cache.clear();
228
229        // FIXME: lift this restriction.
230        if self.vertices.len() <= 2 && other.vertices.len() <= 2 {
231            return;
232        }
233
234        // In 3D we may end up with more than two points.
235        let mut basis = [na::zero(), na::zero()];
236        let mut basis_i = 0;
237
238        Vector::orthonormal_subspace_basis(&[normal.into_inner()], |dir| {
239            basis[basis_i] = *dir;
240            basis_i += 1;
241            true
242        });
243
244        let ref_pt = self.vertices[0];
245
246        for pt in &self.vertices {
247            let dpt = *pt - ref_pt;
248            let coords = Point2::new(basis[0].dot(&dpt), basis[1].dot(&dpt));
249            cache.poly1.push(coords);
250        }
251
252        for pt in &other.vertices {
253            let dpt = *pt - ref_pt;
254            let coords = Point2::new(basis[0].dot(&dpt), basis[1].dot(&dpt));
255            cache.poly2.push(coords);
256        }
257
258        if cache.poly2.len() > 2 {
259            for i in 0..cache.poly1.len() {
260                let pt = &cache.poly1[i];
261
262                if utils::point_in_poly2d(pt, &cache.poly2) {
263                    let origin = ref_pt + basis[0] * pt.x + basis[1] * pt.y;
264
265                    let n2 = other.normal.as_ref().unwrap().into_inner();
266                    let p2 = &other.vertices[0];
267                    if let Some(toi2) =
268                        query::line_toi_with_plane(p2, &n2, &origin, &normal.into_inner())
269                    {
270                        let world2 = origin + normal.into_inner() * toi2;
271                        let world1 = self.vertices[i];
272                        let f2 = other.feature_id;
273                        let f1 = self.vertices_id[i];
274                        let contact = Contact::new_wo_depth(world1, world2, *normal);
275
276                        if -contact.depth <= prediction.linear() {
277                            out.push((contact, f1, f2));
278                        }
279                    }
280                }
281            }
282        }
283
284        if cache.poly1.len() > 2 {
285            for i in 0..cache.poly2.len() {
286                let pt = &cache.poly2[i];
287
288                if utils::point_in_poly2d(pt, &cache.poly1) {
289                    let origin = ref_pt + basis[0] * pt.x + basis[1] * pt.y;
290
291                    let n1 = self.normal.as_ref().unwrap().into_inner();
292                    let p1 = &self.vertices[0];
293                    if let Some(toi1) =
294                        query::line_toi_with_plane(p1, &n1, &origin, &normal.into_inner())
295                    {
296                        let world1 = origin + normal.into_inner() * toi1;
297                        let world2 = other.vertices[i];
298                        let f1 = self.feature_id;
299                        let f2 = other.vertices_id[i];
300                        let contact = Contact::new_wo_depth(world1, world2, *normal);
301
302                        if -contact.depth <= prediction.linear() {
303                            out.push((contact, f1, f2));
304                        }
305                    }
306                }
307            }
308        }
309
310        let nedges1 = self.nedges();
311        let nedges2 = other.nedges();
312
313        for i1 in 0..nedges1 {
314            let j1 = (i1 + 1) % cache.poly1.len();
315            let seg1 = (&cache.poly1[i1], &cache.poly1[j1]);
316
317            for i2 in 0..nedges2 {
318                let j2 = (i2 + 1) % cache.poly2.len();
319                let seg2 = (&cache.poly2[i2], &cache.poly2[j2]);
320
321                if let (SegmentPointLocation::OnEdge(e1), SegmentPointLocation::OnEdge(e2)) =
322                    query::closest_points_segment_segment_with_locations_nD(seg1, seg2)
323                {
324                    let original1 = Segment::new(self.vertices[i1], self.vertices[j1]);
325                    let original2 = Segment::new(other.vertices[i2], other.vertices[j2]);
326                    let world1 = original1.point_at(&SegmentPointLocation::OnEdge(e1));
327                    let world2 = original2.point_at(&SegmentPointLocation::OnEdge(e2));
328                    let f1 = self.edges_id[i1];
329                    let f2 = other.edges_id[i2];
330                    let contact = Contact::new_wo_depth(world1, world2, *normal);
331
332                    if -contact.depth <= prediction.linear() {
333                        out.push((contact, f1, f2));
334                    }
335                }
336            }
337        }
338    }
339
340    /// Given a contact between two polygonal features, adds it to a contact manifold.
341    pub fn add_contact_to_manifold(
342        &self,
343        other: &Self,
344        c: Contact<N>,
345        m1: &Isometry<N>,
346        f1: FeatureId,
347        proc1: Option<&dyn ContactPreprocessor<N>>,
348        m2: &Isometry<N>,
349        f2: FeatureId,
350        proc2: Option<&dyn ContactPreprocessor<N>>,
351        manifold: &mut ContactManifold<N>,
352    ) {
353        let mut kinematic = ContactKinematic::new();
354        let local1 = m1.inverse_transform_point(&c.world1);
355        let local2 = m2.inverse_transform_point(&c.world2);
356
357        match f1 {
358            FeatureId::Face(..) => kinematic.set_approx1(
359                f1,
360                local1,
361                NeighborhoodGeometry::Plane(
362                    m1.inverse_transform_unit_vector(self.normal.as_ref().unwrap()),
363                ),
364            ),
365            FeatureId::Edge(..) => {
366                let e1 = self.edge(f1).expect("Invalid edge id.");
367                if let Some(dir1) = e1.direction() {
368                    let local_dir1 = m1.inverse_transform_unit_vector(&dir1);
369                    let approx1 = NeighborhoodGeometry::Line(local_dir1);
370                    kinematic.set_approx1(f1, local1, approx1)
371                } else {
372                    return;
373                }
374            }
375            FeatureId::Vertex(..) => kinematic.set_approx1(f1, local1, NeighborhoodGeometry::Point),
376            FeatureId::Unknown => return,
377        }
378
379        match f2 {
380            FeatureId::Face(..) => {
381                let approx2 = NeighborhoodGeometry::Plane(
382                    m2.inverse_transform_unit_vector(&other.normal.as_ref().unwrap()),
383                );
384                kinematic.set_approx2(f2, local2, approx2)
385            }
386            FeatureId::Edge(..) => {
387                let e2 = other.edge(f2).expect("Invalid edge id.");
388                if let Some(dir2) = e2.direction() {
389                    let local_dir2 = m2.inverse_transform_unit_vector(&dir2);
390                    let approx2 = NeighborhoodGeometry::Line(local_dir2);
391                    kinematic.set_approx2(f2, local2, approx2)
392                } else {
393                    return;
394                }
395            }
396            FeatureId::Vertex(..) => kinematic.set_approx2(f2, local2, NeighborhoodGeometry::Point),
397            FeatureId::Unknown => return,
398        }
399
400        let _ = manifold.push(c, kinematic, local1, proc1, proc2);
401    }
402}