Skip to main content

pfx_play/
layers.rs

1use std::collections::BTreeMap;
2
3use pfx_physics::rigid;
4
5pub const MAX: usize = 32;
6
7#[derive(Clone, Debug, Default, PartialEq, Eq)]
8pub struct Layers {
9    names: Vec<String>,
10    masks: Vec<u32>,
11}
12
13impl Layers {
14    pub fn from_scene(layers: &BTreeMap<String, Vec<String>>) -> Result<Layers, String> {
15        if layers.len() > MAX {
16            return Err(format!(
17                "[layers] names {} layers; at most {MAX} can be named",
18                layers.len()
19            ));
20        }
21        let names: Vec<String> = layers.keys().cloned().collect();
22        let mut masks = Vec::new();
23        for (name, list) in layers {
24            let mut mask = 0u32;
25            for other in list {
26                let at = names
27                    .iter()
28                    .position(|known| known == other)
29                    .ok_or_else(|| {
30                        format!("layer {name} lists {other:?}, which is not a layer in [layers]")
31                    })?;
32                mask |= 1 << at;
33            }
34            masks.push(mask);
35        }
36        Ok(Layers { names, masks })
37    }
38
39    pub fn len(&self) -> usize {
40        self.names.len()
41    }
42
43    pub fn is_empty(&self) -> bool {
44        self.names.is_empty()
45    }
46
47    pub fn names(&self) -> impl Iterator<Item = &str> {
48        self.names.iter().map(String::as_str)
49    }
50
51    pub fn bit(&self, name: &str) -> Option<u32> {
52        self.names
53            .iter()
54            .position(|known| known == name)
55            .map(|at| 1 << at)
56    }
57
58    pub(crate) fn require(&self, name: &str) -> Result<u32, String> {
59        self.bit(name).ok_or_else(|| self.unknown(name))
60    }
61
62    pub fn mask<S: AsRef<str>>(&self, names: &[S]) -> Result<u32, String> {
63        let mut mask = 0;
64        for name in names {
65            mask |= self.require(name.as_ref())?;
66        }
67        Ok(mask)
68    }
69
70    pub fn names_of(&self, mask: u32) -> Vec<&str> {
71        self.names
72            .iter()
73            .enumerate()
74            .filter(|(at, _)| mask & (1 << at) != 0)
75            .map(|(_, name)| name.as_str())
76            .collect()
77    }
78
79    pub fn collides(&self, a: &str, b: &str) -> Option<bool> {
80        let (bit_a, bit_b) = (self.bit(a)?, self.bit(b)?);
81        Some(self.collision_mask(bit_a) & bit_b != 0 && self.collision_mask(bit_b) & bit_a != 0)
82    }
83
84    pub fn collision_mask(&self, layer: u32) -> u32 {
85        self.masks
86            .iter()
87            .enumerate()
88            .filter(|(at, _)| layer & (1 << at) != 0)
89            .fold(0, |mask, (_, layers)| mask | layers)
90    }
91
92    pub fn groups(&self, layer: u32) -> rigid::Layers {
93        rigid::Layers::new(layer, self.collision_mask(layer))
94    }
95
96    pub(crate) fn unknown(&self, name: &str) -> String {
97        if self.names.is_empty() {
98            format!("layer {name:?}: the project declares no [layers]")
99        } else {
100            let known: Vec<&str> = self.names().collect();
101            format!(
102                "layer {name:?} is not a layer in [layers] ({})",
103                known.join(", ")
104            )
105        }
106    }
107}