parry3d/shape/
triangle.rs

1//! Definition of the triangle shape.
2
3use crate::math::{Isometry, Point, Real, Vector};
4use crate::shape::SupportMap;
5use crate::shape::{PolygonalFeature, Segment};
6use crate::utils;
7
8use core::mem;
9use na::{self, ComplexField, Unit};
10use num::Zero;
11
12#[cfg(feature = "dim3")]
13use {crate::shape::FeatureId, core::f64};
14
15#[cfg(feature = "dim2")]
16use crate::shape::PackedFeatureId;
17
18#[cfg(feature = "rkyv")]
19use rkyv::{bytecheck, CheckBytes};
20
21/// A triangle shape defined by three vertices.
22///
23/// A triangle is one of the most fundamental shapes in computational geometry.
24/// It's the simplest 2D polygon and the building block for triangle meshes.
25///
26/// # Structure
27///
28/// - **a, b, c**: The three vertices of the triangle
29/// - **Edges**: AB (from a to b), BC (from b to c), CA (from c to a)
30/// - **Orientation**: Counter-clockwise (CCW) is the standard convention
31///
32/// # Properties
33///
34/// - **Convex**: Always convex
35/// - **2D/3D**: Can be used in both dimensions
36/// - **In 2D**: A filled triangular region with area
37/// - **In 3D**: A flat surface embedded in 3D space (zero volume)
38///
39/// # Orientation Convention
40///
41/// Triangles are typically defined with counter-clockwise vertex order:
42/// - Looking at the triangle from the "front", vertices go a → b → c in CCW order
43/// - The normal vector (3D) points toward the observer
44/// - Right-hand rule: Curl fingers from a→b→c, thumb points along normal
45///
46/// # Use Cases
47///
48/// - **Mesh building block**: Fundamental unit of triangle meshes
49/// - **Simple collision shapes**: Fast collision detection
50/// - **Terrain representation**: Ground planes and surfaces
51/// - **Testing and debugging**: Simple shape for verification
52///
53/// # Example
54///
55/// ```rust
56/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
57/// use parry3d::shape::Triangle;
58/// use nalgebra::Point3;
59///
60/// // Create a right triangle in the XY plane
61/// let triangle = Triangle::new(
62///     Point3::origin(),  // a: origin
63///     Point3::new(3.0, 0.0, 0.0),  // b: along +X
64///     Point3::new(0.0, 4.0, 0.0)   // c: along +Y
65/// );
66///
67/// // Area of 3-4-5 right triangle is 6.0
68/// assert_eq!(triangle.area(), 6.0);
69///
70/// // Check if a point is inside
71/// let inside = Point3::new(1.0, 1.0, 0.0);
72/// assert!(triangle.contains_point(&inside));
73/// # }
74/// ```
75#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
76#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
77#[cfg_attr(
78    feature = "rkyv",
79    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
80    archive(as = "Self")
81)]
82#[derive(PartialEq, Debug, Copy, Clone, Default)]
83#[repr(C)]
84pub struct Triangle {
85    /// The first vertex of the triangle.
86    pub a: Point<Real>,
87    /// The second vertex of the triangle.
88    pub b: Point<Real>,
89    /// The third vertex of the triangle.
90    pub c: Point<Real>,
91}
92
93/// Description of the location of a point on a triangle.
94#[derive(Copy, Clone, Debug)]
95pub enum TrianglePointLocation {
96    /// The point lies on a vertex.
97    OnVertex(u32),
98    /// The point lies on an edge.
99    ///
100    /// The 0-st edge is the segment AB.
101    /// The 1-st edge is the segment BC.
102    /// The 2-nd edge is the segment AC.
103    // XXX: it appears the conversion of edge indexing here does not match the
104    // convention of edge indexing for the `fn edge` method (from the ConvexPolyhedron impl).
105    OnEdge(u32, [Real; 2]),
106    /// The point lies on the triangle interior.
107    ///
108    /// The integer indicates on which side of the face the point is. 0 indicates the point
109    /// is on the half-space toward the CW normal of the triangle. 1 indicates the point is on the other
110    /// half-space. This is always set to 0 in 2D.
111    OnFace(u32, [Real; 3]),
112    /// The point lies on the triangle interior (for "solid" point queries).
113    OnSolid,
114}
115
116impl TrianglePointLocation {
117    /// The barycentric coordinates corresponding to this point location.
118    ///
119    /// Returns `None` if the location is `TrianglePointLocation::OnSolid`.
120    pub fn barycentric_coordinates(&self) -> Option<[Real; 3]> {
121        let mut bcoords = [0.0; 3];
122
123        match self {
124            TrianglePointLocation::OnVertex(i) => bcoords[*i as usize] = 1.0,
125            TrianglePointLocation::OnEdge(i, uv) => {
126                let idx = match i {
127                    0 => (0, 1),
128                    1 => (1, 2),
129                    2 => (0, 2),
130                    _ => unreachable!(),
131                };
132
133                bcoords[idx.0] = uv[0];
134                bcoords[idx.1] = uv[1];
135            }
136            TrianglePointLocation::OnFace(_, uvw) => {
137                bcoords[0] = uvw[0];
138                bcoords[1] = uvw[1];
139                bcoords[2] = uvw[2];
140            }
141            TrianglePointLocation::OnSolid => {
142                return None;
143            }
144        }
145
146        Some(bcoords)
147    }
148
149    /// Returns `true` if the point is located on the relative interior of the triangle.
150    pub fn is_on_face(&self) -> bool {
151        matches!(*self, TrianglePointLocation::OnFace(..))
152    }
153}
154
155/// Orientation of a triangle.
156#[derive(Copy, Clone, Debug, PartialEq, Eq)]
157pub enum TriangleOrientation {
158    /// Orientation with a clockwise orientation, i.e., with a positive signed area.
159    Clockwise,
160    /// Orientation with a clockwise orientation, i.e., with a negative signed area.
161    CounterClockwise,
162    /// Degenerate triangle.
163    Degenerate,
164}
165
166impl From<[Point<Real>; 3]> for Triangle {
167    fn from(arr: [Point<Real>; 3]) -> Self {
168        *Self::from_array(&arr)
169    }
170}
171
172impl Triangle {
173    /// Creates a triangle from three vertices.
174    ///
175    /// # Arguments
176    ///
177    /// * `a` - The first vertex
178    /// * `b` - The second vertex
179    /// * `c` - The third vertex
180    ///
181    /// # Convention
182    ///
183    /// For proper normal calculation and consistent collision detection, vertices
184    /// should be ordered counter-clockwise when viewed from the "front" side.
185    ///
186    /// # Example
187    ///
188    /// ```
189    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
190    /// use parry3d::shape::Triangle;
191    /// use nalgebra::Point3;
192    ///
193    /// // Create a triangle in the XY plane
194    /// let tri = Triangle::new(
195    ///     Point3::origin(),
196    ///     Point3::new(1.0, 0.0, 0.0),
197    ///     Point3::new(0.0, 1.0, 0.0)
198    /// );
199    ///
200    /// assert_eq!(tri.area(), 0.5);
201    /// # }
202    /// ```
203    #[inline]
204    pub fn new(a: Point<Real>, b: Point<Real>, c: Point<Real>) -> Triangle {
205        Triangle { a, b, c }
206    }
207
208    /// Creates the reference to a triangle from the reference to an array of three points.
209    pub fn from_array(arr: &[Point<Real>; 3]) -> &Triangle {
210        unsafe { mem::transmute(arr) }
211    }
212
213    /// Reference to an array containing the three vertices of this triangle.
214    #[inline]
215    pub fn vertices(&self) -> &[Point<Real>; 3] {
216        unsafe { mem::transmute(self) }
217    }
218
219    /// The normal of this triangle assuming it is oriented ccw.
220    ///
221    /// The normal points such that it is collinear to `AB × AC` (where `×` denotes the cross
222    /// product).
223    #[inline]
224    #[cfg(feature = "dim3")]
225    pub fn normal(&self) -> Option<Unit<Vector<Real>>> {
226        Unit::try_new(self.scaled_normal(), crate::math::DEFAULT_EPSILON)
227    }
228
229    /// The three edges of this triangle: [AB, BC, CA].
230    #[inline]
231    pub fn edges(&self) -> [Segment; 3] {
232        [
233            Segment::new(self.a, self.b),
234            Segment::new(self.b, self.c),
235            Segment::new(self.c, self.a),
236        ]
237    }
238
239    /// Computes a scaled version of this triangle.
240    pub fn scaled(self, scale: &Vector<Real>) -> Self {
241        Self::new(
242            na::Scale::from(*scale) * self.a,
243            na::Scale::from(*scale) * self.b,
244            na::Scale::from(*scale) * self.c,
245        )
246    }
247
248    /// Returns a new triangle with vertices transformed by `m`.
249    #[inline]
250    pub fn transformed(&self, m: &Isometry<Real>) -> Self {
251        Triangle::new(m * self.a, m * self.b, m * self.c)
252    }
253
254    /// The three edges scaled directions of this triangle: [B - A, C - B, A - C].
255    #[inline]
256    pub fn edges_scaled_directions(&self) -> [Vector<Real>; 3] {
257        [self.b - self.a, self.c - self.b, self.a - self.c]
258    }
259
260    /// Return the edge segment of this cuboid with a normal cone containing
261    /// a direction that that maximizes the dot product with `local_dir`.
262    pub fn local_support_edge_segment(&self, dir: Vector<Real>) -> Segment {
263        let dots = na::Vector3::new(
264            dir.dot(&self.a.coords),
265            dir.dot(&self.b.coords),
266            dir.dot(&self.c.coords),
267        );
268
269        match dots.imin() {
270            0 => Segment::new(self.b, self.c),
271            1 => Segment::new(self.c, self.a),
272            _ => Segment::new(self.a, self.b),
273        }
274    }
275
276    /// Return the face of this triangle with a normal that maximizes
277    /// the dot product with `dir`.
278    #[cfg(feature = "dim3")]
279    pub fn support_face(&self, _dir: Vector<Real>) -> PolygonalFeature {
280        PolygonalFeature::from(*self)
281    }
282
283    /// Return the face of this triangle with a normal that maximizes
284    /// the dot product with `dir`.
285    #[cfg(feature = "dim2")]
286    pub fn support_face(&self, dir: Vector<Real>) -> PolygonalFeature {
287        let mut best = 0;
288        let mut best_dot = -Real::MAX;
289
290        for (i, tangent) in self.edges_scaled_directions().iter().enumerate() {
291            let normal = Vector::new(tangent.y, -tangent.x);
292            if let Some(normal) = Unit::try_new(normal, 0.0) {
293                let dot = normal.dot(&dir);
294                if normal.dot(&dir) > best_dot {
295                    best = i;
296                    best_dot = dot;
297                }
298            }
299        }
300
301        let pts = self.vertices();
302        let i1 = best;
303        let i2 = (best + 1) % 3;
304
305        PolygonalFeature {
306            vertices: [pts[i1], pts[i2]],
307            vids: PackedFeatureId::vertices([i1 as u32, i2 as u32]),
308            fid: PackedFeatureId::face(i1 as u32),
309            num_vertices: 2,
310        }
311    }
312
313    /// A vector normal of this triangle.
314    ///
315    /// The vector points such that it is collinear to `AB × AC` (where `×` denotes the cross
316    /// product).
317    ///
318    /// Note that on thin triangles the calculated normals can suffer from numerical issues.
319    /// For a more robust (but more computationally expensive) normal calculation, see
320    /// [`Triangle::robust_scaled_normal`].
321    #[inline]
322    #[cfg(feature = "dim3")]
323    pub fn scaled_normal(&self) -> Vector<Real> {
324        let ab = self.b - self.a;
325        let ac = self.c - self.a;
326        ab.cross(&ac)
327    }
328
329    /// Find a triangle normal more robustly than with [`Triangle::scaled_normal`].
330    ///
331    /// Thin triangles can cause numerical issues when computing its normal. This method accounts
332    /// for these numerical issues more robustly than [`Triangle::scaled_normal`], but is more
333    /// computationally expensive.
334    #[inline]
335    #[cfg(feature = "dim3")]
336    pub fn robust_scaled_normal(&self) -> na::Vector3<Real> {
337        let pts = self.vertices();
338        let best_vertex = self.angle_closest_to_90();
339        let d1 = pts[(best_vertex + 2) % 3] - pts[(best_vertex + 1) % 3];
340        let d2 = pts[best_vertex] - pts[(best_vertex + 1) % 3];
341
342        d1.cross(&d2)
343    }
344
345    /// Similar to [`Triangle::robust_scaled_normal`], but returns the unit length normal.
346    #[inline]
347    #[cfg(feature = "dim3")]
348    pub fn robust_normal(&self) -> na::Vector3<Real> {
349        self.robust_scaled_normal().normalize()
350    }
351
352    /// Computes the extents of this triangle on the given direction.
353    ///
354    /// This computes the min and max values of the dot products between each
355    /// vertex of this triangle and `dir`.
356    #[inline]
357    pub fn extents_on_dir(&self, dir: &Unit<Vector<Real>>) -> (Real, Real) {
358        let a = self.a.coords.dot(dir);
359        let b = self.b.coords.dot(dir);
360        let c = self.c.coords.dot(dir);
361
362        if a > b {
363            if b > c {
364                (c, a)
365            } else if a > c {
366                (b, a)
367            } else {
368                (b, c)
369            }
370        } else {
371            // b >= a
372            if a > c {
373                (c, b)
374            } else if b > c {
375                (a, b)
376            } else {
377                (a, c)
378            }
379        }
380    }
381    //
382    // #[cfg(feature = "dim3")]
383    // fn support_feature_id_toward(&self, local_dir: &Unit<Vector<Real>>, eps: Real) -> FeatureId {
384    //     if let Some(normal) = self.normal() {
385    //         let (seps, ceps) = ComplexField::sin_cos(eps);
386    //
387    //         let normal_dot = local_dir.dot(&*normal);
388    //         if normal_dot >= ceps {
389    //             FeatureId::Face(0)
390    //         } else if normal_dot <= -ceps {
391    //             FeatureId::Face(1)
392    //         } else {
393    //             let edges = self.edges();
394    //             let mut dots = [0.0; 3];
395    //
396    //             let dir1 = edges[0].direction();
397    //             if let Some(dir1) = dir1 {
398    //                 dots[0] = dir1.dot(local_dir);
399    //
400    //                 if dots[0].abs() < seps {
401    //                     return FeatureId::Edge(0);
402    //                 }
403    //             }
404    //
405    //             let dir2 = edges[1].direction();
406    //             if let Some(dir2) = dir2 {
407    //                 dots[1] = dir2.dot(local_dir);
408    //
409    //                 if dots[1].abs() < seps {
410    //                     return FeatureId::Edge(1);
411    //                 }
412    //             }
413    //
414    //             let dir3 = edges[2].direction();
415    //             if let Some(dir3) = dir3 {
416    //                 dots[2] = dir3.dot(local_dir);
417    //
418    //                 if dots[2].abs() < seps {
419    //                     return FeatureId::Edge(2);
420    //                 }
421    //             }
422    //
423    //             if dots[0] > 0.0 && dots[1] < 0.0 {
424    //                 FeatureId::Vertex(1)
425    //             } else if dots[1] > 0.0 && dots[2] < 0.0 {
426    //                 FeatureId::Vertex(2)
427    //             } else {
428    //                 FeatureId::Vertex(0)
429    //             }
430    //         }
431    //     } else {
432    //         FeatureId::Vertex(0)
433    //     }
434    // }
435
436    /// The area of this triangle.
437    #[inline]
438    pub fn area(&self) -> Real {
439        // Kahan's formula.
440        let a = na::distance(&self.a, &self.b);
441        let b = na::distance(&self.b, &self.c);
442        let c = na::distance(&self.c, &self.a);
443
444        let (c, b, a) = utils::sort3(&a, &b, &c);
445        let a = *a;
446        let b = *b;
447        let c = *c;
448
449        let sqr = (a + (b + c)) * (c - (a - b)) * (c + (a - b)) * (a + (b - c));
450
451        // We take the max(0.0) because it can be slightly negative
452        // because of numerical errors due to almost-degenerate triangles.
453        ComplexField::sqrt(sqr.max(0.0)) * 0.25
454    }
455
456    /// Computes the unit angular inertia of this triangle.
457    #[cfg(feature = "dim2")]
458    pub fn unit_angular_inertia(&self) -> Real {
459        let factor = 1.0 / 6.0;
460
461        // Algorithm adapted from Box2D
462        let e1 = self.b - self.a;
463        let e2 = self.c - self.a;
464
465        let intx2 = e1.x * e1.x + e2.x * e1.x + e2.x * e2.x;
466        let inty2 = e1.y * e1.y + e2.y * e1.y + e2.y * e2.y;
467        factor * (intx2 + inty2)
468    }
469
470    /// The geometric center of this triangle.
471    #[inline]
472    pub fn center(&self) -> Point<Real> {
473        utils::center(&[self.a, self.b, self.c])
474    }
475
476    /// The perimeter of this triangle.
477    #[inline]
478    pub fn perimeter(&self) -> Real {
479        na::distance(&self.a, &self.b)
480            + na::distance(&self.b, &self.c)
481            + na::distance(&self.c, &self.a)
482    }
483
484    /// The circumcircle of this triangle.
485    pub fn circumcircle(&self) -> (Point<Real>, Real) {
486        let a = self.a - self.c;
487        let b = self.b - self.c;
488
489        let na = a.norm_squared();
490        let nb = b.norm_squared();
491
492        let dab = a.dot(&b);
493
494        let denom = 2.0 * (na * nb - dab * dab);
495
496        if denom.is_zero() {
497            // The triangle is degenerate (the three points are collinear).
498            // So we find the longest segment and take its center.
499            let c = self.a - self.b;
500            let nc = c.norm_squared();
501
502            if nc >= na && nc >= nb {
503                // Longest segment: [&self.a, &self.b]
504                (
505                    na::center(&self.a, &self.b),
506                    ComplexField::sqrt(nc) / na::convert::<f64, Real>(2.0f64),
507                )
508            } else if na >= nb && na >= nc {
509                // Longest segment: [&self.a, pc]
510                (
511                    na::center(&self.a, &self.c),
512                    ComplexField::sqrt(na) / na::convert::<f64, Real>(2.0f64),
513                )
514            } else {
515                // Longest segment: [&self.b, &self.c]
516                (
517                    na::center(&self.b, &self.c),
518                    ComplexField::sqrt(nb) / na::convert::<f64, Real>(2.0f64),
519                )
520            }
521        } else {
522            let k = b * na - a * nb;
523
524            let center = self.c + (a * k.dot(&b) - b * k.dot(&a)) / denom;
525            let radius = na::distance(&self.a, &center);
526
527            (center, radius)
528        }
529    }
530
531    /// Tests if this triangle is affinely dependent, i.e., its points are almost aligned.
532    #[cfg(feature = "dim3")]
533    pub fn is_affinely_dependent(&self) -> bool {
534        const EPS: Real = crate::math::DEFAULT_EPSILON * 100.0;
535
536        let p1p2 = self.b - self.a;
537        let p1p3 = self.c - self.a;
538        relative_eq!(p1p2.cross(&p1p3).norm_squared(), 0.0, epsilon = EPS * EPS)
539
540        // relative_eq!(
541        //     self.area(),
542        //     0.0,
543        //     epsilon = EPS * self.perimeter()
544        // )
545    }
546
547    /// Is this triangle degenerate or almost degenerate?
548    #[cfg(feature = "dim3")]
549    pub fn is_affinely_dependent_eps(&self, eps: Real) -> bool {
550        let p1p2 = self.b - self.a;
551        let p1p3 = self.c - self.a;
552        relative_eq!(
553            p1p2.cross(&p1p3).norm(),
554            0.0,
555            epsilon = eps * p1p2.norm().max(p1p3.norm())
556        )
557
558        // relative_eq!(
559        //     self.area(),
560        //     0.0,
561        //     epsilon = EPS * self.perimeter()
562        // )
563    }
564
565    /// Tests if a point is inside of this triangle.
566    #[cfg(feature = "dim2")]
567    pub fn contains_point(&self, p: &Point<Real>) -> bool {
568        let ab = self.b - self.a;
569        let bc = self.c - self.b;
570        let ca = self.a - self.c;
571        let sgn1 = ab.perp(&(p - self.a));
572        let sgn2 = bc.perp(&(p - self.b));
573        let sgn3 = ca.perp(&(p - self.c));
574        sgn1.signum() * sgn2.signum() >= 0.0
575            && sgn1.signum() * sgn3.signum() >= 0.0
576            && sgn2.signum() * sgn3.signum() >= 0.0
577    }
578
579    /// Tests if a point is inside of this triangle.
580    #[cfg(feature = "dim3")]
581    pub fn contains_point(&self, p: &Point<Real>) -> bool {
582        const EPS: Real = crate::math::DEFAULT_EPSILON;
583
584        let vb = self.b - self.a;
585        let vc = self.c - self.a;
586        let vp = p - self.a;
587
588        let n = vc.cross(&vb);
589        let n_norm = n.norm_squared();
590        if n_norm < EPS || vp.dot(&n).abs() > EPS * n_norm {
591            // the triangle is degenerate or the
592            // point does not lie on the same plane as the triangle.
593            return false;
594        }
595
596        // We are seeking B, C such that vp = vb * B + vc * C .
597        // If B and C are both in [0, 1] and B + C <= 1 then p is in the triangle.
598        //
599        // We can project this equation along a vector nb coplanar to the triangle
600        // and perpendicular to vb:
601        // vp.dot(nb) = vb.dot(nb) * B + vc.dot(nb) * C
602        //     => C = vp.dot(nb) / vc.dot(nb)
603        // and similarly for B.
604        //
605        // In order to avoid divisions and sqrts we scale both B and C - so
606        // b = vb.dot(nc) * B and c = vc.dot(nb) * C - this results in harder-to-follow math but
607        // hopefully fast code.
608
609        let nb = vb.cross(&n);
610        let nc = vc.cross(&n);
611
612        let signed_blim = vb.dot(&nc);
613        let b = vp.dot(&nc) * signed_blim.signum();
614        let blim = signed_blim.abs();
615
616        let signed_clim = vc.dot(&nb);
617        let c = vp.dot(&nb) * signed_clim.signum();
618        let clim = signed_clim.abs();
619
620        c >= 0.0 && c <= clim && b >= 0.0 && b <= blim && c * blim + b * clim <= blim * clim
621    }
622
623    /// The normal of the given feature of this shape.
624    #[cfg(feature = "dim3")]
625    pub fn feature_normal(&self, _: FeatureId) -> Option<Unit<Vector<Real>>> {
626        self.normal()
627    }
628
629    /// The orientation of the triangle, based on its signed area.
630    ///
631    /// Returns `TriangleOrientation::Degenerate` if the triangle’s area is
632    /// smaller than `epsilon`.
633    #[cfg(feature = "dim2")]
634    pub fn orientation(&self, epsilon: Real) -> TriangleOrientation {
635        let area2 = (self.b - self.a).perp(&(self.c - self.a));
636        // println!("area2: {}", area2);
637        if area2 > epsilon {
638            TriangleOrientation::CounterClockwise
639        } else if area2 < -epsilon {
640            TriangleOrientation::Clockwise
641        } else {
642            TriangleOrientation::Degenerate
643        }
644    }
645
646    /// The orientation of the 2D triangle, based on its signed area.
647    ///
648    /// Returns `TriangleOrientation::Degenerate` if the triangle’s area is
649    /// smaller than `epsilon`.
650    pub fn orientation2d(
651        a: &na::Point2<Real>,
652        b: &na::Point2<Real>,
653        c: &na::Point2<Real>,
654        epsilon: Real,
655    ) -> TriangleOrientation {
656        let area2 = (b - a).perp(&(c - a));
657        // println!("area2: {}", area2);
658        if area2 > epsilon {
659            TriangleOrientation::CounterClockwise
660        } else if area2 < -epsilon {
661            TriangleOrientation::Clockwise
662        } else {
663            TriangleOrientation::Degenerate
664        }
665    }
666
667    /// Find the index of a vertex in this triangle, such that the two
668    /// edges incident in that vertex form the angle closest to 90
669    /// degrees in the triangle.
670    pub fn angle_closest_to_90(&self) -> usize {
671        let points = self.vertices();
672        let mut best_cos = 2.0;
673        let mut selected_i = 0;
674
675        for i in 0..3 {
676            let d1 = (points[i] - points[(i + 1) % 3]).normalize();
677            let d2 = (points[(i + 2) % 3] - points[(i + 1) % 3]).normalize();
678
679            let cos_abs = d1.dot(&d2).abs();
680
681            if cos_abs < best_cos {
682                best_cos = cos_abs;
683                selected_i = i;
684            }
685        }
686
687        selected_i
688    }
689
690    /// Reverse the orientation of this triangle by swapping b and c.
691    pub fn reverse(&mut self) {
692        mem::swap(&mut self.b, &mut self.c);
693    }
694}
695
696impl SupportMap for Triangle {
697    #[inline]
698    fn local_support_point(&self, dir: &Vector<Real>) -> Point<Real> {
699        let d1 = self.a.coords.dot(dir);
700        let d2 = self.b.coords.dot(dir);
701        let d3 = self.c.coords.dot(dir);
702
703        if d1 > d2 {
704            if d1 > d3 {
705                self.a
706            } else {
707                self.c
708            }
709        } else if d2 > d3 {
710            self.b
711        } else {
712            self.c
713        }
714    }
715}
716
717/*
718#[cfg(feature = "dim3")]
719impl ConvexPolyhedron for Triangle {
720    fn vertex(&self, id: FeatureId) -> Point<Real> {
721        match id.unwrap_vertex() {
722            0 => self.a,
723            1 => self.b,
724            2 => self.c,
725            _ => panic!("Triangle vertex index out of bounds."),
726        }
727    }
728    fn edge(&self, id: FeatureId) -> (Point<Real>, Point<Real>, FeatureId, FeatureId) {
729        match id.unwrap_edge() {
730            0 => (self.a, self.b, FeatureId::Vertex(0), FeatureId::Vertex(1)),
731            1 => (self.b, self.c, FeatureId::Vertex(1), FeatureId::Vertex(2)),
732            2 => (self.c, self.a, FeatureId::Vertex(2), FeatureId::Vertex(0)),
733            _ => panic!("Triangle edge index out of bounds."),
734        }
735    }
736
737    fn face(&self, id: FeatureId, face: &mut ConvexPolygonalFeature) {
738        face.clear();
739
740        if let Some(normal) = self.normal() {
741            face.set_feature_id(id);
742
743            match id.unwrap_face() {
744                0 => {
745                    face.push(self.a, FeatureId::Vertex(0));
746                    face.push(self.b, FeatureId::Vertex(1));
747                    face.push(self.c, FeatureId::Vertex(2));
748                    face.push_edge_feature_id(FeatureId::Edge(0));
749                    face.push_edge_feature_id(FeatureId::Edge(1));
750                    face.push_edge_feature_id(FeatureId::Edge(2));
751                    face.set_normal(normal);
752                }
753                1 => {
754                    face.push(self.a, FeatureId::Vertex(0));
755                    face.push(self.c, FeatureId::Vertex(2));
756                    face.push(self.b, FeatureId::Vertex(1));
757                    face.push_edge_feature_id(FeatureId::Edge(2));
758                    face.push_edge_feature_id(FeatureId::Edge(1));
759                    face.push_edge_feature_id(FeatureId::Edge(0));
760                    face.set_normal(-normal);
761                }
762                _ => unreachable!(),
763            }
764
765            face.recompute_edge_normals();
766        } else {
767            face.push(self.a, FeatureId::Vertex(0));
768            face.set_feature_id(FeatureId::Vertex(0));
769        }
770    }
771
772    fn support_face_toward(
773        &self,
774        m: &Isometry<Real>,
775        dir: &Unit<Vector<Real>>,
776        face: &mut ConvexPolygonalFeature,
777    ) {
778        let normal = self.scaled_normal();
779
780        if normal.dot(&*dir) >= 0.0 {
781            ConvexPolyhedron::face(self, FeatureId::Face(0), face);
782        } else {
783            ConvexPolyhedron::face(self, FeatureId::Face(1), face);
784        }
785        face.transform_by(m)
786    }
787
788    fn support_feature_toward(
789        &self,
790        transform: &Isometry<Real>,
791        dir: &Unit<Vector<Real>>,
792        eps: Real,
793        out: &mut ConvexPolygonalFeature,
794    ) {
795        out.clear();
796        let tri = self.transformed(transform);
797        let feature = tri.support_feature_id_toward(dir, eps);
798
799        match feature {
800            FeatureId::Vertex(_) => {
801                let v = tri.vertex(feature);
802                out.push(v, feature);
803                out.set_feature_id(feature);
804            }
805            FeatureId::Edge(_) => {
806                let (a, b, fa, fb) = tri.edge(feature);
807                out.push(a, fa);
808                out.push(b, fb);
809                out.push_edge_feature_id(feature);
810                out.set_feature_id(feature);
811            }
812            FeatureId::Face(_) => tri.face(feature, out),
813            _ => unreachable!(),
814        }
815    }
816
817    fn support_feature_id_toward(&self, local_dir: &Unit<Vector<Real>>) -> FeatureId {
818        self.support_feature_id_toward(local_dir, na::convert::<f64, Real>(f64::consts::PI / 180.0))
819    }
820}
821*/
822
823#[cfg(feature = "dim2")]
824#[cfg(test)]
825mod test {
826    use crate::shape::Triangle;
827    use na::Point2;
828
829    #[test]
830    fn test_triangle_area() {
831        let pa = Point2::new(5.0, 0.0);
832        let pb = Point2::origin();
833        let pc = Point2::new(0.0, 4.0);
834
835        assert!(relative_eq!(Triangle::new(pa, pb, pc).area(), 10.0));
836    }
837
838    #[test]
839    fn test_triangle_contains_point() {
840        let tri = Triangle::new(
841            Point2::new(5.0, 0.0),
842            Point2::origin(),
843            Point2::new(0.0, 4.0),
844        );
845
846        assert!(tri.contains_point(&Point2::new(1.0, 1.0)));
847        assert!(!tri.contains_point(&Point2::new(-1.0, 1.0)));
848    }
849
850    #[test]
851    fn test_obtuse_triangle_contains_point() {
852        let tri = Triangle::new(
853            Point2::new(-10.0, 10.0),
854            Point2::origin(),
855            Point2::new(20.0, 0.0),
856        );
857
858        assert!(tri.contains_point(&Point2::new(-3.0, 5.0)));
859        assert!(tri.contains_point(&Point2::new(5.0, 1.0)));
860        assert!(!tri.contains_point(&Point2::new(0.0, -1.0)));
861    }
862}
863
864#[cfg(feature = "dim3")]
865#[cfg(test)]
866mod test {
867    use crate::math::Real;
868    use crate::shape::Triangle;
869    use na::Point3;
870
871    #[test]
872    fn test_triangle_area() {
873        let pa = Point3::new(0.0, 5.0, 0.0);
874        let pb = Point3::origin();
875        let pc = Point3::new(0.0, 0.0, 4.0);
876
877        assert!(relative_eq!(Triangle::new(pa, pb, pc).area(), 10.0));
878    }
879
880    #[test]
881    fn test_triangle_contains_point() {
882        let tri = Triangle::new(
883            Point3::new(0.0, 5.0, 0.0),
884            Point3::origin(),
885            Point3::new(0.0, 0.0, 4.0),
886        );
887
888        assert!(tri.contains_point(&Point3::new(0.0, 1.0, 1.0)));
889        assert!(!tri.contains_point(&Point3::new(0.0, -1.0, 1.0)));
890    }
891
892    #[test]
893    fn test_obtuse_triangle_contains_point() {
894        let tri = Triangle::new(
895            Point3::new(-10.0, 10.0, 0.0),
896            Point3::origin(),
897            Point3::new(20.0, 0.0, 0.0),
898        );
899
900        assert!(tri.contains_point(&Point3::new(-3.0, 5.0, 0.0)));
901        assert!(tri.contains_point(&Point3::new(5.0, 1.0, 0.0)));
902        assert!(!tri.contains_point(&Point3::new(0.0, -1.0, 0.0)));
903    }
904
905    #[test]
906    fn test_3dtriangle_contains_point() {
907        let o = Point3::origin();
908        let pa = Point3::new(1.2, 1.4, 5.6);
909        let pb = Point3::new(1.5, 6.7, 1.9);
910        let pc = Point3::new(5.0, 2.1, 1.3);
911
912        let tri = Triangle::new(pa, pb, pc);
913
914        let va = pa - o;
915        let vb = pb - o;
916        let vc = pc - o;
917
918        let n = (pa - pb).cross(&(pb - pc));
919
920        // This is a simple algorithm for generating points that are inside the
921        // triangle: o + (va * alpha + vb * beta + vc * gamma) is always inside the
922        // triangle if:
923        // * each of alpha, beta, gamma is in (0, 1)
924        // * alpha + beta + gamma = 1
925        let contained_p = o + (va * 0.2 + vb * 0.3 + vc * 0.5);
926        let not_contained_coplanar_p = o + (va * -0.5 + vb * 0.8 + vc * 0.7);
927        let not_coplanar_p = o + (va * 0.2 + vb * 0.3 + vc * 0.5) + n * 0.1;
928        let not_coplanar_p2 = o + (va * -0.5 + vb * 0.8 + vc * 0.7) + n * 0.1;
929        assert!(tri.contains_point(&contained_p));
930        assert!(!tri.contains_point(&not_contained_coplanar_p));
931        assert!(!tri.contains_point(&not_coplanar_p));
932        assert!(!tri.contains_point(&not_coplanar_p2));
933
934        // Test that points that are clearly within the triangle as seen as such, by testing
935        // a number of points along a line intersecting the triangle.
936        for i in -50i16..150 {
937            let a = 0.15;
938            let b = 0.01 * Real::from(i); // b ranges from -0.5 to 1.5
939            let c = 1.0 - a - b;
940            let p = o + (va * a + vb * b + vc * c);
941
942            match i {
943                ii if ii < 0 || ii > 85 => assert!(
944                    !tri.contains_point(&p),
945                    "Should not contain: i = {}, b = {}",
946                    i,
947                    b
948                ),
949                ii if ii > 0 && ii < 85 => assert!(
950                    tri.contains_point(&p),
951                    "Should contain: i = {}, b = {}",
952                    i,
953                    b
954                ),
955                _ => (), // Points at the edge may be seen as inside or outside
956            }
957        }
958    }
959}