fj_kernel/operations/build/
shell.rs

1use fj_math::Point;
2
3use crate::{
4    objects::{Face, Shell},
5    operations::{
6        BuildFace, Insert, IsInserted, IsInsertedNo, IsInsertedYes, JoinCycle,
7        Polygon, UpdateFace,
8    },
9    services::Services,
10};
11
12/// Build a [`Shell`]
13pub trait BuildShell {
14    /// Build a tetrahedron from the provided points
15    ///
16    /// Accepts 4 points, naturally. For the purposes of the following
17    /// discussion, let's call those `a`, `b`, `c`, and `d`, and assume that the
18    /// order they are listed in here matches the order they are provided in
19    /// within the array.
20    ///
21    /// Assumes that `a`, `b`, and `c` form a triangle in counter-clockwise
22    /// order, when arranging the viewpoint such that it is on the opposite side
23    /// of the triangle from `d`. If this assumption is met, the orientation of
24    /// all faces of the tetrahedron will be valid, meaning their
25    /// counter-clockwise sides are outside.
26    ///
27    /// # Implementation Note
28    ///
29    /// In principle, this method doesn't need to make assumptions about the
30    /// order of the points provided. It could, given some extra effort, just
31    /// build a correct tetrahedron, regardless of that order.
32    fn tetrahedron(
33        points: [impl Into<Point<3>>; 4],
34        services: &mut Services,
35    ) -> TetrahedronShell {
36        let [a, b, c, d] = points.map(Into::into);
37
38        let abc = Face::triangle([a, b, c], services);
39        let bad =
40            Face::triangle([b, a, d], services).update_exterior(|cycle| {
41                cycle
42                    .join_to(abc.face.exterior(), 0..=0, 0..=0, services)
43                    .insert(services)
44            });
45        let dac =
46            Face::triangle([d, a, c], services).update_exterior(|cycle| {
47                cycle
48                    .join_to(abc.face.exterior(), 1..=1, 2..=2, services)
49                    .join_to(bad.face.exterior(), 0..=0, 1..=1, services)
50                    .insert(services)
51            });
52        let cbd =
53            Face::triangle([c, b, d], services).update_exterior(|cycle| {
54                cycle
55                    .join_to(abc.face.exterior(), 0..=0, 1..=1, services)
56                    .join_to(bad.face.exterior(), 1..=1, 2..=2, services)
57                    .join_to(dac.face.exterior(), 2..=2, 2..=2, services)
58                    .insert(services)
59            });
60
61        let triangles =
62            [abc, bad, dac, cbd].map(|triangle| triangle.insert(services));
63        let shell =
64            Shell::new(triangles.iter().map(|triangle| triangle.face.clone()));
65
66        let [abc, bad, dac, cbd] = triangles;
67
68        TetrahedronShell {
69            shell,
70            abc,
71            bad,
72            dac,
73            cbd,
74        }
75    }
76}
77
78impl BuildShell for Shell {}
79
80/// A tetrahedron
81///
82/// A tetrahedron is constructed from 4 points and has 4 faces. For the purpose
83/// of naming the fields of this struct, the points are named `a`, `b`, `c`, and
84/// `d`, in the order in which they are passed.
85///
86/// Returned by [`BuildShell::tetrahedron`].
87pub struct TetrahedronShell<I: IsInserted = IsInsertedNo> {
88    /// The shell that forms the tetrahedron
89    pub shell: I::T<Shell>,
90
91    /// The face formed by the points `a`, `b`, and `c`.
92    pub abc: Polygon<3, IsInsertedYes>,
93
94    /// The face formed by the points `b`, `a`, and `d`.
95    pub bad: Polygon<3, IsInsertedYes>,
96
97    /// The face formed by the points `d`, `a`, and `c`.
98    pub dac: Polygon<3, IsInsertedYes>,
99
100    /// The face formed by the points `c`, `b`, and `d`.
101    pub cbd: Polygon<3, IsInsertedYes>,
102}