geop_core_math/primitives/
triangle.rs1use crate::{
2 geop_error::{GeopError, GeopResult, WithContext},
3 scalars::Scalar,
4 vector::Vector3,
5};
6
7#[derive(Clone)]
8pub struct TriangleFace {
9 pub a: Vector3,
10 pub b: Vector3,
11 pub c: Vector3,
12 pub normal: Vector3,
14 pub vertex_normals: Option<[Vector3; 3]>,
20}
21
22impl core::fmt::Debug for TriangleFace {
23 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
24 write!(f, "TriangleFace({:?}, {:?}, {:?})", self.a, self.b, self.c)
25 }
26}
27
28impl TriangleFace {
29 pub fn try_new(a: Vector3, b: Vector3, c: Vector3) -> GeopResult<Self> {
32 let ctx = |err: GeopError| {
33 err.with_context(format!("TriangleFace::try_new({a:?}, {b:?}, {c:?})"))
34 };
35 let ba = b.sub(&a);
36 let ca = c.sub(&a);
37 let raw_normal = ba.prod_cross(&ca);
38 let normal = raw_normal.normalize().with_context(&ctx)?;
39 Ok(Self {
40 a,
41 b,
42 c,
43 normal,
44 vertex_normals: None,
45 })
46 }
47
48 pub fn with_vertex_normals(self, normals: [Vector3; 3]) -> Self {
53 let flip = normals[0]
54 .prod_dot(&self.normal)
55 .definitely_less(Scalar::ZERO);
56 let orient = |n: Vector3| {
57 if flip {
58 n.prod_scalar(Scalar::ZERO.sub(Scalar::ONE))
59 } else {
60 n
61 }
62 };
63 Self {
64 vertex_normals: Some(normals.map(orient)),
65 ..self
66 }
67 }
68}