Skip to main content

concinnity_core/ecs/
mask.rs

1// A component identity and a bitset over component identities. Each component
2// type has a small integer id (0..128); a ComponentMask records a set of them
3// in a single u128. Masks describe which components an entity has and which
4// components a query requires or excludes, so a query filter reduces to a few
5// bitwise ops.
6
7#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
8/// A component's dense id, its bit position in a [`ComponentMask`].
9pub struct ComponentId(u8);
10
11impl ComponentId {
12    /// The largest id the 128-bit mask can hold.
13    pub const MAX: u8 = 127;
14
15    /// The id for a raw discriminant. Panics in debug past [`ComponentId::MAX`].
16    pub fn new(id: u8) -> ComponentId {
17        debug_assert!(id <= Self::MAX, "component id {id} exceeds {}", Self::MAX);
18        ComponentId(id)
19    }
20
21    /// The raw discriminant.
22    pub fn get(self) -> u8 {
23        self.0
24    }
25}
26
27#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
28/// A set of component ids, one bit each.
29pub struct ComponentMask(u128);
30
31impl ComponentMask {
32    /// The empty set.
33    pub const EMPTY: ComponentMask = ComponentMask(0);
34
35    /// The set holding just `id`.
36    pub fn with(id: ComponentId) -> ComponentMask {
37        let mut mask = ComponentMask::EMPTY;
38        mask.insert(id);
39        mask
40    }
41
42    /// Add `id` to the set.
43    pub fn insert(&mut self, id: ComponentId) {
44        self.0 |= 1u128 << id.0;
45    }
46
47    /// Remove `id` from the set.
48    pub fn remove(&mut self, id: ComponentId) {
49        self.0 &= !(1u128 << id.0);
50    }
51
52    /// Whether `id` is in the set.
53    pub fn contains(self, id: ComponentId) -> bool {
54        self.0 & (1u128 << id.0) != 0
55    }
56
57    // Whether this mask contains every id in `other` (i.e. is a superset).
58    pub(crate) fn contains_all(self, other: ComponentMask) -> bool {
59        self.0 & other.0 == other.0
60    }
61
62    // Whether this mask shares no id with `other`.
63    pub(crate) fn is_disjoint(self, other: ComponentMask) -> bool {
64        self.0 & other.0 == 0
65    }
66
67    /// Whether the set is empty.
68    pub fn is_empty(self) -> bool {
69        self.0 == 0
70    }
71
72    /// The union of two masks.
73    pub fn merged(self, other: ComponentMask) -> ComponentMask {
74        ComponentMask(self.0 | other.0)
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn insert_remove_contains() {
84        let a = ComponentId::new(3);
85        let b = ComponentId::new(70);
86        let mut mask = ComponentMask::EMPTY;
87        assert!(mask.is_empty());
88        mask.insert(a);
89        mask.insert(b);
90        assert!(mask.contains(a));
91        assert!(mask.contains(b));
92        assert!(!mask.contains(ComponentId::new(4)));
93        mask.remove(a);
94        assert!(!mask.contains(a));
95        assert!(mask.contains(b));
96    }
97
98    #[test]
99    fn with_builds_single_bit_mask() {
100        let id = ComponentId::new(127);
101        let mask = ComponentMask::with(id);
102        assert!(mask.contains(id));
103        assert!(!mask.contains(ComponentId::new(0)));
104    }
105
106    #[test]
107    fn contains_all_is_superset() {
108        let mut have = ComponentMask::EMPTY;
109        have.insert(ComponentId::new(1));
110        have.insert(ComponentId::new(2));
111        have.insert(ComponentId::new(3));
112
113        let mut need = ComponentMask::EMPTY;
114        need.insert(ComponentId::new(1));
115        need.insert(ComponentId::new(3));
116        assert!(have.contains_all(need));
117
118        need.insert(ComponentId::new(9));
119        assert!(!have.contains_all(need));
120        // Every mask contains the empty set.
121        assert!(have.contains_all(ComponentMask::EMPTY));
122    }
123
124    #[test]
125    fn is_disjoint_detects_overlap() {
126        let mut a = ComponentMask::EMPTY;
127        a.insert(ComponentId::new(1));
128        a.insert(ComponentId::new(2));
129        let mut b = ComponentMask::EMPTY;
130        b.insert(ComponentId::new(3));
131        assert!(a.is_disjoint(b));
132        b.insert(ComponentId::new(2));
133        assert!(!a.is_disjoint(b));
134        assert!(a.is_disjoint(ComponentMask::EMPTY));
135    }
136}