1use crate::components::*;
10use glam::Mat4;
11use kvas_ecs::{Entity, World};
12use std::collections::{HashMap, HashSet};
13
14pub fn spawn_named(world: &mut World, name: &str) -> Entity {
17 let e = world.spawn();
18 world.insert(e, Name(name.to_string()));
19 world.insert(e, Transform::default());
20 e
21}
22
23pub fn name_of(world: &World, e: Entity) -> &str {
25 world.get::<Name>(e).map_or("", |n| n.0.as_str())
26}
27
28pub fn find_by_name(world: &World, name: &str) -> Option<Entity> {
30 world.iter::<Name>().find(|(_, n)| n.0 == name).map(|(e, _)| e)
31}
32
33pub fn parent_of(world: &World, e: Entity) -> Option<Entity> {
35 world.get::<Parent>(e).map(|p| p.0).filter(|&p| world.is_alive(p))
36}
37
38pub fn children_all(world: &World, e: Entity) -> Vec<Entity> {
40 if !world.is_alive(e) {
41 return Vec::new();
42 }
43 let mut out: Vec<Entity> = world.iter::<Parent>().filter(|(_, p)| p.0 == e).map(|(c, _)| c).collect();
44 out.sort();
45 out
46}
47
48pub fn children(world: &World, e: Entity) -> Vec<Entity> {
50 children_all(world, e).into_iter().filter(|&c| !world.has::<Transient>(c)).collect()
51}
52
53pub fn roots(world: &World) -> Vec<Entity> {
55 world.iter_entities().filter(|&e| parent_of(world, e).is_none() && !world.has::<Transient>(e)).collect()
56}
57
58pub fn is_ancestor(world: &World, ancestor: Entity, of: Entity) -> bool {
60 if ancestor == of || !world.is_alive(ancestor) {
61 return false;
62 }
63 let mut seen = HashSet::from([of]);
64 let mut cur = parent_of(world, of);
65 loop {
66 match cur {
67 Some(p) if p == ancestor => return true,
68 Some(p) if seen.insert(p) => cur = parent_of(world, p),
69 Some(_) => return false,
70 None => return false,
71 }
72 }
73}
74
75pub fn ancestors_count(world: &World, e: Entity) -> usize {
77 let mut n = 0;
78 let mut seen = HashSet::from([e]);
79 let mut cur = parent_of(world, e);
80 while let Some(p) = cur {
81 if !seen.insert(p) {
82 break;
83 }
84 n += 1;
85 cur = parent_of(world, p);
86 }
87 n
88}
89
90pub fn descendants(world: &World, e: Entity) -> Vec<Entity> {
94 if !world.is_alive(e) {
95 return Vec::new();
96 }
97 let mut links: HashMap<Entity, Vec<Entity>> = HashMap::new();
98 for (child, p) in world.iter::<Parent>() {
99 links.entry(p.0).or_default().push(child);
100 }
101 for children in links.values_mut() {
102 children.sort();
103 }
104 let mut out = Vec::new();
105 let mut seen = HashSet::from([e]);
106 let mut stack = vec![e];
107 while let Some(cur) = stack.pop() {
108 for &c in links.get(&cur).into_iter().flatten() {
109 if seen.insert(c) {
110 out.push(c);
111 stack.push(c);
112 }
113 }
114 }
115 out
116}
117
118pub fn despawn_recursive(world: &mut World, e: Entity) {
120 for d in descendants(world, e) {
121 world.despawn(d);
122 }
123 world.despawn(e);
124}
125
126pub fn global_matrix(world: &World, e: Entity) -> Mat4 {
130 let mut m = Mat4::IDENTITY;
131 let mut seen = HashSet::new();
132 let mut cur = Some(e);
133 while let Some(c) = cur {
134 if !world.is_alive(c) || !seen.insert(c) {
135 break;
136 }
137 m = world.get::<Transform>(c).map_or(Mat4::IDENTITY, Transform::matrix) * m;
138 cur = parent_of(world, c);
139 }
140 m
141}
142
143fn valid_parent(world: &World, child: Entity, parent: Option<Entity>) -> bool {
144 if !world.is_alive(child) {
145 return false;
146 }
147 let mut seen = HashSet::from([child]);
148 let mut cur = parent;
149 while let Some(e) = cur {
150 if !world.is_alive(e) || !seen.insert(e) {
151 return false;
152 }
153 cur = world.get::<Parent>(e).map(|p| p.0);
154 }
155 true
156}
157
158pub fn set_parent(world: &mut World, child: Entity, parent: Option<Entity>) -> bool {
163 if !valid_parent(world, child, parent) {
164 return false;
165 }
166 let world_m = global_matrix(world, child);
167 let parent_m = parent.map_or(Mat4::IDENTITY, |p| global_matrix(world, p));
168 if !world_m.is_finite() || !parent_m.is_finite() || parent_m.determinant() == 0.0 {
169 return false;
170 }
171 let local = parent_m.inverse() * world_m;
172 if !local.is_finite() || local.determinant() == 0.0 {
174 return false;
175 }
176 let Some(transform) = Transform::try_from_matrix(local) else {
177 return false;
178 };
179 set_parent_local(world, child, parent);
180 if let Some(t) = world.get_mut::<Transform>(child) {
181 *t = transform;
182 }
183 true
184}
185
186pub fn set_parent_local(world: &mut World, child: Entity, parent: Option<Entity>) -> bool {
189 if !valid_parent(world, child, parent) {
190 return false;
191 }
192 match parent {
193 Some(p) => world.insert(child, Parent(p)),
194 None => {
195 world.remove::<Parent>(child);
196 }
197 }
198 true
199}
200
201pub fn find_all_by_name(world: &World, name: &str) -> Vec<Entity> {
203 let mut matches: Vec<_> = world.iter::<Name>().filter(|(_, n)| n.0 == name).map(|(e, _)| e).collect();
204 matches.sort();
205 matches
206}
207
208pub fn find_descendant(world: &World, root: Entity, name: &str, include_root: bool) -> Option<Entity> {
210 if include_root && world.is_alive(root) && name_of(world, root) == name {
211 return Some(root);
212 }
213 descendants(world, root).into_iter().find(|&e| name_of(world, e) == name)
214}
215
216pub fn is_visible(world: &World, entity: Entity) -> bool {
220 if !world.is_alive(entity) {
221 return false;
222 }
223 let mut current = Some(entity);
224 let mut seen = HashSet::new();
225 while let Some(e) = current {
226 if !seen.insert(e) {
227 break;
228 }
229 if world.get::<Visibility>(e).is_some_and(|v| !v.visible) {
230 return false;
231 }
232 current = parent_of(world, e);
233 }
234 true
235}
236
237pub fn set_world_position(world: &mut World, entity: Entity, position: glam::Vec3) -> bool {
242 if !position.is_finite() || !world.has::<Transform>(entity) {
243 return false;
244 }
245 let parent = parent_of(world, entity).map_or(Mat4::IDENTITY, |parent| global_matrix(world, parent));
246 if !parent.is_finite() || parent.determinant() == 0.0 {
247 return false;
248 }
249 let local = parent.inverse().transform_point3(position);
250 if !local.is_finite() {
251 return false;
252 }
253 world.get_mut::<Transform>(entity).unwrap().position = local;
254 true
255}
256
257pub fn update_globals(world: &mut World) {
262 let entities = world.entities();
263 let mut resolved: HashMap<Entity, Mat4> = HashMap::with_capacity(entities.len());
264 let mut path = Vec::new();
265 let mut seen = HashSet::new();
266 for &entity in &entities {
267 if resolved.contains_key(&entity) {
268 continue;
269 }
270 path.clear();
271 seen.clear();
272 let mut current = Some(entity);
273 let mut base = Mat4::IDENTITY;
274 let mut cycle = false;
275 while let Some(e) = current {
276 if let Some(&matrix) = resolved.get(&e) {
277 base = matrix;
278 break;
279 }
280 if !seen.insert(e) {
281 cycle = true;
282 break;
283 }
284 path.push(e);
285 current = parent_of(world, e);
286 }
287 if cycle {
288 for &e in &path {
289 resolved.insert(e, global_matrix(world, e));
290 }
291 } else {
292 for &e in path.iter().rev() {
293 base *= world.get::<Transform>(e).map_or(Mat4::IDENTITY, Transform::matrix);
294 resolved.insert(e, base);
295 }
296 }
297 }
298 for entity in entities {
299 if world.has::<Transform>(entity) {
300 world.insert(entity, GlobalTransform(resolved[&entity]));
301 }
302 }
303}