Skip to content

Commit 11ad3b6

Browse files
ElliottjPiercebjoernp116dmyyyviridia
authored andcommitted
Finish #17558, re-adding insert_children (#18409)
fixes #17478 # Objective - Complete #17558. - the `insert_children` method was previously removed, and as #17478 points out, needs to be added back. ## Solution - Add a `OrderedRelationshipSourceCollection`, which allows sorting, ordering, rearranging, etc of a `RelationshipSourceCollection`. - Implement `insert_related` - Implement `insert_children` - Tidy up some docs while I'm here. ## Testing @bjoernp116 set up a unit test, and I added a doc test to `OrderedRelationshipSourceCollection`. --------- Co-authored-by: bjoernp116 <[email protected]> Co-authored-by: Dmytro Banin <[email protected]> Co-authored-by: Talin <[email protected]>
1 parent 202faf6 commit 11ad3b6

File tree

4 files changed

+254
-2
lines changed

4 files changed

+254
-2
lines changed

crates/bevy_ecs/src/hierarchy.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,17 +256,26 @@ pub type ChildSpawnerCommands<'w> = RelatedSpawnerCommands<'w, ChildOf>;
256256

257257
impl<'w> EntityWorldMut<'w> {
258258
/// Spawns children of this entity (with a [`ChildOf`] relationship) by taking a function that operates on a [`ChildSpawner`].
259+
/// See also [`with_related`](Self::with_related).
259260
pub fn with_children(&mut self, func: impl FnOnce(&mut ChildSpawner)) -> &mut Self {
260261
self.with_related(func);
261262
self
262263
}
263264

264265
/// Adds the given children to this entity
266+
/// See also [`add_related`](Self::add_related).
265267
pub fn add_children(&mut self, children: &[Entity]) -> &mut Self {
266268
self.add_related::<ChildOf>(children)
267269
}
268270

271+
/// Insert children at specific index.
272+
/// See also [`insert_related`](Self::insert_related).
273+
pub fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self {
274+
self.insert_related::<ChildOf>(index, children)
275+
}
276+
269277
/// Adds the given child to this entity
278+
/// See also [`add_related`](Self::add_related).
270279
pub fn add_child(&mut self, child: Entity) -> &mut Self {
271280
self.add_related::<ChildOf>(&[child])
272281
}
@@ -596,6 +605,35 @@ mod tests {
596605
);
597606
}
598607

