Skip to main content

dcel/
insert.rs

1// SPDX-FileCopyrightText: 2026 dcel contributors
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use std::hash::Hash;
6
7use maplike::{Get, Insert, Push};
8
9use crate::{
10    Dcel, EdgeId, Face, FaceId, HalfEdge, Vertex, VertexId,
11    track::{EdgesTracker, VertexTracker},
12};
13
14impl<
15    VW: Clone + Eq + Hash,
16    HEW: Clone + Default,
17    FW: Clone + Default,
18    VC: Get<usize, Value = Vertex<VW>> + Insert<usize> + Push<usize>,
19    HEC: Get<usize, Value = HalfEdge<HEW>> + Insert<usize> + Push<usize>,
20    FC: Get<usize, Value = Face<FW>> + Insert<usize> + Push<usize>,
21> Dcel<VW, HEW, FW, VC, HEC, FC>
22{
23    pub fn insert_mesh(
24        &mut self,
25        face_polygons: impl IntoIterator<Item = impl IntoIterator<Item = VW>>,
26    ) -> Vec<FaceId> {
27        self.insert_mesh_in_face(face_polygons)
28    }
29
30    pub fn insert_mesh_in_face(
31        &mut self,
32        face_polygons: impl IntoIterator<Item = impl IntoIterator<Item = VW>>,
33    ) -> Vec<FaceId> {
34        let mut vertex_weights_counter = VertexTracker::new();
35        let mut edges_tracker = EdgesTracker::new();
36        let mut faces = vec![];
37
38        for face_polygon in face_polygons {
39            faces.push(self.insert_adjoined_polygon(
40                &mut vertex_weights_counter,
41                &mut edges_tracker,
42                face_polygon,
43            ));
44        }
45
46        faces
47    }
48
49    fn insert_adjoined_polygon(
50        &mut self,
51        vertex_weights_counter: &mut VertexTracker<VW>,
52        edges_tracker: &mut EdgesTracker,
53        vertex_weights: impl IntoIterator<Item = VW>,
54    ) -> FaceId {
55        self.insert_adjoined_polygon_in_face(
56            vertex_weights_counter,
57            edges_tracker,
58            self.unbounded_face(),
59            vertex_weights,
60        )
61    }
62
63    fn insert_adjoined_polygon_in_face(
64        &mut self,
65        vertex_weights_counter: &mut VertexTracker<VW>,
66        edges_tracker: &mut EdgesTracker,
67        outer_face: FaceId,
68        vertex_weights: impl IntoIterator<Item = VW>,
69    ) -> FaceId {
70        self.insert_adjoined_polygon_in_face_with_all_weights(
71            vertex_weights_counter,
72            edges_tracker,
73            outer_face,
74            vertex_weights,
75            std::iter::repeat((HEW::default(), HEW::default())),
76            FW::default(),
77        )
78    }
79}
80
81impl<
82    VW: Clone,
83    HEW: Clone + Default,
84    FW: Clone + Default,
85    VC: Get<usize, Value = Vertex<VW>> + Insert<usize> + Push<usize>,
86    HEC: Get<usize, Value = HalfEdge<HEW>> + Insert<usize> + Push<usize>,
87    FC: Get<usize, Value = Face<FW>> + Insert<usize> + Push<usize>,
88> Dcel<VW, HEW, FW, VC, HEC, FC>
89{
90    pub fn insert_polygon(&mut self, vertex_weights: impl IntoIterator<Item = VW>) -> FaceId {
91        self.insert_polygon_in_face(self.unbounded_face(), vertex_weights)
92    }
93
94    pub fn insert_polygon_in_face(
95        &mut self,
96        outer_face: FaceId,
97        vertex_weights: impl IntoIterator<Item = VW>,
98    ) -> FaceId {
99        self.insert_polygon_in_face_with_all_weights(
100            outer_face,
101            vertex_weights,
102            std::iter::repeat((HEW::default(), HEW::default())),
103            FW::default(),
104        )
105    }
106}
107
108impl<
109    VW: Clone + Eq + Hash,
110    HEW: Clone,
111    FW: Clone,
112    VC: Get<usize, Value = Vertex<VW>> + Insert<usize> + Push<usize>,
113    HEC: Get<usize, Value = HalfEdge<HEW>> + Insert<usize> + Push<usize>,
114    FC: Get<usize, Value = Face<FW>> + Insert<usize> + Push<usize>,
115> Dcel<VW, HEW, FW, VC, HEC, FC>
116{
117    fn insert_adjoined_polygon_with_all_weights(
118        &mut self,
119        vertex_weights_counter: &mut VertexTracker<VW>,
120        edges_tracker: &mut EdgesTracker,
121        vertex_weights: impl IntoIterator<Item = VW>,
122        edge_weights: impl IntoIterator<Item = (HEW, HEW)>,
123        face_weight: FW,
124    ) -> FaceId {
125        self.insert_adjoined_polygon_in_face_with_all_weights(
126            vertex_weights_counter,
127            edges_tracker,
128            self.unbounded_face(),
129            vertex_weights,
130            edge_weights,
131            face_weight,
132        )
133    }
134
135    fn insert_adjoined_polygon_in_face_with_all_weights(
136        &mut self,
137        vertex_weights_tracker: &mut VertexTracker<VW>,
138        edges_tracker: &mut EdgesTracker,
139        outer_face: FaceId,
140        vertex_weights: impl IntoIterator<Item = VW>,
141        edge_weights: impl IntoIterator<Item = (HEW, HEW)>,
142        face_weight: FW,
143    ) -> FaceId {
144        let new_face = self.add_unwired_face(face_weight);
145        let vertexes =
146            self.add_deduplicated_unwired_polygon_vertexes(vertex_weights_tracker, vertex_weights);
147        let edges = self.add_adjoined_unwired_polygon_edges(
148            edges_tracker,
149            &vertexes,
150            new_face,
151            outer_face,
152            edge_weights,
153        );
154
155        self.wire_outer_half_edge_chain_adjoiningly(outer_face, &edges);
156        self.wire_inner_half_edge_chain(new_face, &edges);
157
158        new_face
159    }
160
161    fn add_deduplicated_unwired_polygon_vertexes(
162        &mut self,
163        vertex_weights_counter: &mut VertexTracker<VW>,
164        vertex_weights: impl IntoIterator<Item = VW>,
165    ) -> Vec<VertexId> {
166        vertex_weights
167            .into_iter()
168            .map(|weight| {
169                vertex_weights_counter
170                    .vertex(weight.clone())
171                    .unwrap_or_else(|| {
172                        let vertex = self.add_unwired_vertex(weight.clone());
173                        vertex_weights_counter.visit_vertex(weight, vertex);
174                        vertex
175                    })
176            })
177            .collect()
178    }
179
180    fn add_adjoined_unwired_polygon_edges(
181        &mut self,
182        edges_tracker: &mut EdgesTracker,
183        vertexes: &[VertexId],
184        new_face: FaceId,
185        outer_face: FaceId,
186        edge_weights: impl IntoIterator<Item = (HEW, HEW)>,
187    ) -> Vec<EdgeId> {
188        let vertexes_circular_tuple_windows = vertexes
189            .iter()
190            .zip(vertexes.iter().skip(1).chain(vertexes.iter().take(1)));
191
192        let mut edges = vec![];
193
194        for ((&from_vertex, &to_vertex), (forward_half_edge_weight, backward_half_edge_weight)) in
195            vertexes_circular_tuple_windows.zip(edge_weights)
196        {
197            let edge = if let Some(existing_half_edge) =
198                edges_tracker.vertexes_half_edge(to_vertex, from_vertex)
199            {
200                // Reuse already existing shared edge.
201                let reused_edge = self.full_edge(self.twin(existing_half_edge));
202
203                // Make the forward half-edge point to the new face.
204                self.half_edges.insert(
205                    reused_edge.forward().id(),
206                    HalfEdge {
207                        face: new_face,
208                        ..self
209                            .half_edges
210                            .get(&reused_edge.forward().id())
211                            .unwrap()
212                            .clone()
213                    },
214                );
215
216                reused_edge
217            } else {
218                // There is no preexisting shared edge. Add a new one.
219                self.add_unwired_edge(
220                    from_vertex,
221                    to_vertex,
222                    new_face,
223                    outer_face,
224                    forward_half_edge_weight,
225                    backward_half_edge_weight,
226                )
227            };
228
229            edges_tracker.visit_vertexes_edge(from_vertex, to_vertex, edge.forward());
230            edges.push(edge);
231        }
232
233        edges
234    }
235}
236
237impl<
238    VW: Clone,
239    HEW: Clone,
240    FW: Clone,
241    VC: Get<usize, Value = Vertex<VW>> + Insert<usize> + Push<usize>,
242    HEC: Get<usize, Value = HalfEdge<HEW>> + Insert<usize> + Push<usize>,
243    FC: Get<usize, Value = Face<FW>> + Insert<usize> + Push<usize>,
244> Dcel<VW, HEW, FW, VC, HEC, FC>
245{
246    pub fn insert_polygon_with_all_weights(
247        &mut self,
248        vertex_weights: impl IntoIterator<Item = VW>,
249        edge_weights: impl IntoIterator<Item = (HEW, HEW)>,
250        face_weight: FW,
251    ) -> FaceId {
252        self.insert_polygon_in_face_with_all_weights(
253            self.unbounded_face(),
254            vertex_weights,
255            edge_weights,
256            face_weight,
257        )
258    }
259
260    pub fn insert_polygon_in_face_with_all_weights(
261        &mut self,
262        outer_face: FaceId,
263        vertex_weights: impl IntoIterator<Item = VW>,
264        edge_weights: impl IntoIterator<Item = (HEW, HEW)>,
265        face_weight: FW,
266    ) -> FaceId {
267        let new_face = self.add_unwired_face(face_weight);
268        let vertexes = self.add_unwired_polygon_vertexes(vertex_weights);
269        let edges = self.add_unwired_polygon_edges(&vertexes, new_face, outer_face, edge_weights);
270
271        self.wire_outer_half_edge_chain_circularly(&edges);
272        self.wire_inner_half_edge_chain(new_face, &edges);
273
274        new_face
275    }
276
277    fn add_unwired_polygon_vertexes(
278        &mut self,
279        vertex_weights: impl IntoIterator<Item = VW>,
280    ) -> Vec<VertexId> {
281        vertex_weights
282            .into_iter()
283            .map(|vertex_weight| self.add_unwired_vertex(vertex_weight))
284            .collect()
285    }
286
287    fn add_unwired_polygon_edges(
288        &mut self,
289        vertexes: &[VertexId],
290        new_face: FaceId,
291        outer_face: FaceId,
292        edge_weights: impl IntoIterator<Item = (HEW, HEW)>,
293    ) -> Vec<EdgeId> {
294        let vertexes_circular_tuple_windows = vertexes
295            .iter()
296            .zip(vertexes.iter().skip(1).chain(vertexes.iter().take(1)));
297
298        let mut edges = vec![];
299
300        for ((from_vertex, to_vertex), (forward_half_edge_weight, backward_half_edge_weight)) in
301            vertexes_circular_tuple_windows.zip(edge_weights)
302        {
303            let edge = self.add_unwired_edge(
304                *from_vertex,
305                *to_vertex,
306                new_face,
307                outer_face,
308                forward_half_edge_weight,
309                backward_half_edge_weight,
310            );
311            edges.push(edge);
312        }
313
314        edges
315    }
316}
317
318#[cfg(test)]
319mod test {
320    use crate::HalfEdgeId;
321
322    use super::*;
323
324    #[test]
325    fn test_insert_adjoined_squares() {
326        let two_adjoined_squares: Vec<Vec<(i64, i64)>> = vec![
327            // Left square (CCW).
328            vec![(0, 0), (0, 1), (-1, 1), (-1, 0)],
329            // Right square sharing edge with left square (CCW).
330            vec![(0, 0), (1, 0), (1, 1), (0, 1)],
331        ];
332
333        let mut dcel: Dcel<(i64, i64)> = Dcel::new();
334        dcel.insert_mesh(two_adjoined_squares);
335
336        assert_eq!(dcel.vertexes().len(), 6);
337        assert_eq!(dcel.half_edges().len(), 14);
338        assert_eq!(dcel.faces().len(), 3);
339
340        assert_eq!(
341            dcel.face_vertexes(FaceId::new(0))
342                .collect::<Vec<VertexId>>()
343                .len(),
344            0
345        );
346        assert_eq!(
347            dcel.face_half_edges(FaceId::new(0))
348                .collect::<Vec<HalfEdgeId>>()
349                .len(),
350            0
351        );
352        assert_eq!(
353            dcel.face_edges(FaceId::new(0))
354                .collect::<Vec<EdgeId>>()
355                .len(),
356            0
357        );
358        assert_eq!(
359            dcel.face_half_edges(FaceId::new(1))
360                .collect::<Vec<HalfEdgeId>>()
361                .len(),
362            4
363        );
364        assert_eq!(
365            dcel.face_edges(FaceId::new(1))
366                .collect::<Vec<EdgeId>>()
367                .len(),
368            4
369        );
370        assert_eq!(
371            dcel.face_half_edges(FaceId::new(1))
372                .collect::<Vec<HalfEdgeId>>()
373                .len(),
374            4
375        );
376        assert_eq!(
377            dcel.face_edges(FaceId::new(2))
378                .collect::<Vec<EdgeId>>()
379                .len(),
380            4
381        );
382    }
383
384    #[test]
385    fn test_insert_noisy_4x4_grid_mesh() {
386        // A 2D grid mesh of 4x4 faces with slightly noisified (i64, i64)
387        // coordinates.
388        let mesh: Vec<Vec<(i64, i64)>> = vec![
389            // Row 1.
390            vec![(2, 3), (48, -2), (53, 54), (-4, 48)],
391            vec![(48, -2), (102, 4), (98, 51), (53, 54)],
392            vec![(102, 4), (155, -3), (147, 55), (98, 51)],
393            vec![(155, -3), (201, 2), (197, 49), (147, 55)],
394            // Row 2.
395            vec![(-4, 48), (53, 54), (47, 103), (3, 98)],
396            vec![(53, 54), (98, 51), (104, 97), (47, 103)],
397            vec![(98, 51), (147, 55), (149, 102), (104, 97)],
398            vec![(147, 55), (197, 49), (203, 99), (149, 102)],
399            // Row 3.
400            vec![(3, 98), (47, 103), (51, 155), (-2, 147)],
401            vec![(47, 103), (104, 97), (97, 149), (51, 155)],
402            vec![(104, 97), (149, 102), (155, 148), (97, 149)],
403            vec![(149, 102), (203, 99), (198, 152), (155, 148)],
404            // Row 4.
405            vec![(-2, 147), (51, 155), (49, 204), (5, 197)],
406            vec![(51, 155), (97, 149), (102, 198), (49, 204)],
407            vec![(97, 149), (155, 148), (146, 201), (102, 198)],
408            vec![(155, 148), (198, 152), (204, 199), (146, 201)],
409        ];
410
411        let mut dcel: Dcel<(i64, i64)> = Dcel::new();
412        dcel.insert_mesh(mesh);
413
414        for (i, _face) in dcel.faces().iter().enumerate() {
415            if FaceId::new(i) == dcel.unbounded_face() {
416                assert_eq!(
417                    dcel.face_vertexes(FaceId::new(i))
418                        .collect::<Vec<VertexId>>()
419                        .len(),
420                    0
421                );
422                assert_eq!(
423                    dcel.face_half_edges(FaceId::new(i))
424                        .collect::<Vec<HalfEdgeId>>()
425                        .len(),
426                    0
427                );
428                assert_eq!(
429                    dcel.face_edges(FaceId::new(i))
430                        .collect::<Vec<EdgeId>>()
431                        .len(),
432                    0
433                );
434                continue;
435            }
436
437            assert_eq!(
438                dcel.face_vertexes(FaceId::new(i))
439                    .collect::<Vec<VertexId>>()
440                    .len(),
441                4
442            );
443            assert_eq!(
444                dcel.face_half_edges(FaceId::new(i))
445                    .collect::<Vec<HalfEdgeId>>()
446                    .len(),
447                4
448            );
449            assert_eq!(
450                dcel.face_edges(FaceId::new(i))
451                    .collect::<Vec<EdgeId>>()
452                    .len(),
453                4
454            );
455        }
456
457        assert_eq!(dcel.vertexes().len(), 25);
458        assert_eq!(dcel.half_edges().len(), 80);
459        assert_eq!(dcel.faces().len(), 17);
460    }
461}