ncollide3d/shape/
segment.rs

1//! Definition of the segment shape.
2
3use crate::math::{Isometry, Point, Vector};
4use crate::shape::{ConvexPolygonalFeature, ConvexPolyhedron, FeatureId, SupportMap};
5#[cfg(feature = "dim2")]
6use crate::utils;
7use na::{self, RealField, Unit};
8use std::f64;
9use std::mem;
10
11/// A segment shape.
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13#[repr(C)]
14#[derive(PartialEq, Debug, Copy, Clone)]
15pub struct Segment<N: RealField + Copy> {
16    /// The segment first point.
17    pub a: Point<N>,
18    /// The segment second point.
19    pub b: Point<N>,
20}
21
22/// Logical description of the location of a point on a triangle.
23#[derive(PartialEq, Debug, Clone, Copy)]
24pub enum SegmentPointLocation<N: RealField + Copy> {
25    /// The point lies on a vertex.
26    OnVertex(usize),
27    /// The point lies on the segment interior.
28    OnEdge([N; 2]),
29}
30
31impl<N: RealField + Copy> SegmentPointLocation<N> {
32    /// The barycentric coordinates corresponding to this point location.
33    pub fn barycentric_coordinates(&self) -> [N; 2] {
34        let mut bcoords = [N::zero(); 2];
35
36        match self {
37            SegmentPointLocation::OnVertex(i) => bcoords[*i] = N::one(),
38            SegmentPointLocation::OnEdge(uv) => {
39                bcoords[0] = uv[0];
40                bcoords[1] = uv[1];
41            }
42        }
43
44        bcoords
45    }
46}
47
48impl<N: RealField + Copy> Segment<N> {
49    /// Creates a new segment from two points.
50    #[inline]
51    pub fn new(a: Point<N>, b: Point<N>) -> Segment<N> {
52        Segment { a, b }
53    }
54
55    /// Creates the reference to a segment from the reference to an array of two points.
56    pub fn from_array(arr: &[Point<N>; 2]) -> &Segment<N> {
57        unsafe { mem::transmute(arr) }
58    }
59}
60
61impl<N: RealField + Copy> Segment<N> {
62    /// The first point of this segment.
63    #[inline]
64    #[deprecated(note = "use the `self.a` public field directly.")]
65    pub fn a(&self) -> &Point<N> {
66        &self.a
67    }
68
69    /// The second point of this segment.
70    #[inline]
71    #[deprecated(note = "use the `self.b` public field directly.")]
72    pub fn b(&self) -> &Point<N> {
73        &self.b
74    }
75}
76
77impl<N: RealField + Copy> Segment<N> {
78    /// The direction of this segment scaled by its length.
79    ///
80    /// Points from `self.a` toward `self.b`.
81    pub fn scaled_direction(&self) -> Vector<N> {
82        self.b - self.a
83    }
84
85    /// The length of this segment.
86    pub fn length(&self) -> N {
87        self.scaled_direction().norm()
88    }
89
90    /// Swaps the two vertices of this segment.
91    pub fn swap(&mut self) {
92        mem::swap(&mut self.a, &mut self.b)
93    }
94
95    /// The unit direction of this segment.
96    ///
97    /// Points from `self.a()` toward `self.b()`.
98    /// Returns `None` is both points are equal.
99    pub fn direction(&self) -> Option<Unit<Vector<N>>> {
100        Unit::try_new(self.scaled_direction(), N::default_epsilon())
101    }
102
103    /// In 2D, the not-normalized counterclockwise normal of this segment.
104    #[cfg(feature = "dim2")]
105    pub fn scaled_normal(&self) -> Vector<N> {
106        let dir = self.scaled_direction();
107        Vector::new(dir.y, -dir.x)
108    }
109
110    /// In 2D, the normalized counterclockwise normal of this segment.
111    #[cfg(feature = "dim2")]
112    pub fn normal(&self) -> Option<Unit<Vector<N>>> {
113        Unit::try_new(self.scaled_normal(), N::default_epsilon())
114    }
115
116    /// Returns `None`. Exists only for API similarity with the 2D ncollide.
117    #[cfg(feature = "dim3")]
118    pub fn normal(&self) -> Option<Unit<Vector<N>>> {
119        None
120    }
121
122    /// Applies the isometry `m` to the vertices of this segment and returns the resulting segment.
123    pub fn transformed(&self, m: &Isometry<N>) -> Self {
124        Segment::new(m * self.a, m * self.b)
125    }
126
127    /// Computes the point at the given location.
128    pub fn point_at(&self, location: &SegmentPointLocation<N>) -> Point<N> {
129        match *location {
130            SegmentPointLocation::OnVertex(0) => self.a,
131            SegmentPointLocation::OnVertex(1) => self.b,
132            SegmentPointLocation::OnEdge(bcoords) => {
133                self.a * bcoords[0] + self.b.coords * bcoords[1]
134            }
135            _ => panic!(),
136        }
137    }
138
139    /// Checks that the given direction in world-space is on the tangent cone of the given `feature`.
140    pub fn tangent_cone_contains_dir(
141        &self,
142        feature: FeatureId,
143        m: &Isometry<N>,
144        dir: &Unit<Vector<N>>,
145    ) -> bool {
146        let ls_dir = m.inverse_transform_unit_vector(dir);
147
148        if let Some(direction) = self.direction() {
149            match feature {
150                FeatureId::Vertex(id) => {
151                    let dot = ls_dir.dot(&direction);
152                    if id == 0 {
153                        dot >= N::one() - N::default_epsilon()
154                    } else {
155                        -dot >= N::one() - N::default_epsilon()
156                    }
157                }
158                #[cfg(feature = "dim3")]
159                FeatureId::Edge(_) => {
160                    ls_dir.dot(&direction).abs() >= N::one() - N::default_epsilon()
161                }
162                FeatureId::Face(id) => {
163                    let mut dir = Vector::zeros();
164                    if id == 0 {
165                        dir[0] = direction[1];
166                        dir[1] = -direction[0];
167                    } else {
168                        dir[0] = -direction[1];
169                        dir[1] = direction[0];
170                    }
171
172                    ls_dir.dot(&dir) <= N::zero()
173                }
174                _ => true,
175            }
176        } else {
177            false
178        }
179    }
180}
181
182impl<N: RealField + Copy> SupportMap<N> for Segment<N> {
183    #[inline]
184    fn local_support_point(&self, dir: &Vector<N>) -> Point<N> {
185        if self.a.coords.dot(dir) > self.b.coords.dot(dir) {
186            self.a
187        } else {
188            self.b
189        }
190    }
191}
192
193impl<N: RealField + Copy> ConvexPolyhedron<N> for Segment<N> {
194    fn vertex(&self, id: FeatureId) -> Point<N> {
195        if id.unwrap_vertex() == 0 {
196            self.a
197        } else {
198            self.b
199        }
200    }
201
202    #[cfg(feature = "dim3")]
203    fn edge(&self, _: FeatureId) -> (Point<N>, Point<N>, FeatureId, FeatureId) {
204        (self.a, self.b, FeatureId::Vertex(0), FeatureId::Vertex(1))
205    }
206
207    #[cfg(feature = "dim3")]
208    fn face(&self, _: FeatureId, _: &mut ConvexPolygonalFeature<N>) {
209        panic!("A segment does not have any face in dimensions higher than 2.")
210    }
211
212    #[cfg(feature = "dim2")]
213    fn face(&self, id: FeatureId, face: &mut ConvexPolygonalFeature<N>) {
214        face.clear();
215
216        if let Some(normal) = utils::ccw_face_normal([&self.a, &self.b]) {
217            face.set_feature_id(id);
218
219            match id.unwrap_face() {
220                0 => {
221                    face.push(self.a, FeatureId::Vertex(0));
222                    face.push(self.b, FeatureId::Vertex(1));
223                    face.set_normal(normal);
224                }
225                1 => {
226                    face.push(self.b, FeatureId::Vertex(1));
227                    face.push(self.a, FeatureId::Vertex(0));
228                    face.set_normal(-normal);
229                }
230                _ => unreachable!(),
231            }
232        } else {
233            face.push(self.a, FeatureId::Vertex(0));
234            face.set_feature_id(FeatureId::Vertex(0));
235        }
236    }
237
238    fn feature_normal(&self, feature: FeatureId) -> Unit<Vector<N>> {
239        if let Some(direction) = self.direction() {
240            match feature {
241                FeatureId::Vertex(id) => {
242                    if id == 0 {
243                        direction
244                    } else {
245                        -direction
246                    }
247                }
248                #[cfg(feature = "dim3")]
249                FeatureId::Edge(_) => {
250                    let iamin = direction.iamin();
251                    let mut normal = Vector::zeros();
252                    normal[iamin] = N::one();
253                    normal -= *direction * direction[iamin];
254                    Unit::new_normalize(normal)
255                }
256                FeatureId::Face(id) => {
257                    let mut dir = Vector::zeros();
258                    if id == 0 {
259                        dir[0] = direction[1];
260                        dir[1] = -direction[0];
261                    } else {
262                        dir[0] = -direction[1];
263                        dir[1] = direction[0];
264                    }
265                    Unit::new_unchecked(dir)
266                }
267                _ => panic!("Invalid feature ID: {:?}", feature),
268            }
269        } else {
270            Vector::y_axis()
271        }
272    }
273
274    #[cfg(feature = "dim2")]
275    fn support_face_toward(
276        &self,
277        m: &Isometry<N>,
278        dir: &Unit<Vector<N>>,
279        face: &mut ConvexPolygonalFeature<N>,
280    ) {
281        let seg_dir = self.scaled_direction();
282
283        if dir.perp(&seg_dir) >= na::zero() {
284            self.face(FeatureId::Face(0), face);
285        } else {
286            self.face(FeatureId::Face(1), face);
287        }
288        face.transform_by(m)
289    }
290
291    #[cfg(feature = "dim3")]
292    fn support_face_toward(
293        &self,
294        m: &Isometry<N>,
295        _: &Unit<Vector<N>>,
296        face: &mut ConvexPolygonalFeature<N>,
297    ) {
298        face.clear();
299        face.push(self.a, FeatureId::Vertex(0));
300        face.push(self.b, FeatureId::Vertex(1));
301        face.push_edge_feature_id(FeatureId::Edge(0));
302        face.set_feature_id(FeatureId::Edge(0));
303        face.transform_by(m)
304    }
305
306    fn support_feature_toward(
307        &self,
308        transform: &Isometry<N>,
309        dir: &Unit<Vector<N>>,
310        eps: N,
311        face: &mut ConvexPolygonalFeature<N>,
312    ) {
313        face.clear();
314        let seg = self.transformed(transform);
315        let ceps = eps.sin();
316
317        if let Some(seg_dir) = seg.direction() {
318            let cang = dir.dot(&seg_dir);
319
320            if cang > ceps {
321                face.set_feature_id(FeatureId::Vertex(1));
322                face.push(seg.b, FeatureId::Vertex(1));
323            } else if cang < -ceps {
324                face.set_feature_id(FeatureId::Vertex(0));
325                face.push(seg.a, FeatureId::Vertex(0));
326            } else {
327                #[cfg(feature = "dim3")]
328                {
329                    face.push(seg.a, FeatureId::Vertex(0));
330                    face.push(seg.b, FeatureId::Vertex(1));
331                    face.push_edge_feature_id(FeatureId::Edge(0));
332                    face.set_feature_id(FeatureId::Edge(0));
333                }
334                #[cfg(feature = "dim2")]
335                {
336                    if dir.perp(&seg_dir) >= na::zero() {
337                        seg.face(FeatureId::Face(0), face);
338                    } else {
339                        seg.face(FeatureId::Face(1), face);
340                    }
341                }
342            }
343        }
344    }
345
346    fn support_feature_id_toward(&self, local_dir: &Unit<Vector<N>>) -> FeatureId {
347        if let Some(seg_dir) = self.direction() {
348            let eps: N = na::convert(f64::consts::PI / 180.0);
349            let seps = eps.sin();
350            let dot = seg_dir.dot(local_dir.as_ref());
351
352            if dot <= seps {
353                #[cfg(feature = "dim2")]
354                {
355                    if local_dir.perp(seg_dir.as_ref()) >= na::zero() {
356                        FeatureId::Face(0)
357                    } else {
358                        FeatureId::Face(1)
359                    }
360                }
361                #[cfg(feature = "dim3")]
362                {
363                    FeatureId::Edge(0)
364                }
365            } else if dot >= na::zero() {
366                FeatureId::Vertex(1)
367            } else {
368                FeatureId::Vertex(0)
369            }
370        } else {
371            FeatureId::Vertex(0)
372        }
373    }
374}