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}