Skip to main content

dcel/
iter.rs

1// SPDX-FileCopyrightText: 2026 dcel contributors
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use maplike::Get;
6
7use crate::{
8    Dcel, Face, FaceId, HalfEdge, HalfEdgeId, Vertex, VertexId,
9    walkers::{
10        CirculateEdgesWithExcludesIter, CirculateEdgesWithExcludesReverseIter,
11        CirculateEdgesWithExcludesReverseWalker, CirculateEdgesWithExcludesWalker,
12        CirculateHalfEdgesWithExcludesIter, CirculateHalfEdgesWithExcludesReverseIter,
13        CirculateHalfEdgesWithExcludesReverseWalker, CirculateHalfEdgesWithExcludesWalker,
14        CirculateHalfSpokesIter, CirculateHalfSpokesReverseIter, CirculateVertexesWithExcludesIter,
15        CirculateVertexesWithExcludesReverseIter, CirculateVertexesWithExcludesReverseWalker,
16        CirculateVertexesWithExcludesWalker, CirculationHalfSpokesReverseWalker,
17        CirculationHalfSpokesWalker, CirculationInterspokesIter, CirculationInterspokesReverseIter,
18        CirculationInterspokesReverseWalker, CirculationInterspokesWalker, CirculationSpokesIter,
19        CirculationSpokesReverseIter, CirculationSpokesReverseWalker, CirculationSpokesWalker,
20        HalfSpokesIter, HalfSpokesReverseIter, HalfSpokesReverseWalker, HalfSpokesWalker,
21        InterspokesIter, InterspokesReverseIter, InterspokesReverseWalker, InterspokesWalker,
22        SpokesIter, SpokesReverseIter, SpokesReverseWalker, SpokesWalker,
23    },
24};
25
26impl<VW, HEW, FW, VC: Get<usize, Value = Vertex<VW>>, HEC, FC> Dcel<VW, HEW, FW, VC, HEC, FC> {
27    #[inline]
28    pub fn vertex_half_spokes(
29        &self,
30        vertex: VertexId,
31    ) -> HalfSpokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
32        self.half_spokes(self.vertex_representative(vertex))
33    }
34
35    #[inline]
36    pub fn vertex_half_spokes_reverse(
37        &self,
38        vertex: VertexId,
39    ) -> HalfSpokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
40        self.half_spokes_reverse(self.vertex_representative(vertex))
41    }
42}
43
44impl<VW, HEW, FW, VC: Get<usize, Value = Vertex<VW>>, HEC: Get<usize, Value = HalfEdge<HEW>>, FC>
45    Dcel<VW, HEW, FW, VC, HEC, FC>
46{
47    #[inline]
48    pub fn vertex_rim_vertices(
49        &self,
50        vertex: VertexId,
51    ) -> CirculateVertexesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
52        CirculateVertexesWithExcludesWalker {
53            circulator: self.vertex_rim_half_edges(vertex).walker(),
54        }
55        .iter(self)
56    }
57
58    #[inline]
59    pub fn vertex_rim_vertices_reverse(
60        &self,
61        vertex: VertexId,
62    ) -> CirculateVertexesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
63        CirculateVertexesWithExcludesReverseWalker {
64            circulator: self.vertex_rim_half_edges_reverse(vertex).walker(),
65        }
66        .iter(self)
67    }
68}
69
70impl<VW, HEW, FW, VC: Get<usize, Value = Vertex<VW>>, HEC: Get<usize, Value = HalfEdge<HEW>>, FC>
71    Dcel<VW, HEW, FW, VC, HEC, FC>
72{
73    #[inline]
74    pub fn vertex_rim_half_edges(
75        &self,
76        vertex: VertexId,
77    ) -> CirculateHalfEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
78        let initial_half_edge = self.next(self.vertex_representative(vertex));
79        let excluded_half_edges = self
80            .vertex_spokes(vertex)
81            .flat_map(|edge| [edge.lesser(), edge.greater()])
82            .collect::<Vec<HalfEdgeId>>();
83
84        // Boundary vertices do not have cycles circulating them.
85        // Return an empty walker-iterator instead.
86        if self.check_if_boundary_vertex(vertex) {
87            return CirculateHalfEdgesWithExcludesWalker {
88                initial_half_edge,
89                curr_half_edge: None,
90                excluded_half_edges,
91            }
92            .iter(self);
93        }
94
95        self.circulate_half_edges_with_excludes(initial_half_edge, excluded_half_edges)
96    }
97
98    #[inline]
99    pub fn vertex_rim_half_edges_reverse(
100        &self,
101        vertex: VertexId,
102    ) -> CirculateHalfEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
103        let initial_half_edge = self.prev(self.twin(self.vertex_representative(vertex)));
104        let excluded_half_edges = self
105            .vertex_spokes(vertex)
106            .flat_map(|edge| [edge.lesser(), edge.greater()])
107            .collect::<Vec<HalfEdgeId>>();
108
109        // Boundary vertices do not have cycles circulating them.
110        // Return an empty walker-iterator instead.
111        if self.check_if_boundary_vertex(vertex) {
112            return CirculateHalfEdgesWithExcludesReverseWalker {
113                initial_half_edge,
114                curr_half_edge: None,
115                excluded_half_edges,
116            }
117            .iter(self);
118        }
119
120        self.circulate_half_edges_with_excludes_reverse(initial_half_edge, excluded_half_edges)
121    }
122}
123
124impl<VW, HEW, FW, VC, HEC, FC> Dcel<VW, HEW, FW, VC, HEC, FC> {
125    #[inline]
126    pub fn half_spokes(
127        &self,
128        initial_half_edge: HalfEdgeId,
129    ) -> HalfSpokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
130        HalfSpokesWalker {
131            initial_half_edge,
132            curr_half_edge: Some(initial_half_edge),
133        }
134        .iter(self)
135    }
136
137    #[inline]
138    pub fn half_spokes_reverse(
139        &self,
140        initial_half_edge: HalfEdgeId,
141    ) -> HalfSpokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
142        HalfSpokesReverseWalker {
143            initial_half_edge,
144            curr_half_edge: Some(initial_half_edge),
145        }
146        .iter(self)
147    }
148}
149
150impl<VW, HEW, FW, VC: Get<usize, Value = Vertex<VW>>, HEC: Get<usize, Value = HalfEdge<HEW>>, FC>
151    Dcel<VW, HEW, FW, VC, HEC, FC>
152{
153    #[inline]
154    pub fn vertex_spokes(&self, vertex: VertexId) -> SpokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
155        self.spokes(self.vertex_representative(vertex))
156    }
157
158    #[inline]
159    pub fn vertex_spokes_reverse(
160        &self,
161        vertex: VertexId,
162    ) -> SpokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
163        self.spokes_reverse(self.vertex_representative(vertex))
164    }
165}
166
167impl<VW, HEW, FW, VC: Get<usize, Value = Vertex<VW>>, HEC: Get<usize, Value = HalfEdge<HEW>>, FC>
168    Dcel<VW, HEW, FW, VC, HEC, FC>
169{
170    #[inline]
171    pub fn vertex_rim_edges(
172        &self,
173        vertex: VertexId,
174    ) -> CirculateEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
175        CirculateEdgesWithExcludesWalker {
176            circulator: self.vertex_rim_half_edges(vertex).walker(),
177        }
178        .iter(self)
179    }
180
181    #[inline]
182    pub fn vertex_rim_edges_reverse(
183        &self,
184        vertex: VertexId,
185    ) -> CirculateEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
186        CirculateEdgesWithExcludesReverseWalker {
187            circulator: self.vertex_rim_half_edges_reverse(vertex).walker(),
188        }
189        .iter(self)
190    }
191}
192
193impl<VW, HEW, FW, VC: Get<usize, Value = Vertex<VW>>, HEC: Get<usize, Value = HalfEdge<HEW>>, FC>
194    Dcel<VW, HEW, FW, VC, HEC, FC>
195{
196    #[inline]
197    pub fn vertex_interspokes(
198        &self,
199        vertex: VertexId,
200    ) -> InterspokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
201        self.interspokes(self.vertex_representative(vertex))
202    }
203
204    #[inline]
205    pub fn vertex_interspokes_reverse(
206        &self,
207        vertex: VertexId,
208    ) -> InterspokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
209        self.interspokes_reverse(self.vertex_representative(vertex))
210    }
211}
212
213impl<VW, HEW, FW, VC, HEC: Get<usize, Value = HalfEdge<HEW>>, FC> Dcel<VW, HEW, FW, VC, HEC, FC> {
214    #[inline]
215    pub fn spokes(
216        &self,
217        initial_half_edge: HalfEdgeId,
218    ) -> SpokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
219        SpokesWalker {
220            circulator: HalfSpokesWalker {
221                initial_half_edge,
222                curr_half_edge: Some(initial_half_edge),
223            },
224        }
225        .iter(self)
226    }
227
228    #[inline]
229    pub fn spokes_reverse(
230        &self,
231        initial_half_edge: HalfEdgeId,
232    ) -> SpokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
233        SpokesReverseWalker {
234            circulator: HalfSpokesReverseWalker {
235                initial_half_edge,
236                curr_half_edge: Some(initial_half_edge),
237            },
238        }
239        .iter(self)
240    }
241
242    #[inline]
243    pub fn interspokes(
244        &self,
245        initial_half_edge: HalfEdgeId,
246    ) -> InterspokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
247        InterspokesWalker {
248            initial_half_edge,
249            curr_half_edge: Some(initial_half_edge),
250        }
251        .iter(self)
252    }
253
254    #[inline]
255    pub fn interspokes_reverse(
256        &self,
257        initial_half_edge: HalfEdgeId,
258    ) -> InterspokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
259        InterspokesReverseWalker {
260            initial_half_edge,
261            curr_half_edge: Some(initial_half_edge),
262        }
263        .iter(self)
264    }
265}
266
267impl<VW, HEW, FW, VC, HEC: Get<usize, Value = HalfEdge<HEW>>, FC> Dcel<VW, HEW, FW, VC, HEC, FC> {
268    #[inline]
269    pub fn circulate_vertices(
270        &self,
271        initial_half_edge: HalfEdgeId,
272    ) -> CirculateVertexesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
273        self.circulate_vertices_with_excludes(initial_half_edge, std::iter::empty())
274    }
275
276    #[inline]
277    pub fn circulate_vertices_reverse(
278        &self,
279        initial_half_edge: HalfEdgeId,
280    ) -> CirculateVertexesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
281        self.circulate_vertices_with_excludes_reverse(initial_half_edge, std::iter::empty())
282    }
283
284    #[inline]
285    pub fn circulate_vertices_with_excludes(
286        &self,
287        initial_half_edge: HalfEdgeId,
288        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
289    ) -> CirculateVertexesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
290        CirculateVertexesWithExcludesWalker {
291            circulator: self
292                .circulate_half_edges_with_excludes(initial_half_edge, excluded_half_edges)
293                .walker(),
294        }
295        .iter(self)
296    }
297
298    #[inline]
299    pub fn circulate_vertices_with_excludes_reverse(
300        &self,
301        initial_half_edge: HalfEdgeId,
302        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
303    ) -> CirculateVertexesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
304        CirculateVertexesWithExcludesReverseWalker {
305            circulator: self
306                .circulate_half_edges_with_excludes_reverse(initial_half_edge, excluded_half_edges)
307                .walker(),
308        }
309        .iter(self)
310    }
311
312    #[inline]
313    pub fn circulate_half_edges(
314        &self,
315        initial_half_edge: HalfEdgeId,
316    ) -> CirculateHalfEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
317        self.circulate_half_edges_with_excludes(initial_half_edge, std::iter::empty())
318    }
319
320    #[inline]
321    pub fn circulate_half_edges_reverse(
322        &self,
323        initial_half_edge: HalfEdgeId,
324    ) -> CirculateHalfEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
325        self.circulate_half_edges_with_excludes_reverse(initial_half_edge, std::iter::empty())
326    }
327
328    #[inline]
329    pub fn circulate_half_edges_with_excludes(
330        &self,
331        initial_half_edge: HalfEdgeId,
332        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
333    ) -> CirculateHalfEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
334        CirculateHalfEdgesWithExcludesWalker {
335            initial_half_edge,
336            curr_half_edge: Some(initial_half_edge),
337            excluded_half_edges: excluded_half_edges.into_iter().collect(),
338        }
339        .iter(self)
340    }
341
342    #[inline]
343    pub fn circulate_half_edges_with_excludes_reverse(
344        &self,
345        initial_half_edge: HalfEdgeId,
346        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
347    ) -> CirculateHalfEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
348        CirculateHalfEdgesWithExcludesReverseWalker {
349            initial_half_edge,
350            curr_half_edge: Some(initial_half_edge),
351            excluded_half_edges: excluded_half_edges.into_iter().collect(),
352        }
353        .iter(self)
354    }
355
356    #[inline]
357    pub fn circulate_edges(
358        &self,
359        initial_half_edge: HalfEdgeId,
360    ) -> CirculateEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
361        self.circulate_edges_with_excludes(initial_half_edge, std::iter::empty())
362    }
363
364    #[inline]
365    pub fn circulate_edges_reverse(
366        &self,
367        initial_half_edge: HalfEdgeId,
368    ) -> CirculateEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
369        self.circulate_edges_with_excludes_reverse(initial_half_edge, std::iter::empty())
370    }
371
372    #[inline]
373    pub fn circulate_edges_with_excludes(
374        &self,
375        initial_half_edge: HalfEdgeId,
376        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
377    ) -> CirculateEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
378        CirculateEdgesWithExcludesWalker {
379            circulator: self
380                .circulate_half_edges_with_excludes(initial_half_edge, excluded_half_edges)
381                .walker(),
382        }
383        .iter(self)
384    }
385
386    #[inline]
387    pub fn circulate_edges_with_excludes_reverse(
388        &self,
389        initial_half_edge: HalfEdgeId,
390        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
391    ) -> CirculateEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
392        CirculateEdgesWithExcludesReverseWalker {
393            circulator: self
394                .circulate_half_edges_with_excludes_reverse(initial_half_edge, excluded_half_edges)
395                .walker(),
396        }
397        .iter(self)
398    }
399
400    #[inline]
401    pub fn circulation_half_spokes(
402        &self,
403        initial_half_edge: HalfEdgeId,
404        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
405    ) -> CirculateHalfSpokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
406        CirculationHalfSpokesWalker {
407            circulator: self
408                .circulate_half_edges_with_excludes(initial_half_edge, excluded_half_edges)
409                .walker(),
410            half_spokes_walker: self.half_spokes(initial_half_edge).walker(),
411            prev_half_edge: self.prev(initial_half_edge),
412        }
413        .iter(self)
414    }
415
416    #[inline]
417    pub fn circulation_half_spokes_reverse(
418        &self,
419        initial_half_edge: HalfEdgeId,
420        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
421    ) -> CirculateHalfSpokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
422        CirculationHalfSpokesReverseWalker {
423            circulator: self
424                .circulate_half_edges_with_excludes_reverse(initial_half_edge, excluded_half_edges)
425                .walker(),
426            half_spokes_walker: self.half_spokes(initial_half_edge).walker(),
427            prev_half_edge: self.next(initial_half_edge),
428        }
429        .iter(self)
430    }
431
432    #[inline]
433    pub fn circulation_spokes(
434        &self,
435        initial_half_edge: HalfEdgeId,
436        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
437    ) -> CirculationSpokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
438        CirculationSpokesWalker {
439            circulator: CirculationHalfSpokesWalker {
440                circulator: self
441                    .circulate_half_edges_with_excludes(initial_half_edge, excluded_half_edges)
442                    .walker(),
443                half_spokes_walker: self.half_spokes(initial_half_edge).walker(),
444                prev_half_edge: self.prev(initial_half_edge),
445            },
446        }
447        .iter(self)
448    }
449
450    #[inline]
451    pub fn circulation_spokes_reverse(
452        &self,
453        initial_half_edge: HalfEdgeId,
454        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
455    ) -> CirculationSpokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
456        CirculationSpokesReverseWalker {
457            circulator: CirculationHalfSpokesReverseWalker {
458                circulator: self
459                    .circulate_half_edges_with_excludes_reverse(
460                        initial_half_edge,
461                        excluded_half_edges,
462                    )
463                    .walker(),
464                half_spokes_walker: self.half_spokes(initial_half_edge).walker(),
465                prev_half_edge: self.next(initial_half_edge),
466            },
467        }
468        .iter(self)
469    }
470
471    #[inline]
472    pub fn circulation_interspokes(
473        &self,
474        initial_half_edge: HalfEdgeId,
475        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
476    ) -> CirculationInterspokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
477        CirculationInterspokesWalker {
478            circulator: CirculationHalfSpokesWalker {
479                circulator: self
480                    .circulate_half_edges_with_excludes(initial_half_edge, excluded_half_edges)
481                    .walker(),
482                half_spokes_walker: self.half_spokes(initial_half_edge).walker(),
483                prev_half_edge: self.prev(initial_half_edge),
484            },
485        }
486        .iter(self)
487    }
488
489    #[inline]
490    pub fn circulation_interspokes_reverse(
491        &self,
492        initial_half_edge: HalfEdgeId,
493        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
494    ) -> CirculationInterspokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
495        CirculationInterspokesReverseWalker {
496            circulator: CirculationHalfSpokesReverseWalker {
497                circulator: self
498                    .circulate_half_edges_with_excludes_reverse(
499                        initial_half_edge,
500                        excluded_half_edges,
501                    )
502                    .walker(),
503                half_spokes_walker: self.half_spokes(initial_half_edge).walker(),
504                prev_half_edge: self.prev(initial_half_edge),
505            },
506        }
507        .iter(self)
508    }
509
510    #[inline]
511    pub fn circulation_rim_half_edges(
512        &self,
513        initial_half_edge: HalfEdgeId,
514        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
515    ) -> CirculateHalfEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
516        let excluded_half_edges = excluded_half_edges.into_iter().collect::<Vec<HalfEdgeId>>();
517
518        let mut rim_excluded_half_edges: Vec<HalfEdgeId> = self
519            .circulate_edges(initial_half_edge)
520            .flat_map(|edge| [edge.lesser(), edge.greater()])
521            .collect();
522        rim_excluded_half_edges.extend(
523            self.circulation_spokes(initial_half_edge, excluded_half_edges.iter().copied())
524                .flat_map(|edge| [edge.lesser(), edge.greater()]),
525        );
526        rim_excluded_half_edges.extend(excluded_half_edges.iter().copied());
527
528        let Some(initial_half_spoke) = self
529            .circulation_half_spokes(initial_half_edge, excluded_half_edges.iter().copied())
530            .next()
531        else {
532            return CirculateHalfEdgesWithExcludesWalker {
533                initial_half_edge,
534                curr_half_edge: None,
535                excluded_half_edges: rim_excluded_half_edges,
536            }
537            .iter(self);
538        };
539
540        let initial_half_edge = self
541            .half_spokes(self.twin(initial_half_spoke))
542            .find(|half_spoke| !rim_excluded_half_edges.contains(half_spoke))
543            .unwrap();
544
545        self.circulate_half_edges_with_excludes(initial_half_edge, rim_excluded_half_edges)
546    }
547
548    #[inline]
549    pub fn circulation_rim_half_edges_reverse(
550        &self,
551        initial_half_edge: HalfEdgeId,
552        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
553    ) -> CirculateHalfEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
554        let excluded_half_edges = excluded_half_edges.into_iter().collect::<Vec<HalfEdgeId>>();
555
556        let mut rim_excluded_half_edges: Vec<HalfEdgeId> = self
557            .circulate_edges_reverse(initial_half_edge)
558            .flat_map(|edge| [edge.lesser(), edge.greater()])
559            .collect();
560        rim_excluded_half_edges.extend(
561            self.circulation_spokes_reverse(initial_half_edge, excluded_half_edges.iter().copied())
562                .flat_map(|edge| [edge.lesser(), edge.greater()]),
563        );
564        rim_excluded_half_edges.extend(excluded_half_edges.iter().copied());
565
566        let Some(initial_half_spoke) = self
567            .circulation_half_spokes_reverse(initial_half_edge, excluded_half_edges.iter().copied())
568            .next()
569        else {
570            return CirculateHalfEdgesWithExcludesReverseWalker {
571                initial_half_edge,
572                curr_half_edge: None,
573                excluded_half_edges: rim_excluded_half_edges,
574            }
575            .iter(self);
576        };
577
578        let initial_half_edge = self
579            // XXX: Not sure why, but `.half_spokes_reverse` here makes the
580            // tests fail.
581            .half_spokes(self.twin(initial_half_spoke))
582            .find(|half_spoke| !rim_excluded_half_edges.contains(half_spoke))
583            .unwrap();
584
585        self.circulate_half_edges_with_excludes_reverse(initial_half_edge, rim_excluded_half_edges)
586    }
587
588    #[inline]
589    pub fn circulation_rim_edges(
590        &self,
591        initial_half_edge: HalfEdgeId,
592        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
593    ) -> CirculateEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
594        CirculateEdgesWithExcludesWalker {
595            circulator: self
596                .circulation_rim_half_edges(initial_half_edge, excluded_half_edges)
597                .walker(),
598        }
599        .iter(self)
600    }
601
602    #[inline]
603    pub fn circulation_rim_edges_reverse(
604        &self,
605        initial_half_edge: HalfEdgeId,
606        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
607    ) -> CirculateEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
608        CirculateEdgesWithExcludesReverseWalker {
609            circulator: self
610                .circulation_rim_half_edges_reverse(initial_half_edge, excluded_half_edges)
611                .walker(),
612        }
613        .iter(self)
614    }
615
616    #[inline]
617    pub fn circulation_rim_vertices(
618        &self,
619        initial_half_edge: HalfEdgeId,
620        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
621    ) -> CirculateVertexesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
622        CirculateVertexesWithExcludesWalker {
623            circulator: self
624                .circulation_rim_half_edges(initial_half_edge, excluded_half_edges)
625                .walker(),
626        }
627        .iter(self)
628    }
629
630    #[inline]
631    pub fn circulation_rim_vertices_reverse(
632        &self,
633        initial_half_edge: HalfEdgeId,
634        excluded_half_edges: impl IntoIterator<Item = HalfEdgeId>,
635    ) -> CirculateVertexesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
636        CirculateVertexesWithExcludesReverseWalker {
637            circulator: self
638                .circulation_rim_half_edges_reverse(initial_half_edge, excluded_half_edges)
639                .walker(),
640        }
641        .iter(self)
642    }
643}
644
645impl<
646    VW,
647    HEW,
648    FW,
649    VC: Get<usize, Value = Vertex<VW>>,
650    HEC: Get<usize, Value = HalfEdge<HEW>>,
651    FC: Get<usize, Value = Face<FW>>,
652> Dcel<VW, HEW, FW, VC, HEC, FC>
653{
654    #[inline]
655    pub fn face_vertices(
656        &self,
657        face: FaceId,
658    ) -> CirculateVertexesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
659        CirculateVertexesWithExcludesWalker {
660            circulator: self.face_half_edges(face).walker(),
661        }
662        .iter(self)
663    }
664
665    #[inline]
666    pub fn face_vertices_reverse(
667        &self,
668        face: FaceId,
669    ) -> CirculateVertexesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
670        CirculateVertexesWithExcludesReverseWalker {
671            circulator: self.face_half_edges_reverse(face).walker(),
672        }
673        .iter(self)
674    }
675}
676
677impl<VW, HEW, FW, VC, HEC, FC: Get<usize, Value = Face<FW>>> Dcel<VW, HEW, FW, VC, HEC, FC> {
678    #[inline]
679    pub fn face_half_edges(
680        &self,
681        face: FaceId,
682    ) -> CirculateHalfEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
683        // Unbounded face has no half-edges. Since the unbounded face is
684        // supposed to behave similarly to other faces, it is better to branch
685        // out here than to have the code below panic.
686        let Some(initial_half_edge) = self.face_representative(face) else {
687            return CirculateHalfEdgesWithExcludesWalker {
688                // Uninitialized half-edge.
689                initial_half_edge: HalfEdgeId::new(0),
690                // Setting `curr_edge` to None makes the iterator produce no
691                // elements.
692                curr_half_edge: None,
693                excluded_half_edges: vec![],
694            }
695            .iter(self);
696        };
697
698        CirculateHalfEdgesWithExcludesWalker {
699            initial_half_edge,
700            curr_half_edge: Some(initial_half_edge),
701            excluded_half_edges: vec![],
702        }
703        .iter(self)
704    }
705
706    #[inline]
707    pub fn face_half_edges_reverse(
708        &self,
709        face: FaceId,
710    ) -> CirculateHalfEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
711        // Unbounded face has no half-edges. Since the unbounded face is
712        // supposed to behave similarly to other faces, it is better to branch
713        // out here than to have the code below panic.
714        let Some(initial_half_edge) = self.face_representative(face) else {
715            return CirculateHalfEdgesWithExcludesReverseWalker {
716                // Uninitialized half-edge.
717                initial_half_edge: HalfEdgeId::new(0),
718                // Setting `curr_edge` to None makes the iterator produce no
719                // elements.
720                curr_half_edge: None,
721                excluded_half_edges: vec![],
722            }
723            .iter(self);
724        };
725
726        CirculateHalfEdgesWithExcludesReverseWalker {
727            initial_half_edge,
728            curr_half_edge: Some(initial_half_edge),
729            excluded_half_edges: vec![],
730        }
731        .iter(self)
732    }
733
734    #[inline]
735    pub fn face_edges(
736        &self,
737        face: FaceId,
738    ) -> CirculateEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
739        CirculateEdgesWithExcludesWalker {
740            circulator: self.face_half_edges(face).walker(),
741        }
742        .iter(self)
743    }
744
745    #[inline]
746    pub fn face_edges_reverse(
747        &self,
748        face: FaceId,
749    ) -> CirculateEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
750        CirculateEdgesWithExcludesReverseWalker {
751            circulator: self.face_half_edges_reverse(face).walker(),
752        }
753        .iter(self)
754    }
755}
756
757impl<VW, HEW, FW, VC, HEC: Get<usize, Value = HalfEdge<HEW>>, FC: Get<usize, Value = Face<FW>>>
758    Dcel<VW, HEW, FW, VC, HEC, FC>
759{
760    #[inline]
761    pub fn face_half_spokes(
762        &self,
763        face: FaceId,
764    ) -> CirculateHalfSpokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
765        // Unbounded face has no half-edges. Since the unbounded face is
766        // supposed to behave similarly to other faces, it is better to branch
767        // out here than to have the code below panic.
768        let Some(initial_half_edge) = self.face_representative(face) else {
769            return CirculationHalfSpokesWalker {
770                circulator: CirculateHalfEdgesWithExcludesWalker {
771                    // Uninitialized half-edge.
772                    initial_half_edge: HalfEdgeId::new(0),
773                    // Setting `curr_edge` to None makes the iterator produce no
774                    // elements.
775                    curr_half_edge: None,
776                    excluded_half_edges: vec![],
777                },
778                half_spokes_walker: HalfSpokesWalker {
779                    initial_half_edge: HalfEdgeId::new(0),
780                    curr_half_edge: None,
781                },
782                prev_half_edge: HalfEdgeId::new(0),
783            }
784            .iter(self);
785        };
786
787        self.circulation_half_spokes(initial_half_edge, std::iter::empty())
788    }
789
790    #[inline]
791    pub fn face_half_spokes_reverse(
792        &self,
793        face: FaceId,
794    ) -> CirculateHalfSpokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
795        // Unbounded face has no half-edges. Since the unbounded face is
796        // supposed to behave similarly to other faces, it is better to branch
797        // out here than to have the code below panic.
798        let Some(initial_half_edge) = self.face_representative(face) else {
799            return CirculationHalfSpokesReverseWalker {
800                circulator: CirculateHalfEdgesWithExcludesReverseWalker {
801                    // Uninitialized half-edge.
802                    initial_half_edge: HalfEdgeId::new(0),
803                    // Setting `curr_edge` to None makes the iterator produce no
804                    // elements.
805                    curr_half_edge: None,
806                    excluded_half_edges: vec![],
807                },
808                half_spokes_walker: HalfSpokesWalker {
809                    initial_half_edge: HalfEdgeId::new(0),
810                    curr_half_edge: None,
811                },
812                prev_half_edge: HalfEdgeId::new(0),
813            }
814            .iter(self);
815        };
816
817        self.circulation_half_spokes_reverse(initial_half_edge, std::iter::empty())
818    }
819
820    #[inline]
821    pub fn face_spokes(&self, face: FaceId) -> CirculationSpokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
822        // Unbounded face has no half-edges. Since the unbounded face is
823        // supposed to behave similarly to other faces, it is better to branch
824        // out here than to have the code below panic.
825        let Some(initial_half_edge) = self.face_representative(face) else {
826            return CirculationSpokesWalker {
827                circulator: CirculationHalfSpokesWalker {
828                    circulator: CirculateHalfEdgesWithExcludesWalker {
829                        // Uninitialized half-edge.
830                        initial_half_edge: HalfEdgeId::new(0),
831                        // Setting `curr_edge` to None makes the iterator produce no
832                        // elements.
833                        curr_half_edge: None,
834                        excluded_half_edges: vec![],
835                    },
836                    half_spokes_walker: HalfSpokesWalker {
837                        initial_half_edge: HalfEdgeId::new(0),
838                        curr_half_edge: None,
839                    },
840                    prev_half_edge: HalfEdgeId::new(0),
841                },
842            }
843            .iter(self);
844        };
845
846        self.circulation_spokes(initial_half_edge, std::iter::empty())
847    }
848
849    #[inline]
850    pub fn face_spokes_reverse(
851        &self,
852        face: FaceId,
853    ) -> CirculationSpokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
854        // Unbounded face has no half-edges. Since the unbounded face is
855        // supposed to behave similarly to other faces, it is better to branch
856        // out here than to have the code below panic.
857        let Some(initial_half_edge) = self.face_representative(face) else {
858            return CirculationSpokesReverseWalker {
859                circulator: CirculationHalfSpokesReverseWalker {
860                    circulator: CirculateHalfEdgesWithExcludesReverseWalker {
861                        // Uninitialized half-edge.
862                        initial_half_edge: HalfEdgeId::new(0),
863                        // Setting `curr_edge` to None makes the iterator produce no
864                        // elements.
865                        curr_half_edge: None,
866                        excluded_half_edges: vec![],
867                    },
868                    half_spokes_walker: HalfSpokesWalker {
869                        initial_half_edge: HalfEdgeId::new(0),
870                        curr_half_edge: None,
871                    },
872                    prev_half_edge: HalfEdgeId::new(0),
873                },
874            }
875            .iter(self);
876        };
877
878        self.circulation_spokes_reverse(initial_half_edge, std::iter::empty())
879    }
880
881    #[inline]
882    pub fn face_interspokes(
883        &self,
884        face: FaceId,
885    ) -> CirculationInterspokesIter<'_, VW, HEW, FW, VC, HEC, FC> {
886        // Unbounded face has no half-edges. Since the unbounded face is
887        // supposed to behave similarly to other faces, it is better to branch
888        // out here than to have the code below panic.
889        let Some(initial_half_edge) = self.face_representative(face) else {
890            return CirculationInterspokesWalker {
891                circulator: CirculationHalfSpokesWalker {
892                    circulator: CirculateHalfEdgesWithExcludesWalker {
893                        // Uninitialized half-edge.
894                        initial_half_edge: HalfEdgeId::new(0),
895                        // Setting `curr_edge` to None makes the iterator produce no
896                        // elements.
897                        curr_half_edge: None,
898                        excluded_half_edges: vec![],
899                    },
900                    half_spokes_walker: HalfSpokesWalker {
901                        initial_half_edge: HalfEdgeId::new(0),
902                        curr_half_edge: None,
903                    },
904                    prev_half_edge: HalfEdgeId::new(0),
905                },
906            }
907            .iter(self);
908        };
909
910        self.circulation_interspokes(initial_half_edge, std::iter::empty())
911    }
912
913    #[inline]
914    pub fn face_interspokes_reverse(
915        &self,
916        face: FaceId,
917    ) -> CirculationInterspokesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
918        // Unbounded face has no half-edges. Since the unbounded face is
919        // supposed to behave similarly to other faces, it is better to branch
920        // out here than to have the code below panic.
921        let Some(initial_half_edge) = self.face_representative(face) else {
922            return CirculationInterspokesReverseWalker {
923                circulator: CirculationHalfSpokesReverseWalker {
924                    circulator: CirculateHalfEdgesWithExcludesReverseWalker {
925                        // Uninitialized half-edge.
926                        initial_half_edge: HalfEdgeId::new(0),
927                        // Setting `curr_edge` to None makes the iterator produce no
928                        // elements.
929                        curr_half_edge: None,
930                        excluded_half_edges: vec![],
931                    },
932                    half_spokes_walker: HalfSpokesWalker {
933                        initial_half_edge: HalfEdgeId::new(0),
934                        curr_half_edge: None,
935                    },
936                    prev_half_edge: HalfEdgeId::new(0),
937                },
938            }
939            .iter(self);
940        };
941
942        self.circulation_interspokes_reverse(initial_half_edge, std::iter::empty())
943    }
944
945    #[inline]
946    pub fn face_rim_half_edges(
947        &self,
948        face: FaceId,
949    ) -> CirculateHalfEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
950        // Unbounded face has no half-edges. Since the unbounded face is
951        // supposed to behave similarly to other faces, it is better to branch
952        // out here than to have the code below panic.
953        let Some(initial_half_edge) = self.face_representative(face) else {
954            return CirculateHalfEdgesWithExcludesWalker {
955                // Uninitialized half-edge.
956                initial_half_edge: HalfEdgeId::new(0),
957                // Setting `curr_edge` to None makes the iterator produce no
958                // elements.
959                curr_half_edge: None,
960                excluded_half_edges: vec![],
961            }
962            .iter(self);
963        };
964
965        self.circulation_rim_half_edges(initial_half_edge, std::iter::empty())
966    }
967
968    #[inline]
969    pub fn face_rim_half_edges_reverse(
970        &self,
971        face: FaceId,
972    ) -> CirculateHalfEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
973        // Unbounded face has no half-edges. Since the unbounded face is
974        // supposed to behave similarly to other faces, it is better to branch
975        // out here than to have the code below panic.
976        let Some(initial_half_edge) = self.face_representative(face) else {
977            return CirculateHalfEdgesWithExcludesReverseWalker {
978                // Uninitialized half-edge.
979                initial_half_edge: HalfEdgeId::new(0),
980                // Setting `curr_edge` to None makes the iterator produce no
981                // elements.
982                curr_half_edge: None,
983                excluded_half_edges: vec![],
984            }
985            .iter(self);
986        };
987
988        self.circulation_rim_half_edges_reverse(initial_half_edge, std::iter::empty())
989    }
990
991    #[inline]
992    pub fn face_rim_vertices(
993        &self,
994        face: FaceId,
995    ) -> CirculateVertexesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
996        // Unbounded face has no half-edges. Since the unbounded face is
997        // supposed to behave similarly to other faces, it is better to branch
998        // out here than to have the code below panic.
999        let Some(initial_half_edge) = self.face_representative(face) else {
1000            return CirculateVertexesWithExcludesWalker {
1001                circulator: CirculateHalfEdgesWithExcludesWalker {
1002                    // Uninitialized half-edge.
1003                    initial_half_edge: HalfEdgeId::new(0),
1004                    // Setting `curr_edge` to None makes the iterator produce no
1005                    // elements.
1006                    curr_half_edge: None,
1007                    excluded_half_edges: vec![],
1008                },
1009            }
1010            .iter(self);
1011        };
1012
1013        self.circulation_rim_vertices(initial_half_edge, std::iter::empty())
1014    }
1015
1016    #[inline]
1017    pub fn face_rim_vertices_reverse(
1018        &self,
1019        face: FaceId,
1020    ) -> CirculateVertexesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
1021        // Unbounded face has no half-edges. Since the unbounded face is
1022        // supposed to behave similarly to other faces, it is better to branch
1023        // out here than to have the code below panic.
1024        let Some(initial_half_edge) = self.face_representative(face) else {
1025            return CirculateVertexesWithExcludesReverseWalker {
1026                circulator: CirculateHalfEdgesWithExcludesReverseWalker {
1027                    // Uninitialized half-edge.
1028                    initial_half_edge: HalfEdgeId::new(0),
1029                    // Setting `curr_edge` to None makes the iterator produce no
1030                    // elements.
1031                    curr_half_edge: None,
1032                    excluded_half_edges: vec![],
1033                },
1034            }
1035            .iter(self);
1036        };
1037
1038        self.circulation_rim_vertices_reverse(initial_half_edge, std::iter::empty())
1039    }
1040
1041    #[inline]
1042    pub fn face_rim_edges(
1043        &self,
1044        face: FaceId,
1045    ) -> CirculateEdgesWithExcludesIter<'_, VW, HEW, FW, VC, HEC, FC> {
1046        // Unbounded face has no half-edges. Since the unbounded face is
1047        // supposed to behave similarly to other faces, it is better to branch
1048        // out here than to have the code below panic.
1049        let Some(initial_half_edge) = self.face_representative(face) else {
1050            return CirculateEdgesWithExcludesWalker {
1051                circulator: CirculateHalfEdgesWithExcludesWalker {
1052                    // Uninitialized half-edge.
1053                    initial_half_edge: HalfEdgeId::new(0),
1054                    // Setting `curr_edge` to None makes the iterator produce no
1055                    // elements.
1056                    curr_half_edge: None,
1057                    excluded_half_edges: vec![],
1058                },
1059            }
1060            .iter(self);
1061        };
1062
1063        self.circulation_rim_edges(initial_half_edge, std::iter::empty())
1064    }
1065
1066    #[inline]
1067    pub fn face_rim_edges_reverse(
1068        &self,
1069        face: FaceId,
1070    ) -> CirculateEdgesWithExcludesReverseIter<'_, VW, HEW, FW, VC, HEC, FC> {
1071        // Unbounded face has no half-edges. Since the unbounded face is
1072        // supposed to behave similarly to other faces, it is better to branch
1073        // out here than to have the code below panic.
1074        let Some(initial_half_edge) = self.face_representative(face) else {
1075            return CirculateEdgesWithExcludesReverseWalker {
1076                circulator: CirculateHalfEdgesWithExcludesReverseWalker {
1077                    // Uninitialized half-edge.
1078                    initial_half_edge: HalfEdgeId::new(0),
1079                    // Setting `curr_edge` to None makes the iterator produce no
1080                    // elements.
1081                    curr_half_edge: None,
1082                    excluded_half_edges: vec![],
1083                },
1084            }
1085            .iter(self);
1086        };
1087
1088        self.circulation_rim_edges_reverse(initial_half_edge, std::iter::empty())
1089    }
1090}
1091
1092#[cfg(test)]
1093mod test {
1094    use crate::{
1095        Dcel, assert_face_boundary, assert_face_rim, assert_face_spokes_interspokes,
1096        assert_vertex_rim, assert_vertex_spokes_interspokes, init_dcel_with_3x3_hex_mesh,
1097    };
1098
1099    #[test]
1100    fn test_vertex_rims() {
1101        let dcel = init_dcel_with_3x3_hex_mesh!(Dcel<(i32, i32)>);
1102
1103        assert_vertex_rim!(&dcel, 0, 0);
1104        assert_vertex_rim!(&dcel, 1, 0);
1105        assert_vertex_rim!(&dcel, 2, 0);
1106        assert_vertex_rim!(&dcel, 3, 0);
1107        assert_vertex_rim!(&dcel, 4, 0);
1108        assert_vertex_rim!(&dcel, 5, 12);
1109        assert_vertex_rim!(&dcel, 6, 0);
1110        assert_vertex_rim!(&dcel, 7, 0);
1111        assert_vertex_rim!(&dcel, 8, 12);
1112        assert_vertex_rim!(&dcel, 9, 12);
1113        assert_vertex_rim!(&dcel, 10, 0);
1114        assert_vertex_rim!(&dcel, 11, 0);
1115        assert_vertex_rim!(&dcel, 12, 12);
1116        assert_vertex_rim!(&dcel, 13, 0);
1117        assert_vertex_rim!(&dcel, 14, 0);
1118        assert_vertex_rim!(&dcel, 15, 12);
1119        assert_vertex_rim!(&dcel, 16, 12);
1120        assert_vertex_rim!(&dcel, 17, 12);
1121        assert_vertex_rim!(&dcel, 18, 12);
1122        assert_vertex_rim!(&dcel, 19, 0);
1123        assert_vertex_rim!(&dcel, 20, 0);
1124        assert_vertex_rim!(&dcel, 21, 0);
1125        assert_vertex_rim!(&dcel, 22, 0);
1126        assert_vertex_rim!(&dcel, 23, 0);
1127        assert_vertex_rim!(&dcel, 24, 0);
1128        assert_vertex_rim!(&dcel, 25, 0);
1129        assert_vertex_rim!(&dcel, 26, 0);
1130        assert_vertex_rim!(&dcel, 27, 0);
1131        assert_vertex_rim!(&dcel, 28, 0);
1132        assert_vertex_rim!(&dcel, 29, 0);
1133    }
1134
1135    #[test]
1136    fn test_vertex_spokes_interspokes() {
1137        let dcel = init_dcel_with_3x3_hex_mesh!(Dcel<(i32, i32)>);
1138
1139        assert_vertex_spokes_interspokes!(&dcel, 0, 3);
1140        assert_vertex_spokes_interspokes!(&dcel, 1, 2);
1141        assert_vertex_spokes_interspokes!(&dcel, 2, 2);
1142        assert_vertex_spokes_interspokes!(&dcel, 3, 2);
1143        assert_vertex_spokes_interspokes!(&dcel, 4, 3);
1144        assert_vertex_spokes_interspokes!(&dcel, 5, 3);
1145        assert_vertex_spokes_interspokes!(&dcel, 6, 3);
1146        assert_vertex_spokes_interspokes!(&dcel, 7, 2);
1147        assert_vertex_spokes_interspokes!(&dcel, 8, 3);
1148        assert_vertex_spokes_interspokes!(&dcel, 9, 3);
1149        assert_vertex_spokes_interspokes!(&dcel, 10, 2);
1150        assert_vertex_spokes_interspokes!(&dcel, 11, 2);
1151        assert_vertex_spokes_interspokes!(&dcel, 12, 3);
1152        assert_vertex_spokes_interspokes!(&dcel, 13, 3);
1153        assert_vertex_spokes_interspokes!(&dcel, 14, 3);
1154        assert_vertex_spokes_interspokes!(&dcel, 15, 3);
1155        assert_vertex_spokes_interspokes!(&dcel, 16, 3);
1156        assert_vertex_spokes_interspokes!(&dcel, 17, 3);
1157        assert_vertex_spokes_interspokes!(&dcel, 18, 3);
1158        assert_vertex_spokes_interspokes!(&dcel, 19, 2);
1159        assert_vertex_spokes_interspokes!(&dcel, 20, 3);
1160        assert_vertex_spokes_interspokes!(&dcel, 21, 2);
1161        assert_vertex_spokes_interspokes!(&dcel, 22, 2);
1162        assert_vertex_spokes_interspokes!(&dcel, 23, 2);
1163        assert_vertex_spokes_interspokes!(&dcel, 24, 2);
1164        assert_vertex_spokes_interspokes!(&dcel, 25, 3);
1165        assert_vertex_spokes_interspokes!(&dcel, 26, 2);
1166        assert_vertex_spokes_interspokes!(&dcel, 27, 3);
1167        assert_vertex_spokes_interspokes!(&dcel, 28, 2);
1168        assert_vertex_spokes_interspokes!(&dcel, 29, 2);
1169    }
1170
1171    #[test]
1172    fn test_face_boundaries() {
1173        let dcel = init_dcel_with_3x3_hex_mesh!(Dcel<(i32, i32)>);
1174
1175        assert_face_boundary!(&dcel, 0, 0);
1176        assert_face_boundary!(&dcel, 1, 6);
1177        assert_face_boundary!(&dcel, 2, 6);
1178        assert_face_boundary!(&dcel, 3, 6);
1179        assert_face_boundary!(&dcel, 4, 6);
1180        assert_face_boundary!(&dcel, 5, 6);
1181        assert_face_boundary!(&dcel, 6, 6);
1182        assert_face_boundary!(&dcel, 7, 6);
1183        assert_face_boundary!(&dcel, 8, 6);
1184        assert_face_boundary!(&dcel, 9, 6);
1185    }
1186
1187    #[test]
1188    fn test_face_spokes_interspokes() {
1189        let dcel = init_dcel_with_3x3_hex_mesh!(Dcel<(i32, i32)>);
1190
1191        assert_face_spokes_interspokes!(&dcel, 0, 0);
1192        assert_face_spokes_interspokes!(&dcel, 1, 3);
1193        assert_face_spokes_interspokes!(&dcel, 2, 5);
1194        assert_face_spokes_interspokes!(&dcel, 3, 4);
1195        assert_face_spokes_interspokes!(&dcel, 4, 6);
1196        assert_face_spokes_interspokes!(&dcel, 5, 6);
1197        assert_face_spokes_interspokes!(&dcel, 6, 4);
1198        assert_face_spokes_interspokes!(&dcel, 7, 3);
1199        assert_face_spokes_interspokes!(&dcel, 8, 5);
1200        assert_face_spokes_interspokes!(&dcel, 9, 4);
1201    }
1202
1203    #[test]
1204    fn test_face_rims() {
1205        let dcel = init_dcel_with_3x3_hex_mesh!(Dcel<(i32, i32)>);
1206
1207        assert_face_rim!(&dcel, 0, 0);
1208
1209        // Boundary faces have no rims, so the number of elements is undefined,
1210        // at least for now.
1211        /*assert_face_rim!(&dcel, 1, 22);
1212        assert_face_rim!(&dcel, 2, 30);
1213        assert_face_rim!(&dcel, 3, 26);
1214        assert_face_rim!(&dcel, 4, 34);*/
1215
1216        // Only the central face has a rim.
1217        assert_face_rim!(&dcel, 5, 18);
1218
1219        // Boundary faces have no rims, so the number of elements is undefined,
1220        // at least for now.
1221        /*assert_face_rim!(&dcel, 6, 26);
1222        assert_face_rim!(&dcel, 7, 22);
1223        assert_face_rim!(&dcel, 8, 30);
1224        assert_face_rim!(&dcel, 9, 26);*/
1225    }
1226}