1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use fj_math::Point;

use crate::{
    objects::{Face, Objects, Shell},
    operations::{Insert, JoinCycle, UpdateFace},
    services::Service,
    storage::Handle,
};

use super::BuildFace;

/// Build a [`Shell`]
pub trait BuildShell {
    /// Build a tetrahedron from the provided points
    ///
    /// Accepts 4 points, naturally. For the purposes of the following
    /// discussion, let's call those `a`, `b`, `c`, and `d`, and assume that the
    /// order they are listed in here matches the order they are provided in
    /// within the array.
    ///
    /// Assumes that `a`, `b`, and `c` form a triangle in counter-clockwise
    /// order, when arranging the viewpoint such that it is on the opposite side
    /// of the triangle from `d`. If this assumption is met, the orientation of
    /// all faces of the tetrahedron will be valid, meaning their
    /// counter-clockwise sides are outside.
    ///
    /// # Implementation Note
    ///
    /// In principle, this method doesn't need to make assumptions about the
    /// order of the points provided. It could, given some extra effort, just
    /// build a correct tetrahedron, regardless of that order.
    fn tetrahedron(
        points: [impl Into<Point<3>>; 4],
        objects: &mut Service<Objects>,
    ) -> Tetrahedron {
        let [a, b, c, d] = points.map(Into::into);

        let face_abc = Face::triangle([a, b, c], objects).face;
        let face_bad =
            Face::triangle([b, a, d], objects)
                .face
                .update_exterior(|cycle| {
                    cycle
                        .join_to(face_abc.exterior(), 0..=0, 0..=0, objects)
                        .insert(objects)
                });
        let face_dac =
            Face::triangle([d, a, c], objects)
                .face
                .update_exterior(|cycle| {
                    cycle
                        .join_to(face_abc.exterior(), 1..=1, 2..=2, objects)
                        .join_to(face_bad.exterior(), 0..=0, 1..=1, objects)
                        .insert(objects)
                });
        let face_cbd =
            Face::triangle([c, b, d], objects)
                .face
                .update_exterior(|cycle| {
                    cycle
                        .join_to(face_abc.exterior(), 0..=0, 1..=1, objects)
                        .join_to(face_bad.exterior(), 1..=1, 2..=2, objects)
                        .join_to(face_dac.exterior(), 2..=2, 2..=2, objects)
                        .insert(objects)
                });

        let faces = [face_abc, face_bad, face_dac, face_cbd]
            .map(|face| face.insert(objects));
        let shell = Shell::new(faces.clone());

        let [face_abc, face_bad, face_dac, face_cbd] = faces;

        Tetrahedron {
            shell,
            face_abc,
            face_bad,
            face_dac,
            face_cbd,
        }
    }
}

impl BuildShell for Shell {}

/// A tetrahedron
///
/// A tetrahedron is constructed from 4 points and has 4 faces. For the purpose
/// of naming the fields of this struct, the points are named `a`, `b`, `c`, and
/// `d`, in the order in which they are passed.
///
/// Returned by [`BuildShell::tetrahedron`].
pub struct Tetrahedron {
    /// The shell that forms the tetrahedron
    pub shell: Shell,

    /// The face formed by the points `a`, `b`, and `c`.
    pub face_abc: Handle<Face>,

    /// The face formed by the points `b`, `a`, and `d`.
    pub face_bad: Handle<Face>,

    /// The face formed by the points `d`, `a`, and `c`.
    pub face_dac: Handle<Face>,

    /// The face formed by the points `c`, `b`, and `d`.
    pub face_cbd: Handle<Face>,
}