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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use fj_interop::ext::ArrayExt;
use fj_math::Point;
use itertools::Itertools;

use crate::{
    objects::{Face, HalfEdge, Shell},
    operations::{
        build::{BuildFace, BuildHalfEdge},
        insert::Insert,
        split::SplitEdge,
        update::{
            UpdateCycle, UpdateFace, UpdateHalfEdge, UpdateRegion, UpdateShell,
        },
    },
    services::Services,
    storage::Handle,
};

/// Split a face into two
pub trait SplitFace: Sized {
    /// Split the face into two
    ///
    /// The line that splits the face is defined by two points, each specified
    /// in local coordinates of an edge.
    ///
    /// # Panics
    ///
    /// Panics, if the half-edges are not part of the boundary of the provided
    /// face.
    ///
    /// # Implementation Note
    ///
    /// The way the split line is specified is rather inconvenient, and not very
    /// flexible. This is an artifact of the current implementation, and more
    /// flexible and convenient ways to split the face (like an arbitrary curve)
    /// can be provided later.
    #[must_use]
    fn split_face(
        &self,
        face: &Handle<Face>,
        line: [(&Handle<HalfEdge>, impl Into<Point<1>>); 2],
        services: &mut Services,
    ) -> (Self, [Handle<Face>; 2]);
}

impl SplitFace for Shell {
    fn split_face(
        &self,
        face: &Handle<Face>,
        line: [(&Handle<HalfEdge>, impl Into<Point<1>>); 2],
        services: &mut Services,
    ) -> (Self, [Handle<Face>; 2]) {
        // The code below might assume that the half-edges that define the line
        // are part of the face's exterior. Let's make that explicit here.
        //
        // This is actually the only time we're using `face` in this method, as
        // it's going to get replaced with a new version as soon as we split the
        // edges. We could probably do without it, but not taking it would
        // probably make validating that both half-edges belong to the same face
        // more difficult, as well as make the method signature less intuitive.
        //
        // Something to think about though!
        {
            let [(a, _), (b, _)] = line.each_ref_ext();

            let exterior = face.region().exterior();

            assert!(exterior.half_edges().contains(a));
            assert!(exterior.half_edges().contains(b));
        }

        let mut self_ = self.clone();

        let [[a, b], [c, d]] = line.map(|(half_edge, point)| {
            let (shell, [[a, b], _]) =
                self_.split_edge(half_edge, point, services);
            self_ = shell;
            [a, b]
        });

        // The original face doesn't exist in the updated shell, as it's been
        // replaced by a new version due to the edge splitting. Let's find the
        // face that replaced it.
        let mut updated_face_after_split_edges = None;
        for f in self_.faces() {
            let half_edges = f.region().exterior().half_edges();

            if half_edges.contains(&a)
                && half_edges.contains(&b)
                && half_edges.contains(&c)
                && half_edges.contains(&d)
            {
                assert!(
                    updated_face_after_split_edges.is_none(),
                    "There should never be two faces that share half-edges"
                );
                updated_face_after_split_edges = Some(f);
            }
        }
        let updated_face_after_split_edges = updated_face_after_split_edges
            .expect("Updated shell must contain updated face");

        // Build the edge that's going to divide the new faces.
        let dividing_half_edge_a_to_d = HalfEdge::line_segment(
            [b.start_position(), d.start_position()],
            None,
            services,
        )
        .update_start_vertex(|_| b.start_vertex().clone())
        .insert(services);
        let dividing_half_edge_c_to_b = HalfEdge::from_sibling(
            &dividing_half_edge_a_to_d,
            d.start_vertex().clone(),
        )
        .insert(services);

        let mut half_edges_of_face_starting_at_b =
            updated_face_after_split_edges
                .region()
                .exterior()
                .half_edges()
                .iter()
                .cloned()
                .cycle()
                .skip_while(|half_edge| half_edge != &b);

        let half_edges_b_to_c_inclusive = half_edges_of_face_starting_at_b
            .take_while_ref(|half_edge| half_edge != &d);
        let split_face_a = Face::unbound(
            updated_face_after_split_edges.surface().clone(),
            services,
        )
        .update_region(|region| {
            region
                .update_exterior(|cycle| {
                    cycle
                        .add_half_edges(half_edges_b_to_c_inclusive)
                        .add_half_edges([dividing_half_edge_c_to_b])
                        .insert(services)
                })
                .insert(services)
        })
        .insert(services);

        // The previous operation has moved the iterator along.
        let half_edges_of_face_starting_at_d = half_edges_of_face_starting_at_b;

        let half_edges_d_to_a_inclusive = half_edges_of_face_starting_at_d
            .take_while(|half_edge| half_edge != &b);
        let split_face_b = Face::unbound(
            updated_face_after_split_edges.surface().clone(),
            services,
        )
        .update_region(|region| {
            region
                .update_exterior(|cycle| {
                    cycle
                        .add_half_edges(half_edges_d_to_a_inclusive)
                        .add_half_edges([dividing_half_edge_a_to_d])
                        .insert(services)
                })
                .insert(services)
        })
        .insert(services);

        let faces = [split_face_a, split_face_b];
        let self_ = self_
            .replace_face(updated_face_after_split_edges, |_| faces.clone());

        (self_, faces)
    }
}