avian3d/sync/
ancestor_marker.rsuse core::marker::PhantomData;
use bevy::prelude::*;
pub struct AncestorMarkerPlugin<C: Component>(PhantomData<C>);
impl<C: Component> Default for AncestorMarkerPlugin<C> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<C: Component> Plugin for AncestorMarkerPlugin<C> {
fn build(&self, app: &mut App) {
app.add_observer(
|trigger: Trigger<OnInsert, (ChildOf, C)>,
mut commands: Commands,
collider_query: Query<&C>,
parent_query: Query<&ChildOf>,
ancestor_query: Query<(), With<AncestorMarker<C>>>| {
let entity = trigger.target();
if collider_query.contains(entity) {
add_ancestor_markers(
entity,
&mut commands,
&parent_query,
&ancestor_query,
false,
);
}
},
);
#[allow(clippy::type_complexity)]
app.add_observer(
|trigger: Trigger<OnReplace, (ChildOf, C)>,
mut commands: Commands,
collider_query: Query<&C>,
child_query: Query<&Children>,
parent_query: Query<&ChildOf>,
ancestor_query: Query<
(Entity, Has<C>),
Or<(With<AncestorMarker<C>>, With<C>)>
>| {
let entity = trigger.target();
if collider_query.contains(entity) {
remove_ancestor_markers(entity, &mut commands, &parent_query, &child_query, &ancestor_query, false);
}
},
);
}
}
#[derive(Component, Copy, Reflect)]
#[reflect(Component, Default)]
pub struct AncestorMarker<C: Component> {
#[reflect(ignore)]
_phantom: PhantomData<C>,
}
impl<C: Component> Clone for AncestorMarker<C> {
fn clone(&self) -> Self {
Self::default()
}
}
impl<C: Component> Default for AncestorMarker<C> {
fn default() -> Self {
Self {
_phantom: PhantomData,
}
}
}
fn add_ancestor_markers<C: Component>(
entity: Entity,
commands: &mut Commands,
parent_query: &Query<&ChildOf>,
ancestor_query: &Query<(), With<AncestorMarker<C>>>,
include_self: bool,
) {
if include_self {
commands
.entity(entity)
.insert(AncestorMarker::<C>::default());
}
for parent_entity in parent_query.iter_ancestors(entity) {
if ancestor_query.contains(parent_entity) {
break;
} else {
commands
.entity(parent_entity)
.insert(AncestorMarker::<C>::default());
}
}
}
fn remove_component<T: Bundle>(commands: &mut Commands, entity: Entity) {
if let Ok(mut entity_commands) = commands.get_entity(entity) {
entity_commands.try_remove::<T>();
}
}
#[allow(clippy::type_complexity)]
fn remove_ancestor_markers<C: Component>(
entity: Entity,
commands: &mut Commands,
parent_query: &Query<&ChildOf>,
child_query: &Query<&Children>,
ancestor_query: &Query<(Entity, Has<C>), Or<(With<AncestorMarker<C>>, With<C>)>>,
include_self: bool,
) {
if include_self {
if let Ok(children) = child_query.get(entity) {
let keep_marker = ancestor_query
.iter_many(children)
.any(|(parent_child, _has_c)| parent_child != entity);
if keep_marker {
return;
} else {
remove_component::<AncestorMarker<C>>(commands, entity);
}
} else {
remove_component::<AncestorMarker<C>>(commands, entity);
}
}
let mut previous_parent = entity;
for parent_entity in parent_query.iter_ancestors(entity) {
if let Ok(children) = child_query.get(parent_entity) {
let keep_marker = ancestor_query
.iter_many(children)
.any(|(child, has_c)| child != previous_parent || (has_c && child != entity));
if keep_marker {
return;
} else {
remove_component::<AncestorMarker<C>>(commands, parent_entity);
}
} else {
remove_component::<AncestorMarker<C>>(commands, entity);
}
previous_parent = parent_entity;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Component)]
struct C;
#[test]
fn add_and_remove_component() {
let mut app = App::new();
app.add_plugins(AncestorMarkerPlugin::<C>::default());
let an = app.world_mut().spawn_empty().id();
let bn = app.world_mut().spawn(ChildOf(an)).id();
let cy = app.world_mut().spawn((C, ChildOf(an))).id();
let dn = app.world_mut().spawn(ChildOf(cy)).id();
let en = app.world_mut().spawn(ChildOf(cy)).id();
let fy = app.world_mut().spawn((C, ChildOf(dn))).id();
let gy = app.world_mut().spawn((C, ChildOf(dn))).id();
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(bn).contains::<AncestorMarker<C>>());
assert!(app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(dn).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(en).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(fy).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(gy).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(fy);
entity_mut.remove::<C>();
assert!(app.world().entity(dn).contains::<AncestorMarker<C>>());
assert!(app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(gy);
entity_mut.remove::<C>();
assert!(!app.world().entity(dn).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(cy);
entity_mut.remove::<C>();
assert!(!app.world().entity(an).contains::<AncestorMarker<C>>());
}
#[test]
fn remove_children() {
let mut app = App::new();
app.add_plugins(AncestorMarkerPlugin::<C>::default());
let an = app.world_mut().spawn_empty().id();
let bn = app.world_mut().spawn(ChildOf(an)).id();
let cy = app.world_mut().spawn((C, ChildOf(an))).id();
let dn = app.world_mut().spawn(ChildOf(cy)).id();
let en = app.world_mut().spawn(ChildOf(cy)).id();
let fy = app.world_mut().spawn((C, ChildOf(dn))).id();
let gy = app.world_mut().spawn((C, ChildOf(dn))).id();
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(bn).contains::<AncestorMarker<C>>());
assert!(app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(dn).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(en).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(fy).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(gy).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(fy);
entity_mut.remove::<ChildOf>();
assert!(app.world().entity(dn).contains::<AncestorMarker<C>>());
assert!(app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(gy);
entity_mut.remove::<ChildOf>();
assert!(!app.world().entity(dn).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(cy);
entity_mut.remove::<ChildOf>();
assert!(!app.world().entity(an).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(an);
entity_mut.add_child(cy);
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(cy);
entity_mut.remove::<ChildOf>();
entity_mut.despawn();
}
#[test]
fn move_children() {
let mut app = App::new();
app.add_plugins(AncestorMarkerPlugin::<C>::default());
let an = app.world_mut().spawn_empty().id();
let bn = app.world_mut().spawn(ChildOf(an)).id();
let cy = app.world_mut().spawn((C, ChildOf(an))).id();
let dn = app.world_mut().spawn(ChildOf(cy)).id();
let en = app.world_mut().spawn(ChildOf(cy)).id();
let fy = app.world_mut().spawn((C, ChildOf(dn))).id();
let gy = app.world_mut().spawn((C, ChildOf(dn))).id();
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(bn).contains::<AncestorMarker<C>>());
assert!(app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(dn).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(en).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(fy).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(gy).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(bn);
entity_mut.add_child(fy);
assert!(app.world().entity(bn).contains::<AncestorMarker<C>>());
assert!(app.world().entity(dn).contains::<AncestorMarker<C>>());
assert!(app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(bn);
entity_mut.add_child(gy);
assert!(!app.world().entity(dn).contains::<AncestorMarker<C>>());
assert!(!app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(cy);
entity_mut.add_children(&[fy, gy]);
assert!(!app.world().entity(bn).contains::<AncestorMarker<C>>());
assert!(app.world().entity(cy).contains::<AncestorMarker<C>>());
assert!(app.world().entity(an).contains::<AncestorMarker<C>>());
let mut entity_mut = app.world_mut().entity_mut(bn);
entity_mut.add_children(&[dn, en, fy, gy]);
}
}