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}