From c06ae28eff4e32d9888f13c825c2a6b3bf3c4796 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Oct 2024 13:55:41 -0400 Subject: [PATCH 01/17] Crude initial implementation --- crates/bevy_hierarchy/src/query_extension.rs | 201 ++++++++++++++++++- 1 file changed, 200 insertions(+), 1 deletion(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index 36bf790cec1bd..bf3673119e7b5 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -10,11 +10,46 @@ use crate::{Children, Parent}; /// An extension trait for [`Query`] that adds hierarchy related methods. pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { + /// Returns the parent [`Entity`] of the given `entity`, if any. + fn parent(&'w self, entity: Entity) -> Option + where + D::ReadOnly: WorldQuery = &'w Parent>; + + /// Returns a slice over the [`Children`] of the given `entity`. + /// + /// This may be empty if the `entity` has no children. + fn children(&'w self, entity: Entity) -> impl Iterator + 'w + where + D::ReadOnly: WorldQuery = &'w Children>; + + /// Returns the topmost ancestor of the given `entity`. + /// + /// This may be the entity itself if it has no parent. + fn root_parent(&'w self, entity: Entity) -> Entity + where + D::ReadOnly: WorldQuery = &'w Parent>; + + /// Returns an [`Iterator`] of [`Entity`]s over the leaves of the hierarchy that are underneath this `entity``. + /// + /// Only entities which have no children are considered leaves. + /// This will not include the entity itself, and will not include any entities which are not descendants of the entity, + /// even if they are leaves in the same hierarchical tree. + fn iter_leaves(&'w self, entity: Entity) -> LeafIter<'w, 's, D, F> + where + D::ReadOnly: WorldQuery = &'w Children>; + + /// Returns an [`Iterator`] of [`Entity`]s over the `entity`s immediate siblings, who share them same parent. + /// + /// The entity itself is not included in the iterator. + fn iter_siblings(&'w self, entity: Entity) -> SiblingIter<'w, 's, D, F> + where + D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>; + /// Returns an [`Iterator`] of [`Entity`]s over all of `entity`s descendants. /// /// Can only be called on a [`Query`] of [`Children`] (i.e. `Query<&Children>`). /// - /// Traverses the hierarchy breadth-first. + /// Traverses the hierarchy breadth-first and does not include the entity itself. /// /// # Examples /// ``` @@ -36,6 +71,7 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { /// Returns an [`Iterator`] of [`Entity`]s over all of `entity`s ancestors. /// + /// Does not include the entity itself. /// Can only be called on a [`Query`] of [`Parent`] (i.e. `Query<&Parent>`). /// /// # Examples @@ -58,6 +94,77 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { } impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Query<'w, 's, D, F> { + fn parent(&'w self, entity: Entity) -> Option + where + ::ReadOnly: WorldQuery = &'w Parent>, + { + self.get(entity).map(Parent::get).ok() + } + + fn children(&'w self, entity: Entity) -> impl Iterator + where + ::ReadOnly: WorldQuery = &'w Children>, + { + // We must return the same type from both branches of the match + // So we've defined a throwaway enum to wrap the two types + enum MaybeChildrenIter { + Children { cursor: usize, vec: Vec }, + None, + } + + impl Iterator for MaybeChildrenIter { + type Item = Entity; + + fn next(&mut self) -> Option { + match self { + MaybeChildrenIter::Children { cursor, vec } => { + if *cursor < vec.len() { + let entity = vec[*cursor]; + *cursor += 1; + Some(entity) + } else { + None + } + } + MaybeChildrenIter::None => None, + } + } + } + + match self.get(entity) { + Ok(children) => MaybeChildrenIter::Children { + cursor: 0, + vec: children.to_vec(), + }, + Err(_) => MaybeChildrenIter::None, + } + } + + fn root_parent(&'w self, entity: Entity) -> Entity + where + ::ReadOnly: WorldQuery = &'w Parent>, + { + // Recursively search up the tree until we're out of parents + match self.get(entity) { + Ok(parent) => self.root_parent(parent.get()), + Err(_) => entity, + } + } + + fn iter_leaves(&'w self, entity: Entity) -> LeafIter<'w, 's, D, F> + where + ::ReadOnly: WorldQuery = &'w Children>, + { + LeafIter::new(self, entity) + } + + fn iter_siblings(&'w self, entity: Entity) -> SiblingIter<'w, 's, D, F> + where + D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, + { + SiblingIter::new(self, entity) + } + fn iter_descendants(&'w self, entity: Entity) -> DescendantIter<'w, 's, D, F> where D::ReadOnly: WorldQuery = &'w Children>, @@ -73,6 +180,98 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q } } +/// An [`Iterator`] of [`Entity`]s over the leaf descendants of an [`Entity`]. +pub struct LeafIter<'w, 's, D: QueryData, F: QueryFilter> +where + D::ReadOnly: WorldQuery = &'w Children>, +{ + _children_query: &'w Query<'w, 's, D, F>, + vecdeque: VecDeque, +} + +impl<'w, 's, D: QueryData, F: QueryFilter> LeafIter<'w, 's, D, F> +where + D::ReadOnly: WorldQuery = &'w Children>, +{ + /// Returns a new [`LeafIter`]. + pub fn new(children_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { + let leaf_children = children_query.iter_descendants(entity).filter(|entity| { + children_query + .get(*entity) + // These are leaf nodes if they have the `Children` component but it's empty + .map(|children| children.is_empty()) + // Or if they don't have the `Children` component at all + .unwrap_or(true) + }); + + LeafIter { + _children_query: children_query, + vecdeque: leaf_children.collect(), + } + } +} + +impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for LeafIter<'w, 's, D, F> +where + D::ReadOnly: WorldQuery = &'w Children>, +{ + type Item = Entity; + + fn next(&mut self) -> Option { + let entity: Entity = self.vecdeque.pop_front()?; + Some(entity) + } +} + +/// An [`Iterator`] of [`Entity`]s over the siblings of an [`Entity`]. +pub struct SiblingIter<'w, 's, D: QueryData, F: QueryFilter> +where + D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, +{ + _hierarchy_query: &'w Query<'w, 's, D, F>, + vecdeque: VecDeque, +} + +impl<'w, 's, D: QueryData, F: QueryFilter> SiblingIter<'w, 's, D, F> +where + D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, +{ + /// Returns a new [`SiblingIter`]. + pub fn new(hierarchy_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { + match hierarchy_query.get(entity) { + Ok((parent, _)) => { + let children_of_parent = hierarchy_query + .get(parent.get()) + .map(|(_, children)| children.to_vec()) + .unwrap_or_default(); + + let siblings = children_of_parent.iter().filter(|child| **child != entity); + + SiblingIter { + _hierarchy_query: hierarchy_query, + vecdeque: VecDeque::from_iter(siblings.copied()), + } + } + Err(_) => SiblingIter { + _hierarchy_query: hierarchy_query, + vecdeque: VecDeque::new(), + }, + } + } +} + +impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for SiblingIter<'w, 's, D, F> +where + D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, +{ + type Item = Entity; + + fn next(&mut self) -> Option { + let entity: Entity = self.vecdeque.pop_front()?; + Some(entity) + } +} + /// An [`Iterator`] of [`Entity`]s over the descendants of an [`Entity`]. /// /// Traverses the hierarchy breadth-first. From c29a326c923f9ec1ce8034f190a8829f74f5a055 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Oct 2024 13:59:21 -0400 Subject: [PATCH 02/17] Optimize SiblingIter with SmallVec and less allocations --- crates/bevy_hierarchy/src/query_extension.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index bf3673119e7b5..002aa1b1f8c1e 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -5,6 +5,7 @@ use bevy_ecs::{ query::{QueryData, QueryFilter, WorldQuery}, system::Query, }; +use smallvec::SmallVec; use crate::{Children, Parent}; @@ -229,7 +230,7 @@ where D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, { _hierarchy_query: &'w Query<'w, 's, D, F>, - vecdeque: VecDeque, + small_vec: SmallVec<[Entity; 8]>, } impl<'w, 's, D: QueryData, F: QueryFilter> SiblingIter<'w, 's, D, F> @@ -240,21 +241,23 @@ where pub fn new(hierarchy_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { match hierarchy_query.get(entity) { Ok((parent, _)) => { - let children_of_parent = hierarchy_query - .get(parent.get()) - .map(|(_, children)| children.to_vec()) - .unwrap_or_default(); + let Ok((_, children_of_parent)) = hierarchy_query.get(parent.get()) else { + return SiblingIter { + _hierarchy_query: hierarchy_query, + small_vec: SmallVec::new(), + }; + }; let siblings = children_of_parent.iter().filter(|child| **child != entity); SiblingIter { _hierarchy_query: hierarchy_query, - vecdeque: VecDeque::from_iter(siblings.copied()), + small_vec: SmallVec::from_iter(siblings.copied()), } } Err(_) => SiblingIter { _hierarchy_query: hierarchy_query, - vecdeque: VecDeque::new(), + small_vec: SmallVec::new(), }, } } @@ -267,7 +270,7 @@ where type Item = Entity; fn next(&mut self) -> Option { - let entity: Entity = self.vecdeque.pop_front()?; + let entity: Entity = self.small_vec.pop()?; Some(entity) } } From 1f7ed97dcd11f5fba77c942782d69f00cea7514e Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Oct 2024 14:02:04 -0400 Subject: [PATCH 03/17] Remove query data from SiblingIter --- crates/bevy_hierarchy/src/query_extension.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index 002aa1b1f8c1e..f0bf6b46f40f5 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -1,3 +1,5 @@ +use std::marker::PhantomData; + use alloc::collections::VecDeque; use bevy_ecs::{ @@ -163,7 +165,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q where D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, { - SiblingIter::new(self, entity) + SiblingIter::::new(self, entity) } fn iter_descendants(&'w self, entity: Entity) -> DescendantIter<'w, 's, D, F> @@ -229,8 +231,8 @@ pub struct SiblingIter<'w, 's, D: QueryData, F: QueryFilter> where D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, { - _hierarchy_query: &'w Query<'w, 's, D, F>, small_vec: SmallVec<[Entity; 8]>, + _phantom: PhantomData<(&'w D, &'s F)>, } impl<'w, 's, D: QueryData, F: QueryFilter> SiblingIter<'w, 's, D, F> @@ -243,21 +245,21 @@ where Ok((parent, _)) => { let Ok((_, children_of_parent)) = hierarchy_query.get(parent.get()) else { return SiblingIter { - _hierarchy_query: hierarchy_query, small_vec: SmallVec::new(), + _phantom: PhantomData, }; }; let siblings = children_of_parent.iter().filter(|child| **child != entity); SiblingIter { - _hierarchy_query: hierarchy_query, small_vec: SmallVec::from_iter(siblings.copied()), + _phantom: PhantomData, } } Err(_) => SiblingIter { - _hierarchy_query: hierarchy_query, small_vec: SmallVec::new(), + _phantom: PhantomData, }, } } From 3d17174811f2bb95c11a8dde36ad3aec0093cf83 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Oct 2024 14:05:07 -0400 Subject: [PATCH 04/17] Remove stored query in LeafIter --- crates/bevy_hierarchy/src/query_extension.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index f0bf6b46f40f5..d1824afc1f09f 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -188,8 +188,10 @@ pub struct LeafIter<'w, 's, D: QueryData, F: QueryFilter> where D::ReadOnly: WorldQuery = &'w Children>, { - _children_query: &'w Query<'w, 's, D, F>, vecdeque: VecDeque, + // PERF: if this ends up resulting in too much memory being allocated, we can store the query instead + // like in IterDescendants + _phantom: PhantomData<(&'w D, &'s F)>, } impl<'w, 's, D: QueryData, F: QueryFilter> LeafIter<'w, 's, D, F> @@ -208,8 +210,8 @@ where }); LeafIter { - _children_query: children_query, vecdeque: leaf_children.collect(), + _phantom: PhantomData, } } } @@ -231,6 +233,8 @@ pub struct SiblingIter<'w, 's, D: QueryData, F: QueryFilter> where D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, { + // Unlike other iterators, we don't need to store the query here, + // as the number of siblings is likely to be much smaller than the number of descendants. small_vec: SmallVec<[Entity; 8]>, _phantom: PhantomData<(&'w D, &'s F)>, } From c8b1d9b1aafb03b9471c84802f38ebd5b2d0a076 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Oct 2024 14:28:44 -0400 Subject: [PATCH 05/17] Add simple tests --- crates/bevy_hierarchy/src/query_extension.rs | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index d1824afc1f09f..d535b71470ed6 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -409,4 +409,57 @@ mod tests { assert_eq!([&A(1), &A(0)], result.as_slice()); } + + #[test] + fn root_parent() { + let world = &mut World::new(); + + let [a, b, c] = core::array::from_fn(|i| world.spawn(A(i)).id()); + + world.entity_mut(a).add_children(&[b]); + world.entity_mut(b).add_children(&[c]); + + let mut system_state = SystemState::>::new(world); + let parent_query = system_state.get(world); + + assert_eq!(a, parent_query.root_parent(c)); + assert_eq!(a, parent_query.root_parent(b)); + assert_eq!(a, parent_query.root_parent(a)); + } + + #[test] + fn leaf_iter() { + let world = &mut World::new(); + + let [a, b, c, d] = core::array::from_fn(|i| world.spawn(A(i)).id()); + + world.entity_mut(a).add_children(&[b, c]); + world.entity_mut(c).add_children(&[d]); + + let mut system_state = SystemState::<(Query<&Children>, Query<&A>)>::new(world); + let (children_query, a_query) = system_state.get(world); + + let result: Vec<_> = a_query.iter_many(children_query.iter_leaves(a)).collect(); + + assert_eq!([&A(1), &A(3)], result.as_slice()); + } + + #[test] + fn siblings() { + let world = &mut World::new(); + + let [a, b, c, d, e] = core::array::from_fn(|i| world.spawn(A(i)).id()); + + world.entity_mut(a).add_children(&[b, c, d]); + world.entity_mut(c).add_children(&[e]); + + let mut system_state = SystemState::<(Query<(&Parent, &Children)>, Query<&A>)>::new(world); + let (hierarchy_query, a_query) = system_state.get(world); + + let result: Vec<_> = a_query + .iter_many(hierarchy_query.iter_siblings(b)) + .collect(); + + assert_eq!([&A(2), &A(3)], result.as_slice()); + } } From 4ca9169e0c474f6d74e106eff15ffef2cd3453c5 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Oct 2024 15:01:42 -0400 Subject: [PATCH 06/17] Appease CI --- crates/bevy_hierarchy/src/query_extension.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index d535b71470ed6..caf391c4141a8 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use core::marker::PhantomData; use alloc::collections::VecDeque; @@ -32,7 +32,7 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { where D::ReadOnly: WorldQuery = &'w Parent>; - /// Returns an [`Iterator`] of [`Entity`]s over the leaves of the hierarchy that are underneath this `entity``. + /// Returns an [`Iterator`] of [`Entity`]s over the leaves of the hierarchy that are underneath this `entity`. /// /// Only entities which have no children are considered leaves. /// This will not include the entity itself, and will not include any entities which are not descendants of the entity, From 12a8287d3b7728d67ae1c90b77097ebe51ea47d6 Mon Sep 17 00:00:00 2001 From: Viktor Gustavsson Date: Sat, 5 Oct 2024 19:57:59 +0200 Subject: [PATCH 07/17] Increase readability of test cases --- crates/bevy_hierarchy/src/query_extension.rs | 44 ++++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index caf391c4141a8..306187c70ec67 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -378,16 +378,16 @@ mod tests { fn descendant_iter() { let world = &mut World::new(); - let [a, b, c, d] = core::array::from_fn(|i| world.spawn(A(i)).id()); + let [a0, a1, a2, a3] = core::array::from_fn(|i| world.spawn(A(i)).id()); - world.entity_mut(a).add_children(&[b, c]); - world.entity_mut(c).add_children(&[d]); + world.entity_mut(a0).add_children(&[a1, a2]); + world.entity_mut(a2).add_children(&[a3]); let mut system_state = SystemState::<(Query<&Children>, Query<&A>)>::new(world); let (children_query, a_query) = system_state.get(world); let result: Vec<_> = a_query - .iter_many(children_query.iter_descendants(a)) + .iter_many(children_query.iter_descendants(a0)) .collect(); assert_eq!([&A(1), &A(2), &A(3)], result.as_slice()); @@ -397,15 +397,15 @@ mod tests { fn ancestor_iter() { let world = &mut World::new(); - let [a, b, c] = core::array::from_fn(|i| world.spawn(A(i)).id()); + let [a0, a1, a2] = core::array::from_fn(|i| world.spawn(A(i)).id()); - world.entity_mut(a).add_children(&[b]); - world.entity_mut(b).add_children(&[c]); + world.entity_mut(a0).add_children(&[a1]); + world.entity_mut(a1).add_children(&[a2]); let mut system_state = SystemState::<(Query<&Parent>, Query<&A>)>::new(world); let (parent_query, a_query) = system_state.get(world); - let result: Vec<_> = a_query.iter_many(parent_query.iter_ancestors(c)).collect(); + let result: Vec<_> = a_query.iter_many(parent_query.iter_ancestors(a2)).collect(); assert_eq!([&A(1), &A(0)], result.as_slice()); } @@ -414,32 +414,32 @@ mod tests { fn root_parent() { let world = &mut World::new(); - let [a, b, c] = core::array::from_fn(|i| world.spawn(A(i)).id()); + let [a0, a1, a2] = core::array::from_fn(|i| world.spawn(A(i)).id()); - world.entity_mut(a).add_children(&[b]); - world.entity_mut(b).add_children(&[c]); + world.entity_mut(a0).add_children(&[a1]); + world.entity_mut(a1).add_children(&[a2]); let mut system_state = SystemState::>::new(world); let parent_query = system_state.get(world); - assert_eq!(a, parent_query.root_parent(c)); - assert_eq!(a, parent_query.root_parent(b)); - assert_eq!(a, parent_query.root_parent(a)); + assert_eq!(a0, parent_query.root_parent(a2)); + assert_eq!(a0, parent_query.root_parent(a1)); + assert_eq!(a0, parent_query.root_parent(a0)); } #[test] fn leaf_iter() { let world = &mut World::new(); - let [a, b, c, d] = core::array::from_fn(|i| world.spawn(A(i)).id()); + let [a0, a1, a2, a3] = core::array::from_fn(|i| world.spawn(A(i)).id()); - world.entity_mut(a).add_children(&[b, c]); - world.entity_mut(c).add_children(&[d]); + world.entity_mut(a0).add_children(&[a1, a2]); + world.entity_mut(a2).add_children(&[a3]); let mut system_state = SystemState::<(Query<&Children>, Query<&A>)>::new(world); let (children_query, a_query) = system_state.get(world); - let result: Vec<_> = a_query.iter_many(children_query.iter_leaves(a)).collect(); + let result: Vec<_> = a_query.iter_many(children_query.iter_leaves(a0)).collect(); assert_eq!([&A(1), &A(3)], result.as_slice()); } @@ -448,16 +448,16 @@ mod tests { fn siblings() { let world = &mut World::new(); - let [a, b, c, d, e] = core::array::from_fn(|i| world.spawn(A(i)).id()); + let [a0, a1, a2, a3, a4] = core::array::from_fn(|i| world.spawn(A(i)).id()); - world.entity_mut(a).add_children(&[b, c, d]); - world.entity_mut(c).add_children(&[e]); + world.entity_mut(a0).add_children(&[a1, a2, a3]); + world.entity_mut(a2).add_children(&[a4]); let mut system_state = SystemState::<(Query<(&Parent, &Children)>, Query<&A>)>::new(world); let (hierarchy_query, a_query) = system_state.get(world); let result: Vec<_> = a_query - .iter_many(hierarchy_query.iter_siblings(b)) + .iter_many(hierarchy_query.iter_siblings(a1)) .collect(); assert_eq!([&A(2), &A(3)], result.as_slice()); From e690db17980b781aba6dc47f52bb16735c670d5d Mon Sep 17 00:00:00 2001 From: Viktor Gustavsson Date: Sat, 5 Oct 2024 20:46:20 +0200 Subject: [PATCH 08/17] Fix iter_siblings() --- crates/bevy_hierarchy/src/query_extension.rs | 80 +++++--------------- 1 file changed, 20 insertions(+), 60 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index 306187c70ec67..2e3e5bd7a3a4e 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -7,7 +7,6 @@ use bevy_ecs::{ query::{QueryData, QueryFilter, WorldQuery}, system::Query, }; -use smallvec::SmallVec; use crate::{Children, Parent}; @@ -44,9 +43,9 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { /// Returns an [`Iterator`] of [`Entity`]s over the `entity`s immediate siblings, who share them same parent. /// /// The entity itself is not included in the iterator. - fn iter_siblings(&'w self, entity: Entity) -> SiblingIter<'w, 's, D, F> + fn iter_siblings(&'w self, entity: Entity) -> impl Iterator where - D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>; + D::ReadOnly: WorldQuery = (Option<&'w Parent>, Option<&'w Children>)>; /// Returns an [`Iterator`] of [`Entity`]s over all of `entity`s descendants. /// @@ -161,11 +160,24 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q LeafIter::new(self, entity) } - fn iter_siblings(&'w self, entity: Entity) -> SiblingIter<'w, 's, D, F> + fn iter_siblings(&'w self, entity: Entity) -> impl Iterator where - D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, + D::ReadOnly: WorldQuery = (Option<&'w Parent>, Option<&'w Children>)>, { - SiblingIter::::new(self, entity) + self.get(entity).into_iter().flat_map(move |(parent, _)| { + parent.into_iter().flat_map(move |parent| { + self.get(parent.get()) + .into_iter() + .flat_map(move |(_, children)| { + children + .into_iter() + .flat_map(move |children| { + children.iter().filter(move |child| **child != entity) + }) + .copied() + }) + }) + }) } fn iter_descendants(&'w self, entity: Entity) -> DescendantIter<'w, 's, D, F> @@ -228,59 +240,6 @@ where } } -/// An [`Iterator`] of [`Entity`]s over the siblings of an [`Entity`]. -pub struct SiblingIter<'w, 's, D: QueryData, F: QueryFilter> -where - D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, -{ - // Unlike other iterators, we don't need to store the query here, - // as the number of siblings is likely to be much smaller than the number of descendants. - small_vec: SmallVec<[Entity; 8]>, - _phantom: PhantomData<(&'w D, &'s F)>, -} - -impl<'w, 's, D: QueryData, F: QueryFilter> SiblingIter<'w, 's, D, F> -where - D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, -{ - /// Returns a new [`SiblingIter`]. - pub fn new(hierarchy_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { - match hierarchy_query.get(entity) { - Ok((parent, _)) => { - let Ok((_, children_of_parent)) = hierarchy_query.get(parent.get()) else { - return SiblingIter { - small_vec: SmallVec::new(), - _phantom: PhantomData, - }; - }; - - let siblings = children_of_parent.iter().filter(|child| **child != entity); - - SiblingIter { - small_vec: SmallVec::from_iter(siblings.copied()), - _phantom: PhantomData, - } - } - Err(_) => SiblingIter { - small_vec: SmallVec::new(), - _phantom: PhantomData, - }, - } - } -} - -impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for SiblingIter<'w, 's, D, F> -where - D::ReadOnly: WorldQuery = (&'w Parent, &'w Children)>, -{ - type Item = Entity; - - fn next(&mut self) -> Option { - let entity: Entity = self.small_vec.pop()?; - Some(entity) - } -} - /// An [`Iterator`] of [`Entity`]s over the descendants of an [`Entity`]. /// /// Traverses the hierarchy breadth-first. @@ -453,7 +412,8 @@ mod tests { world.entity_mut(a0).add_children(&[a1, a2, a3]); world.entity_mut(a2).add_children(&[a4]); - let mut system_state = SystemState::<(Query<(&Parent, &Children)>, Query<&A>)>::new(world); + let mut system_state = + SystemState::<(Query<(Option<&Parent>, Option<&Children>)>, Query<&A>)>::new(world); let (hierarchy_query, a_query) = system_state.get(world); let result: Vec<_> = a_query From 03452411c3e23df77dfe0ffa10f6930d10f1e028 Mon Sep 17 00:00:00 2001 From: Viktor Gustavsson Date: Sun, 6 Oct 2024 18:52:33 +0200 Subject: [PATCH 09/17] Clean up iter_siblings a bit by flattening the expression --- crates/bevy_hierarchy/src/query_extension.rs | 22 +++++++------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index 2e3e5bd7a3a4e..3b66feb9bd0f7 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -164,20 +164,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q where D::ReadOnly: WorldQuery = (Option<&'w Parent>, Option<&'w Children>)>, { - self.get(entity).into_iter().flat_map(move |(parent, _)| { - parent.into_iter().flat_map(move |parent| { - self.get(parent.get()) - .into_iter() - .flat_map(move |(_, children)| { - children - .into_iter() - .flat_map(move |children| { - children.iter().filter(move |child| **child != entity) - }) - .copied() - }) - }) - }) + self.get(entity) + .ok() + .and_then(|(maybe_parent, _)| maybe_parent.map(Parent::get)) + .and_then(|parent| self.get(parent).ok()) + .and_then(|(_, maybe_children)| maybe_children) + .into_iter() + .flat_map(move |children| children.iter().filter(move |child| **child != entity)) + .copied() } fn iter_descendants(&'w self, entity: Entity) -> DescendantIter<'w, 's, D, F> From c7f46e54034ced8e9d814c7ab81da08a58c2e832 Mon Sep 17 00:00:00 2001 From: Viktor Gustavsson Date: Sun, 6 Oct 2024 19:06:18 +0200 Subject: [PATCH 10/17] Clean up children() and return slice instead of iterator --- crates/bevy_hierarchy/src/query_extension.rs | 39 ++------------------ 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index 3b66feb9bd0f7..7581867218762 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -20,7 +20,7 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { /// Returns a slice over the [`Children`] of the given `entity`. /// /// This may be empty if the `entity` has no children. - fn children(&'w self, entity: Entity) -> impl Iterator + 'w + fn children(&'w self, entity: Entity) -> &'w [Entity] where D::ReadOnly: WorldQuery = &'w Children>; @@ -103,43 +103,12 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q self.get(entity).map(Parent::get).ok() } - fn children(&'w self, entity: Entity) -> impl Iterator + fn children(&'w self, entity: Entity) -> &'w [Entity] where ::ReadOnly: WorldQuery = &'w Children>, { - // We must return the same type from both branches of the match - // So we've defined a throwaway enum to wrap the two types - enum MaybeChildrenIter { - Children { cursor: usize, vec: Vec }, - None, - } - - impl Iterator for MaybeChildrenIter { - type Item = Entity; - - fn next(&mut self) -> Option { - match self { - MaybeChildrenIter::Children { cursor, vec } => { - if *cursor < vec.len() { - let entity = vec[*cursor]; - *cursor += 1; - Some(entity) - } else { - None - } - } - MaybeChildrenIter::None => None, - } - } - } - - match self.get(entity) { - Ok(children) => MaybeChildrenIter::Children { - cursor: 0, - vec: children.to_vec(), - }, - Err(_) => MaybeChildrenIter::None, - } + self.get(entity) + .map_or(&[] as &[Entity], |children| children) } fn root_parent(&'w self, entity: Entity) -> Entity From bf332c64ec7a9f2418146f0d93483350271e8d1c Mon Sep 17 00:00:00 2001 From: Viktor Gustavsson Date: Sun, 6 Oct 2024 19:09:20 +0200 Subject: [PATCH 11/17] Rename root_parent -> root_ancestor --- crates/bevy_hierarchy/src/query_extension.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index 7581867218762..d7cca5584146d 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -27,7 +27,7 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { /// Returns the topmost ancestor of the given `entity`. /// /// This may be the entity itself if it has no parent. - fn root_parent(&'w self, entity: Entity) -> Entity + fn root_ancestor(&'w self, entity: Entity) -> Entity where D::ReadOnly: WorldQuery = &'w Parent>; @@ -111,13 +111,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q .map_or(&[] as &[Entity], |children| children) } - fn root_parent(&'w self, entity: Entity) -> Entity + fn root_ancestor(&'w self, entity: Entity) -> Entity where ::ReadOnly: WorldQuery = &'w Parent>, { // Recursively search up the tree until we're out of parents match self.get(entity) { - Ok(parent) => self.root_parent(parent.get()), + Ok(parent) => self.root_ancestor(parent.get()), Err(_) => entity, } } @@ -333,7 +333,7 @@ mod tests { } #[test] - fn root_parent() { + fn root_ancestor() { let world = &mut World::new(); let [a0, a1, a2] = core::array::from_fn(|i| world.spawn(A(i)).id()); @@ -344,9 +344,9 @@ mod tests { let mut system_state = SystemState::>::new(world); let parent_query = system_state.get(world); - assert_eq!(a0, parent_query.root_parent(a2)); - assert_eq!(a0, parent_query.root_parent(a1)); - assert_eq!(a0, parent_query.root_parent(a0)); + assert_eq!(a0, parent_query.root_ancestor(a2)); + assert_eq!(a0, parent_query.root_ancestor(a1)); + assert_eq!(a0, parent_query.root_ancestor(a0)); } #[test] From aadcc99651a60177121ae057c550f4f689e9269d Mon Sep 17 00:00:00 2001 From: Viktor Gustavsson Date: Sun, 6 Oct 2024 19:18:20 +0200 Subject: [PATCH 12/17] Clean up iter_leaves and reduce allocations --- crates/bevy_hierarchy/src/query_extension.rs | 59 +++----------------- 1 file changed, 9 insertions(+), 50 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index d7cca5584146d..a84a6cecdda93 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use alloc::collections::VecDeque; use bevy_ecs::{ @@ -36,7 +34,7 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { /// Only entities which have no children are considered leaves. /// This will not include the entity itself, and will not include any entities which are not descendants of the entity, /// even if they are leaves in the same hierarchical tree. - fn iter_leaves(&'w self, entity: Entity) -> LeafIter<'w, 's, D, F> + fn iter_leaves(&'w self, entity: Entity) -> impl Iterator + 'w where D::ReadOnly: WorldQuery = &'w Children>; @@ -122,11 +120,17 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q } } - fn iter_leaves(&'w self, entity: Entity) -> LeafIter<'w, 's, D, F> + fn iter_leaves(&'w self, entity: Entity) -> impl Iterator where ::ReadOnly: WorldQuery = &'w Children>, { - LeafIter::new(self, entity) + self.iter_descendants(entity).filter(|entity| { + self.get(*entity) + // These are leaf nodes if they have the `Children` component but it's empty + .map(|children| children.is_empty()) + // Or if they don't have the `Children` component at all + .unwrap_or(true) + }) } fn iter_siblings(&'w self, entity: Entity) -> impl Iterator @@ -158,51 +162,6 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q } } -/// An [`Iterator`] of [`Entity`]s over the leaf descendants of an [`Entity`]. -pub struct LeafIter<'w, 's, D: QueryData, F: QueryFilter> -where - D::ReadOnly: WorldQuery = &'w Children>, -{ - vecdeque: VecDeque, - // PERF: if this ends up resulting in too much memory being allocated, we can store the query instead - // like in IterDescendants - _phantom: PhantomData<(&'w D, &'s F)>, -} - -impl<'w, 's, D: QueryData, F: QueryFilter> LeafIter<'w, 's, D, F> -where - D::ReadOnly: WorldQuery = &'w Children>, -{ - /// Returns a new [`LeafIter`]. - pub fn new(children_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { - let leaf_children = children_query.iter_descendants(entity).filter(|entity| { - children_query - .get(*entity) - // These are leaf nodes if they have the `Children` component but it's empty - .map(|children| children.is_empty()) - // Or if they don't have the `Children` component at all - .unwrap_or(true) - }); - - LeafIter { - vecdeque: leaf_children.collect(), - _phantom: PhantomData, - } - } -} - -impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for LeafIter<'w, 's, D, F> -where - D::ReadOnly: WorldQuery = &'w Children>, -{ - type Item = Entity; - - fn next(&mut self) -> Option { - let entity: Entity = self.vecdeque.pop_front()?; - Some(entity) - } -} - /// An [`Iterator`] of [`Entity`]s over the descendants of an [`Entity`]. /// /// Traverses the hierarchy breadth-first. From d212eee8cb3859e49001e5ff4ecaa045cca91cf9 Mon Sep 17 00:00:00 2001 From: Viktor Gustavsson Date: Sun, 6 Oct 2024 19:20:03 +0200 Subject: [PATCH 13/17] Add docs about traversal order for iter_leaves --- crates/bevy_hierarchy/src/query_extension.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index a84a6cecdda93..2944aca4b8162 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -34,6 +34,8 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { /// Only entities which have no children are considered leaves. /// This will not include the entity itself, and will not include any entities which are not descendants of the entity, /// even if they are leaves in the same hierarchical tree. + /// + /// Traverses the hierarchy breadth-first. fn iter_leaves(&'w self, entity: Entity) -> impl Iterator + 'w where D::ReadOnly: WorldQuery = &'w Children>; From d33e3c17a85e6b089c2f81ab02727e8e96036ba6 Mon Sep 17 00:00:00 2001 From: Viktor Gustavsson Date: Sun, 6 Oct 2024 20:21:35 +0200 Subject: [PATCH 14/17] Add iter_descendants_depth_first --- crates/bevy_hierarchy/src/query_extension.rs | 91 +++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index 2944aca4b8162..df6532e715b66 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -5,6 +5,7 @@ use bevy_ecs::{ query::{QueryData, QueryFilter, WorldQuery}, system::Query, }; +use smallvec::SmallVec; use crate::{Children, Parent}; @@ -35,7 +36,7 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { /// This will not include the entity itself, and will not include any entities which are not descendants of the entity, /// even if they are leaves in the same hierarchical tree. /// - /// Traverses the hierarchy breadth-first. + /// Traverses the hierarchy depth-first. fn iter_leaves(&'w self, entity: Entity) -> impl Iterator + 'w where D::ReadOnly: WorldQuery = &'w Children>; @@ -71,6 +72,18 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { where D::ReadOnly: WorldQuery = &'w Children>; + /// Returns an [`Iterator`] of [`Entity`]s over all of `entity`s descendants. + /// + /// Can only be called on a [`Query`] of [`Children`] (i.e. `Query<&Children>`). + /// + /// This is a depth-first alternative to [`HierarchyQueryExt::iter_descendants`]. + fn iter_descendants_depth_first( + &'w self, + entity: Entity, + ) -> DescendantDepthFirstIter<'w, 's, D, F> + where + D::ReadOnly: WorldQuery = &'w Children>; + /// Returns an [`Iterator`] of [`Entity`]s over all of `entity`s ancestors. /// /// Does not include the entity itself. @@ -156,6 +169,16 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q DescendantIter::new(self, entity) } + fn iter_descendants_depth_first( + &'w self, + entity: Entity, + ) -> DescendantDepthFirstIter<'w, 's, D, F> + where + D::ReadOnly: WorldQuery = &'w Children>, + { + DescendantDepthFirstIter::new(self, entity) + } + fn iter_ancestors(&'w self, entity: Entity) -> AncestorIter<'w, 's, D, F> where D::ReadOnly: WorldQuery = &'w Parent>, @@ -210,6 +233,51 @@ where } } +/// An [`Iterator`] of [`Entity`]s over the descendants of an [`Entity`]. +/// +/// Traverses the hierarchy depth-first. +pub struct DescendantDepthFirstIter<'w, 's, D: QueryData, F: QueryFilter> +where + D::ReadOnly: WorldQuery = &'w Children>, +{ + children_query: &'w Query<'w, 's, D, F>, + stack: SmallVec<[Entity; 8]>, +} + +impl<'w, 's, D: QueryData, F: QueryFilter> DescendantDepthFirstIter<'w, 's, D, F> +where + D::ReadOnly: WorldQuery = &'w Children>, +{ + /// Returns a new [`DescendantDepthFirstIter`]. + pub fn new(children_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { + DescendantDepthFirstIter { + children_query, + stack: children_query + .get(entity) + .map_or(SmallVec::new(), |children| { + children.iter().rev().copied().collect() + }), + } + } +} + +impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for DescendantDepthFirstIter<'w, 's, D, F> +where + D::ReadOnly: WorldQuery = &'w Children>, +{ + type Item = Entity; + + fn next(&mut self) -> Option { + let entity = self.stack.pop()?; + + if let Ok(children) = self.children_query.get(entity) { + self.stack.extend(children.iter().rev().copied()); + } + + Some(entity) + } +} + /// An [`Iterator`] of [`Entity`]s over the ancestors of an [`Entity`]. pub struct AncestorIter<'w, 's, D: QueryData, F: QueryFilter> where @@ -264,7 +332,7 @@ mod tests { let [a0, a1, a2, a3] = core::array::from_fn(|i| world.spawn(A(i)).id()); world.entity_mut(a0).add_children(&[a1, a2]); - world.entity_mut(a2).add_children(&[a3]); + world.entity_mut(a1).add_children(&[a3]); let mut system_state = SystemState::<(Query<&Children>, Query<&A>)>::new(world); let (children_query, a_query) = system_state.get(world); @@ -276,6 +344,25 @@ mod tests { assert_eq!([&A(1), &A(2), &A(3)], result.as_slice()); } + #[test] + fn descendant_depth_first_iter() { + let world = &mut World::new(); + + let [a0, a1, a2, a3] = core::array::from_fn(|i| world.spawn(A(i)).id()); + + world.entity_mut(a0).add_children(&[a1, a2]); + world.entity_mut(a1).add_children(&[a3]); + + let mut system_state = SystemState::<(Query<&Children>, Query<&A>)>::new(world); + let (children_query, a_query) = system_state.get(world); + + let result: Vec<_> = a_query + .iter_many(children_query.iter_descendants_depth_first(a0)) + .collect(); + + assert_eq!([&A(1), &A(3), &A(2)], result.as_slice()); + } + #[test] fn ancestor_iter() { let world = &mut World::new(); From 9014f1883870ebd70bec41a136eb1d3d13c82676 Mon Sep 17 00:00:00 2001 From: Viktor Gustavsson Date: Sun, 6 Oct 2024 20:23:48 +0200 Subject: [PATCH 15/17] Change iter_leaves to use depth-first traversal --- crates/bevy_hierarchy/src/query_extension.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index df6532e715b66..32a85a6bb5584 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -139,7 +139,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> HierarchyQueryExt<'w, 's, D, F> for Q where ::ReadOnly: WorldQuery = &'w Children>, { - self.iter_descendants(entity).filter(|entity| { + self.iter_descendants_depth_first(entity).filter(|entity| { self.get(*entity) // These are leaf nodes if they have the `Children` component but it's empty .map(|children| children.is_empty()) @@ -404,14 +404,14 @@ mod tests { let [a0, a1, a2, a3] = core::array::from_fn(|i| world.spawn(A(i)).id()); world.entity_mut(a0).add_children(&[a1, a2]); - world.entity_mut(a2).add_children(&[a3]); + world.entity_mut(a1).add_children(&[a3]); let mut system_state = SystemState::<(Query<&Children>, Query<&A>)>::new(world); let (children_query, a_query) = system_state.get(world); let result: Vec<_> = a_query.iter_many(children_query.iter_leaves(a0)).collect(); - assert_eq!([&A(1), &A(3)], result.as_slice()); + assert_eq!([&A(3), &A(2)], result.as_slice()); } #[test] From 32d83e4cfeed1c944884b9cd290c27fab950d11b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 6 Oct 2024 17:43:00 -0400 Subject: [PATCH 16/17] Doc comment typo Co-authored-by: poopy --- crates/bevy_hierarchy/src/query_extension.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index 32a85a6bb5584..ecac1729a4aa0 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -41,7 +41,7 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { where D::ReadOnly: WorldQuery = &'w Children>; - /// Returns an [`Iterator`] of [`Entity`]s over the `entity`s immediate siblings, who share them same parent. +/// Returns an [`Iterator`] of [`Entity`]s over the `entity`s immediate siblings, who share the same parent. /// /// The entity itself is not included in the iterator. fn iter_siblings(&'w self, entity: Entity) -> impl Iterator From 63d617f4e617a29600dcb6535e184f89e92f0880 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 6 Oct 2024 18:33:22 -0400 Subject: [PATCH 17/17] Formatting Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com> --- crates/bevy_hierarchy/src/query_extension.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index ecac1729a4aa0..5cd8631310762 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -41,7 +41,7 @@ pub trait HierarchyQueryExt<'w, 's, D: QueryData, F: QueryFilter> { where D::ReadOnly: WorldQuery = &'w Children>; -/// Returns an [`Iterator`] of [`Entity`]s over the `entity`s immediate siblings, who share the same parent. + /// Returns an [`Iterator`] of [`Entity`]s over the `entity`s immediate siblings, who share the same parent. /// /// The entity itself is not included in the iterator. fn iter_siblings(&'w self, entity: Entity) -> impl Iterator