Skip to main content

dcel/
split.rs

1// SPDX-FileCopyrightText: 2026 dcel contributors
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use maplike::{Get, Insert, Push};
6
7use crate::{Dcel, EdgeId, Face, FaceId, HalfEdge, HalfEdgeId, Vertex, VertexId};
8
9impl<
10    VW: Clone,
11    HEW: Clone + Default,
12    FW: Clone + Default,
13    VC: Get<usize, Value = Vertex<VW>> + Insert<usize> + Push<usize>,
14    HEC: Get<usize, Value = HalfEdge<HEW>> + Insert<usize> + Push<usize>,
15    FC: Get<usize, Value = Face<FW>> + Insert<usize> + Push<usize>,
16> Dcel<VW, HEW, FW, VC, HEC, FC>
17{
18    pub fn split_edge_by_vertex(
19        &mut self,
20        edge_to_split: EdgeId,
21        vertex: VW,
22    ) -> (VertexId, EdgeId) {
23        let (source, _) = self.edge_endpoints(edge_to_split);
24        let forward = edge_to_split.lesser();
25        let backward = edge_to_split.greater();
26        let face = self.incident_face(forward);
27        let twin_face = self.incident_face(backward);
28        let (forward_weight, backward_weight) = self.edge_weights(edge_to_split);
29        let (forward_weight, backward_weight) = (forward_weight.clone(), backward_weight.clone());
30
31        let new_vertex = self.add_unwired_vertex(vertex);
32        let (new_forward, _) = self.add_unwired_edge(
33            source,
34            new_vertex,
35            face,
36            twin_face,
37            forward_weight.clone(),
38            backward_weight.clone(),
39        );
40        let new_edge = self.full_edge(new_forward);
41
42        // Retarget the original edge to start at the new vertex.
43        self.half_edges.insert(
44            forward.id(),
45            HalfEdge {
46                source: new_vertex,
47                ..self.half_edges.get(&forward.id()).unwrap().clone()
48            },
49        );
50
51        let forward_prev = self.prev(forward);
52        let backward_next = self.next(backward);
53
54        // Insert the new edge in the forward face cycle.
55        self.link_subsequent_half_edges(forward_prev, new_edge.lesser());
56        self.link_subsequent_half_edges(new_edge.lesser(), forward);
57
58        // Insert the new edge in the backward face cycle.
59        self.link_subsequent_half_edges(backward, new_edge.greater());
60        self.link_subsequent_half_edges(new_edge.greater(), backward_next);
61
62        // Update the outgoing spokes of vertices.
63        // XXX: Is this really needed?
64        self.link_vertex_with_half_edge(new_vertex, forward);
65        if self.vertex_representative(source) == forward {
66            self.link_vertex_with_half_edge(source, new_edge.lesser());
67        }
68
69        (new_vertex, new_edge)
70    }
71
72    pub fn split_face_by_edge(
73        &mut self,
74        from: VertexId,
75        to: VertexId,
76        face_to_split: FaceId,
77    ) -> ((HalfEdgeId, HalfEdgeId), FaceId) {
78        let (mut new_edges, new_face) = self.split_face_by_edge_chain(from, to, [], face_to_split);
79        let new_edge = new_edges
80            .pop()
81            .expect("split_face_by_edge should create exactly one edge");
82
83        (new_edge, new_face)
84    }
85
86    pub fn split_face_by_edge_chain(
87        &mut self,
88        from: VertexId,
89        to: VertexId,
90        vertex_weights: impl IntoIterator<Item = VW>,
91        face_to_split: FaceId,
92    ) -> (Vec<(HalfEdgeId, HalfEdgeId)>, FaceId) {
93        self.split_face_by_edge_chain_with_all_weights(
94            from,
95            to,
96            vertex_weights,
97            std::iter::repeat((HEW::default(), HEW::default())),
98            face_to_split,
99            FW::default(),
100        )
101    }
102}
103
104impl<
105    VW: Clone,
106    HEW: Clone,
107    FW: Clone,
108    VC: Get<usize, Value = Vertex<VW>> + Insert<usize> + Push<usize>,
109    HEC: Get<usize, Value = HalfEdge<HEW>> + Insert<usize> + Push<usize>,
110    FC: Get<usize, Value = Face<FW>> + Insert<usize> + Push<usize>,
111> Dcel<VW, HEW, FW, VC, HEC, FC>
112{
113    pub fn split_face_by_edge_chain_with_all_weights(
114        &mut self,
115        from: VertexId,
116        to: VertexId,
117        vertex_weights: impl IntoIterator<Item = VW>,
118        edge_weights: impl IntoIterator<Item = (HEW, HEW)>,
119        face_to_split: FaceId,
120        new_face_weight: FW,
121    ) -> (Vec<(HalfEdgeId, HalfEdgeId)>, FaceId) {
122        let from_incoming = self.find_prev_incident_half_spoke(from, face_to_split);
123        let from_outgoing = self.find_incident_half_spoke(from, face_to_split);
124        let to_incoming = self.find_prev_incident_half_spoke(to, face_to_split);
125        let to_outgoing = self.find_incident_half_spoke(to, face_to_split);
126
127        let new_face = self.add_unwired_face(new_face_weight);
128        let (mut new_edges, last_vertex, last_edge_weight) = self.add_unwired_dangling_edge_chain(
129            from,
130            vertex_weights,
131            edge_weights,
132            face_to_split,
133            new_face,
134        );
135        let (forward, backward) = self.add_unwired_edge(
136            last_vertex,
137            to,
138            face_to_split,
139            new_face,
140            last_edge_weight.0,
141            last_edge_weight.1,
142        );
143        new_edges.push((forward, backward));
144
145        let mut face_to_split_directed_edges: Vec<(HalfEdgeId, HalfEdgeId)> = vec![];
146        face_to_split_directed_edges.push((from_incoming, self.twin(from_incoming)));
147        face_to_split_directed_edges.extend(new_edges.iter().copied());
148        face_to_split_directed_edges.push((to_outgoing, self.twin(to_outgoing)));
149
150        // TODO: No need to run the whole loop here actually.
151        let mut curr_half_edge = self.next(to_outgoing);
152        while curr_half_edge != from_incoming {
153            face_to_split_directed_edges.push((curr_half_edge, self.twin(curr_half_edge)));
154            curr_half_edge = self.next(curr_half_edge);
155        }
156
157        let mut new_face_directed_edges: Vec<(HalfEdgeId, HalfEdgeId)> = vec![];
158        new_face_directed_edges.push((to_incoming, self.twin(to_incoming)));
159        new_face_directed_edges.extend(
160            new_edges
161                .iter()
162                .rev()
163                .map(|(forward, backward)| (*backward, *forward)),
164        );
165        new_face_directed_edges.push((from_outgoing, self.twin(from_outgoing)));
166
167        // TODO: No need to run the whole loop here actually.
168        let mut curr_half_edge = self.next(from_outgoing);
169        while curr_half_edge != to_incoming {
170            new_face_directed_edges.push((curr_half_edge, self.twin(curr_half_edge)));
171            curr_half_edge = self.next(curr_half_edge);
172        }
173
174        self.wire_inner_half_edge_chain(
175            face_to_split,
176            &face_to_split_directed_edges
177                .iter()
178                .map(|(forward, _)| *forward)
179                .collect::<Vec<HalfEdgeId>>(),
180        );
181        self.wire_inner_half_edge_chain(
182            new_face,
183            &new_face_directed_edges
184                .iter()
185                .map(|(forward, _)| *forward)
186                .collect::<Vec<HalfEdgeId>>(),
187        );
188
189        (new_edges, new_face)
190    }
191
192    fn add_unwired_dangling_edge_chain(
193        &mut self,
194        from: VertexId,
195        dangling_vertex_weights: impl IntoIterator<Item = VW>,
196        edge_weights: impl IntoIterator<Item = (HEW, HEW)>,
197        face: FaceId,
198        twin_face: FaceId,
199    ) -> (Vec<(HalfEdgeId, HalfEdgeId)>, VertexId, (HEW, HEW)) {
200        let mut edge_weights = edge_weights.into_iter();
201        let mut edges = vec![];
202        let mut last_vertex = from;
203
204        for (vertex_weight, edge_weight) in dangling_vertex_weights
205            .into_iter()
206            .zip(edge_weights.by_ref())
207        {
208            let (new_edge, new_vertex) = self.add_unwired_dangling_edge(
209                last_vertex,
210                vertex_weight,
211                edge_weight,
212                face,
213                twin_face,
214            );
215
216            edges.push(new_edge);
217            last_vertex = new_vertex;
218        }
219
220        (edges, last_vertex, edge_weights.next().unwrap())
221    }
222
223    fn add_unwired_dangling_edge(
224        &mut self,
225        from: VertexId,
226        dangling_vertex_weight: VW,
227        edge_weights: (HEW, HEW),
228        face: FaceId,
229        twin_face: FaceId,
230    ) -> ((HalfEdgeId, HalfEdgeId), VertexId) {
231        let dangling_vertex = self.add_unwired_vertex(dangling_vertex_weight);
232        let (forward, backward) = self.add_unwired_edge(
233            from,
234            dangling_vertex,
235            face,
236            twin_face,
237            edge_weights.0,
238            edge_weights.1,
239        );
240        let dangling_edge = (forward, backward);
241
242        (dangling_edge, dangling_vertex)
243    }
244}
245
246#[cfg(all(test, feature = "stable-vec"))]
247mod test {
248    use crate::{
249        EdgeId, FaceId, HalfEdgeId, StableDcel, VertexId, assert_face_boundary,
250        init_dcel_with_3x3_hex_mesh,
251    };
252
253    #[test]
254    fn test_split_edge_by_vertex() {
255        let mut dcel = init_dcel_with_3x3_hex_mesh!(StableDcel<(i32, i32)>);
256        let face = FaceId::new(5);
257        let edge = EdgeId::new(HalfEdgeId::new(38), HalfEdgeId::new(39));
258        let old_source = dcel.source(edge.lesser());
259
260        let (_new_vertex, new_edge) = dcel.split_edge_by_vertex(edge, (259, 150));
261
262        assert_eq!(dcel.vertices().num_elements(), 31);
263        assert_eq!(dcel.half_edges().num_elements(), 78);
264        assert_eq!(dcel.faces().num_elements(), 10);
265
266        assert_face_boundary!(&dcel, 0, 0);
267        assert_face_boundary!(&dcel, 1, 6);
268        assert_face_boundary!(&dcel, 2, 6);
269        assert_face_boundary!(&dcel, 3, 6);
270        assert_face_boundary!(&dcel, 4, 7);
271        assert_face_boundary!(&dcel, 5, 7);
272        assert_face_boundary!(&dcel, 6, 6);
273        assert_face_boundary!(&dcel, 7, 6);
274        assert_face_boundary!(&dcel, 8, 6);
275        assert_face_boundary!(&dcel, 9, 6);
276    }
277
278    #[test]
279    fn test_split_face_by_edge() {
280        let mut dcel = init_dcel_with_3x3_hex_mesh!(StableDcel<(i32, i32)>);
281        let face_to_split = FaceId::new(5);
282        let face_to_split_vertices: Vec<VertexId> = dcel.face_vertices(face_to_split).collect();
283
284        // Split face 5 in two with a single edge.
285        let (_new_edge, _new_face) = dcel.split_face_by_edge(
286            face_to_split_vertices[0],
287            face_to_split_vertices[3],
288            face_to_split,
289        );
290
291        // There are now eleven faces in total: one unbounded and ten bounded.
292        assert_eq!(dcel.vertices().num_elements(), 30);
293        assert_eq!(dcel.half_edges().num_elements(), 78);
294        assert_eq!(dcel.faces().num_elements(), 11);
295
296        // The original hexagon is now split into two quads.
297        assert_face_boundary!(&dcel, 0, 0);
298        assert_face_boundary!(&dcel, 1, 6);
299        assert_face_boundary!(&dcel, 2, 6);
300        assert_face_boundary!(&dcel, 3, 6);
301        assert_face_boundary!(&dcel, 4, 6);
302        // Second quad face.
303        assert_face_boundary!(&dcel, 5, 4);
304        assert_face_boundary!(&dcel, 6, 6);
305        assert_face_boundary!(&dcel, 7, 6);
306        assert_face_boundary!(&dcel, 8, 6);
307        assert_face_boundary!(&dcel, 9, 6);
308        // Second quad face.
309        assert_face_boundary!(&dcel, 10, 4);
310    }
311
312    #[test]
313    fn test_split_face_by_chain_of_two_edges() {
314        let mut dcel = init_dcel_with_3x3_hex_mesh!(StableDcel<(i32, i32)>);
315        let face_to_split = FaceId::new(5);
316        let face_to_split_vertices: Vec<VertexId> = dcel.face_vertices(face_to_split).collect();
317
318        // Split face 5 in two with a chain of two edges, with their common
319        // point around the face's center.
320        let (_new_edges, _new_face) = dcel.split_face_by_edge_chain(
321            face_to_split_vertices[0],
322            face_to_split_vertices[3],
323            [(259, 150)],
324            face_to_split,
325        );
326
327        // There are now eleven faces in total: one unbounded and ten bounded.
328        assert_eq!(dcel.vertices().num_elements(), 31);
329        assert_eq!(dcel.half_edges().num_elements(), 80);
330        assert_eq!(dcel.faces().num_elements(), 11);
331
332        // The original hexagon is now split into two pentagons.
333
334        assert_face_boundary!(&dcel, 0, 0);
335        assert_face_boundary!(&dcel, 1, 6);
336        assert_face_boundary!(&dcel, 2, 6);
337        assert_face_boundary!(&dcel, 3, 6);
338        assert_face_boundary!(&dcel, 4, 6);
339        // First pentagon face.
340        assert_face_boundary!(&dcel, 5, 5);
341        assert_face_boundary!(&dcel, 6, 6);
342        assert_face_boundary!(&dcel, 7, 6);
343        assert_face_boundary!(&dcel, 8, 6);
344        assert_face_boundary!(&dcel, 9, 6);
345        // Second pentagon face.
346        assert_face_boundary!(&dcel, 10, 5);
347    }
348}