1use nalgebra::{Point3, Vector3};
4
5use crate::{Classification, Plane3D, PlaneSide};
6
7#[derive(Debug, Clone, PartialEq)]
9pub struct Triangle {
10 vertices: [Point3<f32>; 3],
11}
12
13impl Triangle {
14 pub fn new(a: Point3<f32>, b: Point3<f32>, c: Point3<f32>) -> Self {
19 Self {
20 vertices: [a, b, c],
21 }
22 }
23
24 #[inline]
26 pub fn vertices(&self) -> &[Point3<f32>; 3] {
27 &self.vertices
28 }
29
30 pub fn normal(&self) -> Vector3<f32> {
34 let [a, b, c] = &self.vertices;
35 let ab = b - a;
36 let ac = c - a;
37 ab.cross(&ac)
38 }
39
40 pub fn unit_normal(&self) -> Option<Vector3<f32>> {
44 let n = self.normal();
45 let len = n.norm();
46 if len > f32::EPSILON {
47 Some(n / len)
48 } else {
49 None
50 }
51 }
52
53 pub fn plane(&self) -> Plane3D {
58 let [a, b, c] = &self.vertices;
59 Plane3D::from_three_points(*a, *b, *c)
60 }
61
62 pub fn centroid(&self) -> Point3<f32> {
64 let [a, b, c] = &self.vertices;
65 Point3::from((a.coords + b.coords + c.coords) / 3.0)
66 }
67
68 pub fn classify(&self, plane: &Plane3D) -> Classification {
76 let mut front = 0;
77 let mut back = 0;
78 let mut on_plane = 0;
79
80 for vertex in &self.vertices {
81 match plane.classify_point(*vertex) {
82 PlaneSide::Front => front += 1,
83 PlaneSide::Back => back += 1,
84 PlaneSide::OnPlane => on_plane += 1,
85 }
86 }
87
88 if on_plane == 3 {
89 Classification::Coplanar
90 } else if back == 0 {
91 Classification::Front
92 } else if front == 0 {
93 Classification::Back
94 } else {
95 Classification::Spanning
96 }
97 }
98}
99
100impl From<Triangle> for Plane3D {
101 fn from(triangle: Triangle) -> Self {
102 triangle.plane()
103 }
104}
105
106impl From<&Triangle> for Plane3D {
107 fn from(triangle: &Triangle) -> Self {
108 triangle.plane()
109 }
110}