Skip to content

Commit 75b3e13

Browse files
committed
add DescendantIter::filter_hierarchies
1 parent 329630c commit 75b3e13

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

crates/bevy_ecs/src/relationship/relationship_query.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,21 @@ where
169169
.collect(),
170170
}
171171
}
172+
173+
/// Creates an iterator which uses a closure to determine if recursive [`RelationshipTarget`]s
174+
/// should be yielded.
175+
///
176+
/// Once the the provided closure returns `false` for an [`Entity`] it and its recursive
177+
/// [`RelationshipTarget`]s will not be yielded, effectively skipping that sub hierarchy.
178+
pub fn filter_hierarchies<HF>(self, filter: HF) -> FilterDescendantIter<'w, 's, D, F, S, HF>
179+
where
180+
HF: FnMut(&Entity) -> bool,
181+
{
182+
FilterDescendantIter {
183+
iter: self,
184+
hierarchy_filter: filter,
185+
}
186+
}
172187
}
173188

174189
impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> Iterator
@@ -270,3 +285,108 @@ where
270285
self.next
271286
}
272287
}
288+
289+
/// An [`Iterator`] of [`Entity`]s over the descendants of an [`Entity`].
290+
///
291+
/// Allows conditional skipping of sub hierarchies.
292+
pub struct FilterDescendantIter<'w, 's, D, QF, S, HF>
293+
where
294+
D: QueryData,
295+
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
296+
QF: QueryFilter,
297+
S: RelationshipTarget,
298+
HF: FnMut(&Entity) -> bool,
299+
{
300+
iter: DescendantIter<'w, 's, D, QF, S>,
301+
hierarchy_filter: HF,
302+
}
303+
304+
impl<'w, 's, D, QF, S, HF> Iterator for FilterDescendantIter<'w, 's, D, QF, S, HF>
305+
where
306+
D: QueryData,
307+
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
308+
QF: QueryFilter,
309+
S: RelationshipTarget,
310+
HF: FnMut(&Entity) -> bool,
311+
{
312+
type Item = Entity;
313+
314+
fn next(&mut self) -> Option<Self::Item> {
315+
let mut entity = self.iter.vecdeque.pop_front()?;
316+
317+
while !(self.hierarchy_filter)(&entity) {
318+
entity = self.iter.vecdeque.pop_front()?;
319+
}
320+
321+
if let Ok(children) = self.iter.children_query.get(entity) {
322+
self.iter.vecdeque.extend(children.iter());
323+
}
324+
325+
Some(entity)
326+
}
327+
}
328+
329+
#[cfg(test)]
330+
mod test_iter_descendants {
331+
use crate::{
332+
prelude::*,
333+
system::{RunSystemError, RunSystemOnce},
334+
};
335+
use alloc::{vec, vec::Vec};
336+
337+
#[test]
338+
fn filter_hierarchies_all() -> Result<(), RunSystemError> {
339+
let mut world = World::new();
340+
let root = world.spawn_empty().id();
341+
let children = vec![
342+
world.spawn(ChildOf(root)).id(),
343+
world.spawn(ChildOf(root)).id(),
344+
world.spawn(ChildOf(root)).id(),
345+
];
346+
347+
let descendants = world.run_system_once(move |q: Query<&Children>| {
348+
q.iter_descendants(root)
349+
.filter_hierarchies(|_| true)
350+
.collect::<Vec<_>>()
351+
})?;
352+
353+
assert_eq!(descendants, children);
354+
Ok(())
355+
}
356+
357+
#[test]
358+
fn filter_hierarchies_skip_flat() -> Result<(), RunSystemError> {
359+
let mut world = World::new();
360+
let root = world.spawn_empty().id();
361+
let c0 = world.spawn(ChildOf(root)).id();
362+
let c_skip = world.spawn(ChildOf(root)).id();
363+
let c2 = world.spawn(ChildOf(root)).id();
364+
365+
let descendants = world.run_system_once(move |q: Query<&Children>| {
366+
q.iter_descendants(root)
367+
.filter_hierarchies(|e| e != &c_skip)
368+
.collect::<Vec<_>>()
369+
})?;
370+
371+
assert_eq!(descendants, vec![c0, c2]);
372+
Ok(())
373+
}
374+
375+
#[test]
376+
fn filter_hierarchies_skip_sub_hierarchy() -> Result<(), RunSystemError> {
377+
let mut world = World::new();
378+
let root = world.spawn_empty().id();
379+
let c0 = world.spawn(ChildOf(root)).id();
380+
let c_skip = world.spawn((ChildOf(root), children![(), ()])).id();
381+
let c2 = world.spawn(ChildOf(root)).id();
382+
383+
let descendants = world.run_system_once(move |q: Query<&Children>| {
384+
q.iter_descendants(root)
385+
.filter_hierarchies(|e| e != &c_skip)
386+
.collect::<Vec<_>>()
387+
})?;
388+
389+
assert_eq!(descendants, vec![c0, c2]);
390+
Ok(())
391+
}
392+
}

0 commit comments

Comments
 (0)