axiolid_arrangement/
id.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Typed arena handles for the planar arrangement.
4//!
5//! Mirrors the convention in `axiolid-topology`: a bare `usize` cannot be
6//! passed where a vertex, half-edge, or face is expected, because the three
7//! are different types. The arrangement's handles are deliberately NOT the
8//! same types as the B-rep's -- a planar face and a B-rep face are different
9//! concepts, and letting one stand in for the other would be a bug the
10//! compiler should catch.
11
12use core::fmt;
13
14macro_rules! arrangement_id {
15    ($name:ident, $label:literal) => {
16        #[doc = concat!("Stable handle into the arrangement's ", $label, " arena.")]
17        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18        pub struct $name(u32);
19
20        impl $name {
21            pub(crate) fn from_index(index: usize) -> Self {
22                Self(u32::try_from(index).expect("arrangement arena exceeds u32 capacity"))
23            }
24
25            /// Zero-based arena index.
26            #[must_use]
27            pub const fn index(self) -> usize {
28                self.0 as usize
29            }
30        }
31
32        impl fmt::Display for $name {
33            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34                write!(f, concat!($label, "#{}"), self.0)
35            }
36        }
37    };
38}
39
40arrangement_id!(VertexId, "vertex");
41arrangement_id!(HalfEdgeId, "halfedge");
42arrangement_id!(FaceId, "face");
43
44impl FaceId {
45    /// The unbounded face is always arena slot zero, created with the
46    /// arrangement itself. Fixing it by construction means `outer_face()`
47    /// needs no bookkeeping and cannot drift.
48    pub(crate) const fn outer() -> Self {
49        Self(0)
50    }
51}