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