1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#[cfg(feature = "rkyv")]
use rkyv::{bytecheck, CheckBytes};

/// An identifier of a feature of a convex polyhedron.
///
/// This identifier is shape-dependent and is such that it
/// allows an efficient retrieval of the geometric information of the
/// feature.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
    feature = "rkyv",
    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
    archive(as = "Self")
)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Default)]
pub enum FeatureId {
    /// Shape-dependent identifier of a vertex.
    Vertex(u32),
    #[cfg(feature = "dim3")]
    /// Shape-dependent identifier of an edge.
    Edge(u32),
    /// Shape-dependent identifier of a face.
    Face(u32),
    // XXX: remove this variant.
    /// Unknown identifier.
    #[default]
    Unknown,
}

impl FeatureId {
    /// Revries the value of the identifier if `self` is a vertex.
    pub fn unwrap_vertex(self) -> u32 {
        match self {
            FeatureId::Vertex(id) => id,
            _ => panic!("The feature id does not identify a vertex."),
        }
    }

    /// Revries the value of the identifier if `self` is an edge.
    #[cfg(feature = "dim3")]
    pub fn unwrap_edge(self) -> u32 {
        match self {
            FeatureId::Edge(id) => id,
            _ => panic!("The feature id does not identify an edge."),
        }
    }

    /// Retrieves the value of the identifier if `self` is a face.
    pub fn unwrap_face(self) -> u32 {
        match self {
            FeatureId::Face(id) => id,
            _ => panic!("The feature id does not identify a face."),
        }
    }
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
    feature = "rkyv",
    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
    archive(as = "Self")
)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
/// A feature id where the feature type is packed into the same value as the feature index.
pub struct PackedFeatureId(pub u32);

impl PackedFeatureId {
    /// Packed feature id identifying an unknown feature.
    pub const UNKNOWN: Self = Self(0);

    const CODE_MASK: u32 = 0x3fff_ffff;
    const HEADER_MASK: u32 = !Self::CODE_MASK;
    const HEADER_VERTEX: u32 = 0b01 << 30;
    #[cfg(feature = "dim3")]
    const HEADER_EDGE: u32 = 0b10 << 30;
    const HEADER_FACE: u32 = 0b11 << 30;

    /// Converts a vertex feature id into a packed feature id.
    pub fn vertex(code: u32) -> Self {
        assert_eq!(code & Self::HEADER_MASK, 0);
        Self(Self::HEADER_VERTEX | code)
    }

    /// Converts a edge feature id into a packed feature id.
    #[cfg(feature = "dim3")]
    pub fn edge(code: u32) -> Self {
        assert_eq!(code & Self::HEADER_MASK, 0);
        Self(Self::HEADER_EDGE | code)
    }

    /// Converts a face feature id into a packed feature id.
    pub fn face(code: u32) -> Self {
        assert_eq!(code & Self::HEADER_MASK, 0);
        Self(Self::HEADER_FACE | code)
    }

    #[cfg(feature = "dim2")]
    /// Converts an array of vertex feature ids into an array of packed feature ids.
    pub(crate) fn vertices(code: [u32; 2]) -> [Self; 2] {
        [Self::vertex(code[0]), Self::vertex(code[1])]
    }

    #[cfg(feature = "dim3")]
    /// Converts an array of vertex feature ids into an array of packed feature ids.
    pub(crate) fn vertices(code: [u32; 4]) -> [Self; 4] {
        [
            Self::vertex(code[0]),
            Self::vertex(code[1]),
            Self::vertex(code[2]),
            Self::vertex(code[3]),
        ]
    }

    #[cfg(feature = "dim3")]
    /// Converts an array of edge feature ids into an array of packed feature ids.
    pub(crate) fn edges(code: [u32; 4]) -> [Self; 4] {
        [
            Self::edge(code[0]),
            Self::edge(code[1]),
            Self::edge(code[2]),
            Self::edge(code[3]),
        ]
    }

    /// Unpacks this feature id into an explicit enum.
    pub fn unpack(self) -> FeatureId {
        let header = self.0 & Self::HEADER_MASK;
        let code = self.0 & Self::CODE_MASK;
        match header {
            Self::HEADER_VERTEX => FeatureId::Vertex(code),
            #[cfg(feature = "dim3")]
            Self::HEADER_EDGE => FeatureId::Edge(code),
            Self::HEADER_FACE => FeatureId::Face(code),
            _ => FeatureId::Unknown,
        }
    }

    /// Is the identified feature a face?
    pub fn is_face(self) -> bool {
        self.0 & Self::HEADER_MASK == Self::HEADER_FACE
    }

    /// Is the identified feature a vertex?
    pub fn is_vertex(self) -> bool {
        self.0 & Self::HEADER_MASK == Self::HEADER_VERTEX
    }

    /// Is the identified feature an edge?
    #[cfg(feature = "dim3")]
    pub fn is_edge(self) -> bool {
        self.0 & Self::HEADER_MASK == Self::HEADER_EDGE
    }

    /// Is the identified feature unknown?
    pub fn is_unknown(self) -> bool {
        self == Self::UNKNOWN
    }
}

impl From<FeatureId> for PackedFeatureId {
    fn from(value: FeatureId) -> Self {
        match value {
            FeatureId::Face(fid) => Self::face(fid),
            #[cfg(feature = "dim3")]
            FeatureId::Edge(fid) => Self::edge(fid),
            FeatureId::Vertex(fid) => Self::vertex(fid),
            FeatureId::Unknown => Self::UNKNOWN,
        }
    }
}