Skip to main content

dcel/
lib.rs

1// SPDX-FileCopyrightText: 2026 dcel contributors
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5mod get;
6mod insert;
7mod iter;
8mod merge;
9mod remove;
10mod split;
11mod track;
12mod triangulate;
13mod walkers;
14
15#[cfg(test)]
16pub mod test_common;
17
18#[cfg(feature = "stable-vec")]
19mod stable_vec;
20
21#[cfg(feature = "stable-vec")]
22pub use stable_vec::StableDcel;
23
24#[cfg(feature = "rstar")]
25mod rstar;
26
27#[cfg(feature = "rstar")]
28pub use rstar::{RTreedDcel, RTreedStableDcel};
29
30use maplike::{Get, Insert, Push};
31
32pub use walkers::{
33    HalfSpokesIter, HalfSpokesReverseIter, HalfSpokesReverseWalker, HalfSpokesWalker, SpokesIter,
34    SpokesReverseIter, SpokesReverseWalker, SpokesWalker,
35};
36
37#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
38pub struct VertexId(usize);
39
40impl VertexId {
41    #[inline]
42    pub fn new(id: usize) -> Self {
43        Self(id)
44    }
45
46    #[inline]
47    pub fn id(self) -> usize {
48        self.0
49    }
50}
51
52#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
53pub struct HalfEdgeId(usize);
54
55impl HalfEdgeId {
56    #[inline]
57    pub fn new(id: usize) -> Self {
58        Self(id)
59    }
60
61    #[inline]
62    pub fn id(self) -> usize {
63        self.0
64    }
65}
66
67#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
68pub struct EdgeId(HalfEdgeId, HalfEdgeId);
69
70impl EdgeId {
71    #[inline]
72    pub(crate) fn new(half_edge1: HalfEdgeId, half_edge2: HalfEdgeId) -> EdgeId {
73        Self(
74            std::cmp::min(half_edge1, half_edge2),
75            std::cmp::max(half_edge1, half_edge2),
76        )
77    }
78
79    #[inline]
80    pub fn lesser(self) -> HalfEdgeId {
81        self.0
82    }
83
84    #[inline]
85    pub fn greater(self) -> HalfEdgeId {
86        self.1
87    }
88}
89
90#[derive(Clone, Copy, Debug, Eq, PartialEq)]
91pub struct FaceId(usize);
92
93impl FaceId {
94    #[inline]
95    pub fn new(id: usize) -> Self {
96        Self(id)
97    }
98
99    #[inline]
100    pub fn id(self) -> usize {
101        self.0
102    }
103}
104
105#[derive(Clone, Debug)]
106pub struct Vertex<VW> {
107    outgoing_next_half_edge: HalfEdgeId,
108    weight: VW,
109}
110
111#[derive(Clone, Debug)]
112pub struct HalfEdge<HEW> {
113    origin: VertexId,
114    twin: HalfEdgeId,
115    prev: HalfEdgeId,
116    next: HalfEdgeId,
117    face: FaceId,
118    weight: HEW,
119}
120
121#[derive(Clone, Debug)]
122pub struct Face<FW> {
123    incident_half_edge: Option<HalfEdgeId>,
124    weight: FW,
125}
126
127#[derive(Clone, Debug)]
128pub struct Dcel<
129    VW,
130    HEW = (),
131    FW = (),
132    VC = Vec<Vertex<VW>>,
133    HEC = Vec<HalfEdge<HEW>>,
134    FC = Vec<Face<FW>>,
135> {
136    vertexes: VC,
137    half_edges: HEC,
138    faces: FC,
139    vertex_weight_marker: std::marker::PhantomData<VW>,
140    half_edge_weight_marker: std::marker::PhantomData<HEW>,
141    face_weight_marker: std::marker::PhantomData<FW>,
142}
143
144impl<VW, HEW, FW: Default, VC: Default, HEC: Default, FC: Default + Push<usize, Value = Face<FW>>>
145    Dcel<VW, HEW, FW, VC, HEC, FC>
146{
147    #[inline]
148    pub fn new() -> Self {
149        let mut faces = FC::default();
150
151        // Push the outermost face.
152        faces.push(Face {
153            incident_half_edge: None,
154            weight: FW::default(),
155        });
156
157        Self {
158            vertexes: VC::default(),
159            half_edges: HEC::default(),
160            faces,
161            vertex_weight_marker: std::marker::PhantomData,
162            half_edge_weight_marker: std::marker::PhantomData,
163            face_weight_marker: std::marker::PhantomData,
164        }
165    }
166}
167
168impl<VW, HEW, FW: Default, VC: Default, HEC: Default, FC: Default + Push<usize, Value = Face<FW>>>
169    Default for Dcel<VW, HEW, FW, VC, HEC, FC>
170{
171    #[inline]
172    fn default() -> Self {
173        Self::new()
174    }
175}
176
177impl<VW, HEW, FW, VC, HEC, FC> Dcel<VW, HEW, FW, VC, HEC, FC> {
178    #[inline]
179    pub fn from_collections(vertexes: VC, half_edges: HEC, faces: FC) -> Self {
180        Self {
181            vertexes,
182            half_edges,
183            faces,
184            vertex_weight_marker: std::marker::PhantomData,
185            half_edge_weight_marker: std::marker::PhantomData,
186            face_weight_marker: std::marker::PhantomData,
187        }
188    }
189}
190
191impl<VW, HEW, FW, VC, HEC, FC> Dcel<VW, HEW, FW, VC, HEC, FC>
192where
193    for<'a> &'a VC: IntoIterator<Item = &'a usize>,
194{
195    #[inline]
196    pub fn vertex_ids(&self) -> impl Iterator<Item = VertexId> {
197        self.vertexes.into_iter().map(|id| VertexId(*id))
198    }
199}
200
201impl<VW, HEW, FW, VC, HEC, FC> Dcel<VW, HEW, FW, VC, HEC, FC>
202where
203    for<'a> &'a HEC: IntoIterator<Item = &'a usize>,
204{
205    #[inline]
206    pub fn half_edge_ids(&self) -> impl Iterator<Item = HalfEdgeId> {
207        self.half_edges.into_iter().map(|id| HalfEdgeId(*id))
208    }
209}
210
211impl<VW, HEW, FW, VC, HEC, FC> Dcel<VW, HEW, FW, VC, HEC, FC>
212where
213    for<'a> &'a FC: IntoIterator<Item = &'a usize>,
214{
215    #[inline]
216    pub fn face_ids(&self) -> impl Iterator<Item = FaceId> {
217        self.faces.into_iter().map(|id| FaceId(*id))
218    }
219}
220
221impl<
222    VW: Clone,
223    HEW: Clone,
224    FW: Clone,
225    VC: Get<usize, Value = Vertex<VW>> + Insert<usize>,
226    HEC: Get<usize, Value = HalfEdge<HEW>> + Insert<usize>,
227    FC: Get<usize, Value = Face<FW>> + Insert<usize>,
228> Dcel<VW, HEW, FW, VC, HEC, FC>
229{
230    fn wire_inner_half_edge_chain(&mut self, face: FaceId, half_edges: &[HalfEdgeId]) {
231        let half_edges_circular_tuple_windows = half_edges
232            .iter()
233            .zip(half_edges.iter().skip(1).chain(half_edges.iter().take(1)));
234
235        for (&half_edge, &next_half_edge) in half_edges_circular_tuple_windows {
236            self.link_vertex_with_half_edge(self.origin(half_edge), half_edge);
237            self.link_subsequent_half_edges(half_edge, next_half_edge);
238            self.link_face_with_half_edge(face, half_edge);
239        }
240    }
241
242    fn wire_outer_half_edge_chain_circularly(&mut self, outer_half_edges: &[HalfEdgeId]) {
243        let outer_half_edges_circular_tuple_windows = outer_half_edges.iter().zip(
244            outer_half_edges
245                .iter()
246                .skip(1)
247                .chain(outer_half_edges.iter().take(1)),
248        );
249
250        for (&outer_half_edge, &next_outer_half_edge) in outer_half_edges_circular_tuple_windows {
251            self.link_subsequent_half_edges(outer_half_edge, next_outer_half_edge);
252        }
253    }
254
255    fn wire_outer_half_edge_chain_adjoiningly(
256        &mut self,
257        outer_face: FaceId,
258        outer_half_edges: &[HalfEdgeId],
259    ) {
260        let outer_half_edges_circular_tuple_windows = outer_half_edges.iter().zip(
261            outer_half_edges
262                .iter()
263                .skip(1)
264                .chain(outer_half_edges.iter().take(1)),
265        );
266
267        for (&outer_half_edge, &next_outer_half_edge) in outer_half_edges_circular_tuple_windows {
268            let is_edge_outward = self.face_in_front(outer_half_edge) == outer_face;
269            let is_next_edge_outward = self.face_in_front(next_outer_half_edge) == outer_face;
270
271            if is_edge_outward && is_next_edge_outward {
272                self.link_subsequent_half_edges(next_outer_half_edge, outer_half_edge);
273            } else if !is_edge_outward && is_next_edge_outward {
274                self.link_subsequent_half_edges(
275                    next_outer_half_edge,
276                    self.turn_half_edge(outer_half_edge),
277                );
278            } else if is_edge_outward && !is_next_edge_outward {
279                self.link_subsequent_half_edges(
280                    self.twin(self.turn_back_half_edge(self.twin(next_outer_half_edge))),
281                    outer_half_edge,
282                );
283            } else {
284                // Both subsequent edges are pre-existing, so they are already
285                // fully wired. Nothing to do here.
286            }
287        }
288    }
289}
290
291impl<VW, HEW, FW, VC: Push<usize, Value = Vertex<VW>>, HEC, FC> Dcel<VW, HEW, FW, VC, HEC, FC> {
292    fn add_unwired_vertex(&mut self, weight: VW) -> VertexId {
293        VertexId(self.vertexes.push(Vertex {
294            // Since we do not use optionals, we cannot use `None` as the uninitialized value.
295            // So instead uninitialized edge ids are edge 0.
296            // Initializing `.outward_edge` to a correct value is the
297            // responsibility of the caller.
298            outgoing_next_half_edge: HalfEdgeId(0),
299            weight,
300        }))
301    }
302}
303
304impl<VW: Clone, HEW, FW, VC: Get<usize, Value = Vertex<VW>> + Insert<usize>, HEC, FC>
305    Dcel<VW, HEW, FW, VC, HEC, FC>
306{
307    fn link_vertex_with_half_edge(&mut self, vertex: VertexId, outgoing_half_edge: HalfEdgeId) {
308        self.vertexes.insert(
309            vertex.id(),
310            Vertex {
311                outgoing_next_half_edge: outgoing_half_edge,
312                weight: self.vertexes.get(&vertex.id()).unwrap().weight.clone(),
313            },
314        )
315    }
316}
317
318impl<VW, HEW: Clone, FW, VC, HEC: Insert<usize, Value = HalfEdge<HEW>> + Push<usize>, FC>
319    Dcel<VW, HEW, FW, VC, HEC, FC>
320{
321    fn add_unwired_edge(
322        &mut self,
323        origin: VertexId,
324        twin_origin: VertexId,
325        face: FaceId,
326        twin_face: FaceId,
327        weight: HEW,
328        twin_weight: HEW,
329    ) -> (HalfEdgeId, HalfEdgeId) {
330        let forward_half_edge = HalfEdgeId(self.half_edges.push(HalfEdge {
331            origin,
332            // Uninitialized as edge 0 before until the twin is created in the next few lines of this method.
333            twin: HalfEdgeId(0),
334            // Uninitialized as edge 0. Initializing `.prev` and `.next` to
335            // correct value is the responsibility of the caller.
336            prev: HalfEdgeId(0),
337            next: HalfEdgeId(0),
338            face,
339            weight: weight.clone(),
340        }));
341
342        let backward_half_edge = HalfEdgeId(self.half_edges.push(HalfEdge {
343            origin: twin_origin,
344            twin: forward_half_edge,
345            // Uninitialized as edge 0. Initializing `.prev` and `.next` to
346            // correct value is the responsibility of the caller.
347            prev: HalfEdgeId(0),
348            next: HalfEdgeId(0),
349            face: twin_face,
350            weight: twin_weight,
351        }));
352
353        // Now actually initialize `halfedge0`'s twin.
354        // PERF: This could actually be optimized away by making it the
355        // responsibility of the caller
356        self.half_edges.insert(
357            forward_half_edge.id(),
358            HalfEdge {
359                origin,
360                // Uninitialized as edge 0 before until the twin is created in the next few lines of this method.
361                twin: backward_half_edge,
362                // Uninitialized as edge 0. Initializing `.prev` and `.next` to
363                // correct value is the responsibility of the caller.
364                prev: HalfEdgeId(0),
365                next: HalfEdgeId(0),
366                face,
367                weight,
368            },
369        );
370
371        (forward_half_edge, backward_half_edge)
372    }
373}
374
375impl<VW, HEW: Clone, FW, VC, HEC: Get<usize, Value = HalfEdge<HEW>> + Insert<usize>, FC>
376    Dcel<VW, HEW, FW, VC, HEC, FC>
377{
378    fn link_subsequent_half_edges(&mut self, half_edge: HalfEdgeId, next_half_edge: HalfEdgeId) {
379        self.half_edges.insert(
380            half_edge.id(),
381            HalfEdge {
382                next: next_half_edge,
383                ..self.half_edges.get(&half_edge.id()).unwrap().clone()
384            },
385        );
386        self.half_edges.insert(
387            next_half_edge.id(),
388            HalfEdge {
389                prev: half_edge,
390                ..self.half_edges.get(&next_half_edge.id()).unwrap().clone()
391            },
392        );
393    }
394}
395
396impl<VW, HEW, FW, VC, HEC, FC: Push<usize, Value = Face<FW>>> Dcel<VW, HEW, FW, VC, HEC, FC> {
397    fn add_unwired_face(&mut self, weight: FW) -> FaceId {
398        FaceId(self.faces.push(Face {
399            incident_half_edge: None,
400            weight,
401        }))
402    }
403}
404
405impl<
406    VW,
407    HEW: Clone,
408    FW: Clone,
409    VC,
410    HEC: Get<usize, Value = HalfEdge<HEW>> + Insert<usize>,
411    FC: Get<usize, Value = Face<FW>> + Insert<usize>,
412> Dcel<VW, HEW, FW, VC, HEC, FC>
413{
414    fn link_face_with_half_edge(&mut self, face: FaceId, half_edge: HalfEdgeId) {
415        self.half_edges.insert(
416            half_edge.id(),
417            HalfEdge {
418                face,
419                ..self.half_edges.get(&half_edge.id()).unwrap().clone()
420            },
421        );
422        self.faces.insert(
423            face.id(),
424            Face {
425                incident_half_edge: Some(half_edge),
426                weight: self.faces.get(&face.id()).unwrap().weight.clone(),
427            },
428        );
429    }
430}