fj_core/operations/build/
shell.rs

1use std::collections::BTreeMap;
2
3use fj_interop::ext::ArrayExt;
4use fj_math::Point;
5
6use crate::{
7    geometry::CurveBoundary,
8    objects::{Curve, Face, HalfEdge, Shell, Surface, Vertex},
9    operations::{
10        build::{BuildFace, BuildHalfEdge, BuildSurface, Polygon},
11        geometry::UpdateHalfEdgeGeometry,
12        insert::{Insert, IsInserted, IsInsertedNo, IsInsertedYes},
13        join::JoinCycle,
14        reverse::ReverseCurveCoordinateSystems,
15        update::{
16            UpdateCycle, UpdateFace, UpdateHalfEdge, UpdateRegion, UpdateShell,
17        },
18    },
19    Core,
20};
21
22/// Build a [`Shell`]
23///
24/// See [module-level documentation] for context.
25///
26/// [module-level documentation]: super
27pub trait BuildShell {
28    /// Build an empty shell
29    fn empty() -> Shell {
30        Shell::new([])
31    }
32
33    /// Build a polyhedron by specifying its vertices and indices
34    fn from_vertices_and_indices(
35        vertices: impl IntoIterator<Item = impl Into<Point<3>>>,
36        indices: impl IntoIterator<Item = [usize; 3]>,
37        core: &mut Core,
38    ) -> Shell {
39        let vertices = vertices
40            .into_iter()
41            .enumerate()
42            .map(|(index, position)| {
43                let vertex = Vertex::new().insert(core);
44                let position = position.into();
45
46                (index, (vertex, position))
47            })
48            .collect::<BTreeMap<_, _>>();
49
50        let mut curves = BTreeMap::new();
51
52        let faces = indices
53            .into_iter()
54            .map(|indices| {
55                let [(a, a_pos), (b, b_pos), (c, c_pos)] = indices
56                    .map(|index| vertices.get(&index).expect("Invalid index"));
57
58                let (surface, _) = Surface::plane_from_points(
59                    [a_pos, b_pos, c_pos].map(Clone::clone),
60                    core,
61                );
62
63                let curves_and_boundaries =
64                    [[a, b], [b, c], [c, a]].map(|vertices| {
65                        let vertices = vertices.map(Clone::clone);
66                        let vertices = CurveBoundary::<Vertex>::from(vertices);
67
68                        curves
69                            .get(&vertices.clone().reverse())
70                            .cloned()
71                            .unwrap_or_else(|| {
72                                let curve = Curve::new().insert(core);
73                                let boundary =
74                                    CurveBoundary::<Point<1>>::from([
75                                        [0.],
76                                        [1.],
77                                    ]);
78
79                                curves.insert(
80                                    vertices,
81                                    (curve.clone(), boundary),
82                                );
83
84                                (curve, boundary.reverse())
85                            })
86                    });
87
88                let half_edges = {
89                    let vertices = [a, b, c].map(Clone::clone);
90                    let [a, b, c] = [[0., 0.], [1., 0.], [0., 1.]];
91                    vertices
92                        .zip_ext([[a, b], [b, c], [c, a]])
93                        .zip_ext(curves_and_boundaries)
94                        .map(|((vertex, positions), (curve, boundary))| {
95                            let half_edge = HalfEdge::line_segment(
96                                positions,
97                                Some(boundary.reverse().inner),
98                                core,
99                            );
100                            half_edge
101                                .update_start_vertex(|_, _| vertex, core)
102                                .update_curve(|_, _| curve, core)
103                                .insert(core)
104                                .set_path(
105                                    core.layers
106                                        .geometry
107                                        .of_half_edge(&half_edge)
108                                        .path,
109                                    &mut core.layers.geometry,
110                                )
111                        })
112                };
113
114                Face::unbound(surface, core).update_region(
115                    |region, core| {
116                        region.update_exterior(
117                            |cycle, core| {
118                                cycle.add_half_edges(half_edges, core)
119                            },
120                            core,
121                        )
122                    },
123                    core,
124                )
125            })
126            .collect::<Vec<_>>();
127
128        Shell::empty().add_faces(faces, core)
129    }
130
131    /// Build a tetrahedron from the provided points
132    ///
133    /// Accepts 4 points, naturally. For the purposes of the following
134    /// discussion, let's call those `a`, `b`, `c`, and `d`, and assume that the
135    /// order they are listed in here matches the order they are provided in
136    /// within the array.
137    ///
138    /// Assumes that `a`, `b`, and `c` form a triangle in counter-clockwise
139    /// order, when arranging the viewpoint such that it is on the opposite side
140    /// of the triangle from `d`. If this assumption is met, the orientation of
141    /// all faces of the tetrahedron will be valid, meaning their
142    /// counter-clockwise sides are outside.
143    ///
144    /// # Implementation Note
145    ///
146    /// In principle, this method doesn't need to make assumptions about the
147    /// order of the points provided. It could, given some extra effort, just
148    /// build a correct tetrahedron, regardless of that order.
149    fn tetrahedron(
150        points: [impl Into<Point<3>>; 4],
151        core: &mut Core,
152    ) -> TetrahedronShell {
153        let [a, b, c, d] = points.map(Into::into);
154
155        let abc = Face::triangle([a, b, c], core);
156        let bad = Face::triangle([b, a, d], core).update_region(
157            |region, core| {
158                region.update_exterior(
159                    |cycle, core| {
160                        cycle
161                            .update_half_edge(
162                                cycle.half_edges().nth_circular(0),
163                                |edge, core| {
164                                    [edge
165                                        .reverse_curve_coordinate_systems(core)]
166                                },
167                                core,
168                            )
169                            .join_to(
170                                abc.face.region().exterior(),
171                                0..=0,
172                                0..=0,
173                                core,
174                            )
175                    },
176                    core,
177                )
178            },
179            core,
180        );
181        let dac = Face::triangle([d, a, c], core).update_region(
182            |region, core| {
183                region.update_exterior(
184                    |cycle, core| {
185                        cycle
186                            .update_half_edge(
187                                cycle.half_edges().nth_circular(1),
188                                |edge, core| {
189                                    [edge
190                                        .reverse_curve_coordinate_systems(core)]
191                                },
192                                core,
193                            )
194                            .join_to(
195                                abc.face.region().exterior(),
196                                1..=1,
197                                2..=2,
198                                core,
199                            )
200                            .update_half_edge(
201                                cycle.half_edges().nth_circular(0),
202                                |edge, core| {
203                                    [edge
204                                        .reverse_curve_coordinate_systems(core)]
205                                },
206                                core,
207                            )
208                            .join_to(
209                                bad.face.region().exterior(),
210                                0..=0,
211                                1..=1,
212                                core,
213                            )
214                    },
215                    core,
216                )
217            },
218            core,
219        );
220        let cbd = Face::triangle([c, b, d], core).update_region(
221            |region, core| {
222                region.update_exterior(
223                    |cycle, core| {
224                        cycle
225                            .update_half_edge(
226                                cycle.half_edges().nth_circular(0),
227                                |edge, core| {
228                                    [edge
229                                        .reverse_curve_coordinate_systems(core)]
230                                },
231                                core,
232                            )
233                            .update_half_edge(
234                                cycle.half_edges().nth_circular(1),
235                                |edge, core| {
236                                    [edge
237                                        .reverse_curve_coordinate_systems(core)]
238                                },
239                                core,
240                            )
241                            .update_half_edge(
242                                cycle.half_edges().nth_circular(2),
243                                |edge, core| {
244                                    [edge
245                                        .reverse_curve_coordinate_systems(core)]
246                                },
247                                core,
248                            )
249                            .join_to(
250                                abc.face.region().exterior(),
251                                0..=0,
252                                1..=1,
253                                core,
254                            )
255                            .join_to(
256                                bad.face.region().exterior(),
257                                1..=1,
258                                2..=2,
259                                core,
260                            )
261                            .join_to(
262                                dac.face.region().exterior(),
263                                2..=2,
264                                2..=2,
265                                core,
266                            )
267                    },
268                    core,
269                )
270            },
271            core,
272        );
273
274        let triangles =
275            [abc, bad, dac, cbd].map(|triangle| triangle.insert(core));
276        let shell =
277            Shell::new(triangles.iter().map(|triangle| triangle.face.clone()));
278
279        let [abc, bad, dac, cbd] = triangles;
280
281        TetrahedronShell {
282            shell,
283            abc,
284            bad,
285            dac,
286            cbd,
287        }
288    }
289}
290
291impl BuildShell for Shell {}
292
293/// A tetrahedron
294///
295/// A tetrahedron is constructed from 4 points and has 4 faces. For the purpose
296/// of naming the fields of this struct, the points are named `a`, `b`, `c`, and
297/// `d`, in the order in which they are passed.
298///
299/// Returned by [`BuildShell::tetrahedron`].
300pub struct TetrahedronShell<I: IsInserted = IsInsertedNo> {
301    /// The shell that forms the tetrahedron
302    pub shell: I::T<Shell>,
303
304    /// The face formed by the points `a`, `b`, and `c`.
305    pub abc: Polygon<3, IsInsertedYes>,
306
307    /// The face formed by the points `b`, `a`, and `d`.
308    pub bad: Polygon<3, IsInsertedYes>,
309
310    /// The face formed by the points `d`, `a`, and `c`.
311    pub dac: Polygon<3, IsInsertedYes>,
312
313    /// The face formed by the points `c`, `b`, and `d`.
314    pub cbd: Polygon<3, IsInsertedYes>,
315}