Skip to main content

dreamwell_engine/physics/
properties.rs

1use super::tags::TagSet;
2use serde::{Deserialize, Serialize};
3
4/// Property key — interned string identifier for property lookups.
5pub type PropertyKey = u32;
6
7/// Typed property value.
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9pub enum PropertyValue {
10    Bool(bool),
11    Int(i32),
12    Float(f32),
13    Vec2([f32; 2]),
14    Vec3([f32; 3]),
15    Vec4([f32; 4]),
16    Tag(String),
17    Enum(String),
18    String(String),
19}
20
21/// Property enum for material class and similar categorical values.
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
23pub enum PropertyEnum {
24    Material(super::materials::MaterialClass),
25    Custom(String),
26}
27
28/// Property assignment — key-value pair for initialization.
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct PropertyAssignment {
31    pub key: String,
32    pub value: PropertyValue,
33}
34
35/// Predicate for property-based rule evaluation.
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub enum PropertyPredicate {
38    GreaterThan(String, f32),
39    LessThan(String, f32),
40    EqualsBool(String, bool),
41    EqualsEnum(String, String),
42}
43
44/// Semantic properties for an entity — traits, signals, receivers, and typed properties.
45/// Uses Vec-based storage (sorted by key) instead of HashMap for hot-path access.
46/// Physics property stores are typically small (<64 entries), so linear/binary search
47/// outperforms HashMap due to better cache locality and lower per-lookup overhead.
48#[derive(Debug, Clone, Default)]
49pub struct SemanticProperties {
50    pub traits: TagSet,
51    pub signals: TagSet,
52    pub receivers: TagSet,
53    pub scalars: Vec<(PropertyKey, f32)>,
54    pub vectors: Vec<(PropertyKey, [f32; 4])>,
55    pub ints: Vec<(PropertyKey, i32)>,
56    pub bools: Vec<(PropertyKey, bool)>,
57    pub enums: Vec<(PropertyKey, PropertyEnum)>,
58}
59
60/// Insert or update a value in a sorted key-value Vec.
61fn vec_insert<V>(entries: &mut Vec<(PropertyKey, V)>, key: PropertyKey, value: V) {
62    match entries.binary_search_by_key(&key, |(k, _)| *k) {
63        Ok(idx) => entries[idx].1 = value,
64        Err(idx) => entries.insert(idx, (key, value)),
65    }
66}
67
68/// Look up a value in a sorted key-value Vec.
69fn vec_get<V: Copy>(entries: &[(PropertyKey, V)], key: PropertyKey) -> Option<V> {
70    entries
71        .binary_search_by_key(&key, |(k, _)| *k)
72        .ok()
73        .map(|idx| entries[idx].1)
74}
75
76/// Look up a reference in a sorted key-value Vec.
77fn vec_get_ref<V>(entries: &[(PropertyKey, V)], key: PropertyKey) -> Option<&V> {
78    entries
79        .binary_search_by_key(&key, |(k, _)| *k)
80        .ok()
81        .map(|idx| &entries[idx].1)
82}
83
84impl SemanticProperties {
85    pub fn new() -> Self {
86        Self::default()
87    }
88
89    pub fn set_scalar(&mut self, key: PropertyKey, value: f32) {
90        vec_insert(&mut self.scalars, key, value);
91    }
92
93    pub fn get_scalar(&self, key: PropertyKey) -> Option<f32> {
94        vec_get(&self.scalars, key)
95    }
96
97    pub fn set_bool(&mut self, key: PropertyKey, value: bool) {
98        vec_insert(&mut self.bools, key, value);
99    }
100
101    pub fn get_bool(&self, key: PropertyKey) -> Option<bool> {
102        vec_get(&self.bools, key)
103    }
104
105    pub fn set_int(&mut self, key: PropertyKey, value: i32) {
106        vec_insert(&mut self.ints, key, value);
107    }
108
109    pub fn get_int(&self, key: PropertyKey) -> Option<i32> {
110        vec_get(&self.ints, key)
111    }
112
113    pub fn set_vec4(&mut self, key: PropertyKey, value: [f32; 4]) {
114        vec_insert(&mut self.vectors, key, value);
115    }
116
117    pub fn get_vec4(&self, key: PropertyKey) -> Option<[f32; 4]> {
118        vec_get(&self.vectors, key)
119    }
120
121    pub fn get_enum(&self, key: PropertyKey) -> Option<&PropertyEnum> {
122        vec_get_ref(&self.enums, key)
123    }
124
125    pub fn set_enum(&mut self, key: PropertyKey, value: PropertyEnum) {
126        vec_insert(&mut self.enums, key, value);
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn semantic_properties_scalar_roundtrip() {
136        let mut props = SemanticProperties::new();
137        props.set_scalar(0, 42.0);
138        assert_eq!(props.get_scalar(0), Some(42.0));
139        assert_eq!(props.get_scalar(1), None);
140    }
141
142    #[test]
143    fn semantic_properties_bool_roundtrip() {
144        let mut props = SemanticProperties::new();
145        props.set_bool(5, true);
146        assert_eq!(props.get_bool(5), Some(true));
147    }
148
149    #[test]
150    fn property_value_serde_roundtrip() {
151        let val = PropertyValue::Float(3.14);
152        let json = serde_json::to_string(&val).unwrap();
153        let restored: PropertyValue = serde_json::from_str(&json).unwrap();
154        assert_eq!(val, restored);
155    }
156
157    #[test]
158    fn property_assignment_serde() {
159        let pa = PropertyAssignment {
160            key: "durability".to_string(),
161            value: PropertyValue::Float(600.0),
162        };
163        let json = serde_json::to_string(&pa).unwrap();
164        assert!(json.contains("durability"));
165    }
166}