608+
#[test]
609+
fn insert_children() {
610+
let mut world = World::new();
611+
let child1 = world.spawn_empty().id();
612+
let child2 = world.spawn_empty().id();
613+
let child3 = world.spawn_empty().id();
614+
let child4 = world.spawn_empty().id();
615+
616+
let mut entity_world_mut = world.spawn_empty();
617+
618+
let first_children = entity_world_mut.add_children(&[child1, child2]);
619+
620+
let root = first_children.insert_children(1, &[child3, child4]).id();
621+
622+
let hierarchy = get_hierarchy(&world, root);
623+
assert_eq!(
624+
hierarchy,
625+
Node::new_with(
626+
root,
627+
vec![
628+
Node::new(child1),
629+
Node::new(child3),
630+
Node::new(child4),
631+
Node::new(child2)
632+
]
633+
)
634+
);
635+
}
636+
599637
#[test]
600638
fn self_parenting_invalid() {
601639
let mut world = World::new();

crates/bevy_ecs/src/relationship/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,14 @@ pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
209209
///
210210
/// # Warning
211211
/// This should generally not be called by user code, as modifying the internal collection could invalidate the relationship.
212+
/// The collection should not contain duplicates.
212213
fn collection_mut_risky(&mut self) -> &mut Self::Collection;
213214

214215
/// Creates a new [`RelationshipTarget`] from the given [`RelationshipTarget::Collection`].
215216
///
216217
/// # Warning
217218
/// This should generally not be called by user code, as constructing the internal collection could invalidate the relationship.
219+
/// The collection should not contain duplicates.
218220
fn from_collection_risky(collection: Self::Collection) -> Self;
219221

220222
/// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.

crates/bevy_ecs/src/relationship/related_methods.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use crate::{
1010
use bevy_platform_support::prelude::{Box, Vec};
1111
use core::{marker::PhantomData, mem};
1212

13+
use super::OrderedRelationshipSourceCollection;
14+
1315
impl<'w> EntityWorldMut<'w> {
1416
/// Spawns entities related to this entity (with the `R` relationship) by taking a function that operates on a [`RelatedSpawner`].
1517
pub fn with_related<R: Relationship>(
@@ -36,6 +38,64 @@ impl<'w> EntityWorldMut<'w> {
3638
self
3739
}
3840

41+
/// Relates the given entities to this entity with the relation `R`, starting at this particular index.
42+
///
43+
/// If the `related` has duplicates, a related entity will take the index of its last occurrence in `related`.
44+
/// If the indices go out of bounds, they will be clamped into bounds.
45+
/// This will not re-order existing related entities unless they are in `related`.
46+
///
47+
/// # Example
48+
///
49+
/// ```
50+
/// use bevy_ecs::prelude::*;
51+
///
52+
/// let mut world = World::new();
53+
/// let e0 = world.spawn_empty().id();
54+
/// let e1 = world.spawn_empty().id();
55+
/// let e2 = world.spawn_empty().id();
56+
/// let e3 = world.spawn_empty().id();
57+
/// let e4 = world.spawn_empty().id();
58+
///
59+
/// let mut main_entity = world.spawn_empty();
60+
/// main_entity.add_related::<ChildOf>(&[e0, e1, e2, e2]);
61+
/// main_entity.insert_related::<ChildOf>(1, &[e0, e3, e4, e4]);
62+
/// let main_id = main_entity.id();
63+
///
64+
/// let relationship_source = main_entity.get::<Children>().unwrap().collection();
65+
/// assert_eq!(relationship_source, &[e1, e0, e3, e2, e4]);
66+
/// ```
67+
pub fn insert_related<R: Relationship>(&mut self, index: usize, related: &[Entity]) -> &mut Self
68+
where
69+
<R::RelationshipTarget as RelationshipTarget>::Collection:
70+
OrderedRelationshipSourceCollection,
71+
{
72+
let id = self.id();
73+
self.world_scope(|world| {
74+
for (offset, related) in related.iter().enumerate() {
75+
let index = index + offset;
76+
if world
77+
.get::<R>(*related)
78+
.is_some_and(|relationship| relationship.get() == id)
79+
{
80+
world
81+
.get_mut::<R::RelationshipTarget>(id)
82+
.expect("hooks should have added relationship target")
83+
.collection_mut_risky()
84+
.place(*related, index);
85+
} else {
86+
world.entity_mut(*related).insert(R::from(id));
87+
world
88+
.get_mut::<R::RelationshipTarget>(id)
89+
.expect("hooks should have added relationship target")
90+
.collection_mut_risky()
91+
.place_most_recent(index);
92+
}
93+
}
94+
});
95+
96+
self
97+
}
98+
3999
/// Replaces all the related entities with a new set of entities.
40100
pub fn replace_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {
41101
type Collection<R> =

crates/bevy_ecs/src/relationship/relationship_source_collection.rs

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,58 @@ pub trait RelationshipSourceCollection {
7474
}
7575
}
7676

77+
/// This trait signals that a [`RelationshipSourceCollection`] is ordered.
78+
pub trait OrderedRelationshipSourceCollection: RelationshipSourceCollection {
79+
/// Inserts the entity at a specific index.
80+
/// If the index is too large, the entity will be added to the end of the collection.
81+
fn insert(&mut self, index: usize, entity: Entity);
82+
/// Removes the entity at the specified idnex if it exists.
83+
fn remove_at(&mut self, index: usize) -> Option<Entity>;
84+
/// Inserts the entity at a specific index.
85+
/// This will never reorder other entities.
86+
/// If the index is too large, the entity will be added to the end of the collection.
87+
fn insert_stable(&mut self, index: usize, entity: Entity);
88+
/// Removes the entity at the specified idnex if it exists.
89+
/// This will never reorder other entities.
90+
fn remove_at_stable(&mut self, index: usize) -> Option<Entity>;
91+
/// Sorts the source collection.
92+
fn sort(&mut self);
93+
/// Inserts the entity at the proper place to maintain sorting.
94+
fn insert_sorted(&mut self, entity: Entity);
95+
96+
/// This places the most recently added entity at the particular index.
97+
fn place_most_recent(&mut self, index: usize);
98+
99+
/// This places the given entity at the particular index.
100+
/// This will do nothing if the entity is not in the collection.
101+
/// If the index is out of bounds, this will put the entity at the end.
102+
fn place(&mut self, entity: Entity, index: usize);
103+
104+
/// Adds the entity at index 0.
105+
fn push_front(&mut self, entity: Entity) {
106+
self.insert(0, entity);
107+
}
108+
109+
/// Adds the entity to the back of the collection.
110+
fn push_back(&mut self, entity: Entity) {
111+
self.insert(usize::MAX, entity);
112+
}
113+
114+
/// Removes the first entity.
115+
fn pop_front(&mut self) -> Option<Entity> {
116+
self.remove_at(0)
117+
}
118+
119+
/// Removes the last entity.
120+
fn pop_back(&mut self) -> Option<Entity> {
121+
if self.is_empty() {
122+
None
123+
} else {
124+
self.remove_at(self.len() - 1)
125+
}
126+
}
127+
}
128+
77129
impl RelationshipSourceCollection for Vec<Entity> {
78130
type SourceIter<'a> = core::iter::Copied<core::slice::Iter<'a, Entity>>;
79131

@@ -98,7 +150,6 @@ impl RelationshipSourceCollection for Vec<Entity> {
98150
fn remove(&mut self, entity: Entity) -> bool {
99151
if let Some(index) = <[Entity]>::iter(self).position(|e| *e == entity) {
100152
Vec::remove(self, index);
101-
102153
return true;
103154
}
104155

@@ -126,6 +177,57 @@ impl RelationshipSourceCollection for Vec<Entity> {
126177
}
127178
}
128179

180+
impl OrderedRelationshipSourceCollection for Vec<Entity> {
181+
fn insert(&mut self, index: usize, entity: Entity) {
182+
self.push(entity);
183+
let len = self.len();
184+
if index < len {
185+
self.swap(index, len - 1);
186+
}
187+
}
188+
189+
fn remove_at(&mut self, index: usize) -> Option<Entity> {
190+
(index < self.len()).then(|| self.swap_remove(index))
191+
}
192+
193+
fn insert_stable(&mut self, index: usize, entity: Entity) {
194+
if index < self.len() {
195+
Vec::insert(self, index, entity);
196+
} else {
197+
self.push(entity);
198+
}
199+
}
200+
201+
fn remove_at_stable(&mut self, index: usize) -> Option<Entity> {
202+
(index < self.len()).then(|| self.remove(index))
203+
}
204+
205+
fn sort(&mut self) {
206+
self.sort_unstable();
207+
}
208+
209+
fn insert_sorted(&mut self, entity: Entity) {
210+
let index = self.partition_point(|e| e <= &entity);
211+
self.insert_stable(index, entity);
212+
}
213+
214+
fn place_most_recent(&mut self, index: usize) {
215+
if let Some(entity) = self.pop() {
216+
let index = index.min(self.len() - 1);
217+
self.insert(index, entity);
218+
}
219+
}
220+
221+
fn place(&mut self, entity: Entity, index: usize) {
222+
if let Some(current) = <[Entity]>::iter(self).position(|e| *e == entity) {
223+
// The len is at least 1, so the subtraction is safe.
224+
let index = index.min(self.len() - 1);
225+
Vec::remove(self, current);
226+
self.insert(index, entity);
227+
};
228+
}
229+
}
230+
129231
impl RelationshipSourceCollection for EntityHashSet {
130232
type SourceIter<'a> = core::iter::Copied<crate::entity::hash_set::Iter<'a>>;
131233

@@ -196,7 +298,6 @@ impl<const N: usize> RelationshipSourceCollection for SmallVec<[Entity; N]> {
196298
fn remove(&mut self, entity: Entity) -> bool {
197299
if let Some(index) = <[Entity]>::iter(self).position(|e| *e == entity) {
198300
SmallVec::remove(self, index);
199-
200301
return true;
201302
}
202303

@@ -277,6 +378,57 @@ impl RelationshipSourceCollection for Entity {
277378
}
278379
}
279380

381+
impl<const N: usize> OrderedRelationshipSourceCollection for SmallVec<[Entity; N]> {
382+
fn insert(&mut self, index: usize, entity: Entity) {
383+
self.push(entity);
384+
let len = self.len();
385+
if index < len {
386+
self.swap(index, len - 1);
387+
}
388+
}
389+
390+
fn remove_at(&mut self, index: usize) -> Option<Entity> {
391+
(index < self.len()).then(|| self.swap_remove(index))
392+
}
393+
394+
fn insert_stable(&mut self, index: usize, entity: Entity) {
395+
if index < self.len() {
396+
SmallVec::<[Entity; N]>::insert(self, index, entity);
397+
} else {
398+
self.push(entity);
399+
}
400+
}
401+
402+
fn remove_at_stable(&mut self, index: usize) -> Option<Entity> {
403+
(index < self.len()).then(|| self.remove(index))
404+
}
405+
406+
fn sort(&mut self) {
407+
self.sort_unstable();
408+
}
409+
410+
fn insert_sorted(&mut self, entity: Entity) {
411+
let index = self.partition_point(|e| e <= &entity);
412+
self.insert_stable(index, entity);
413+
}
414+
415+
fn place_most_recent(&mut self, index: usize) {
416+
if let Some(entity) = self.pop() {
417+
let index = index.min(self.len() - 1);
418+
self.insert(index, entity);
419+
}
420+
}
421+
422+
fn place(&mut self, entity: Entity, index: usize) {
423+
if let Some(current) = <[Entity]>::iter(self).position(|e| *e == entity) {
424+
// The len is at least 1, so the subtraction is safe.
425+
let index = index.min(self.len() - 1);
426+
SmallVec::<[Entity; N]>::remove(self, current);
427+
self.insert(index, entity);
428+
};
429+
}
430+
}
431+
280432
#[cfg(test)]
281433
mod tests {
282434
use super::*;

0 commit comments

Comments
 (0)