pub fn propagate(
scratch: &mut Scratch,
entities: &Entities,
parents: &Store<Parent>,
locals: &Store<Local>,
globals: &mut Store<Global>,
) -> PropagatedExpand description
Compose every Local into a Global, parents first.
Runs in time proportional to the number of scene nodes plus the highest
occupied slot: each node’s ancestry is climbed once across the whole call,
not once per node. Allocates only while scratch or globals is still
growing to fit the world.
The return value is the only report of a malformed hierarchy — a discarded
Propagated turns a parent typo into a thing quietly drawn in the wrong
place — so it is #[must_use].
§Example
A hub turned a quarter turn, with a child one unit along its x axis. The child swings round to the y axis rather than staying put, which is the one thing a caller could not have got by adding two vectors — and the reason this crate exists rather than the composition living at each call site.
use renew_ecs::{Entities, Store};
use renew_fixed::{Angle, Fixed, Vec2};
use renew_scene::{Global, Local, Parent, Scratch, propagate};
let mut entities = Entities::new();
let (mut parents, mut locals, mut globals) =
(Store::default(), Store::default(), Store::default());
let hub = entities.spawn();
locals.insert(hub.index(), Local::new(Vec2::ZERO, Angle::QUARTER));
let arm = entities.spawn();
locals.insert(
arm.index(),
Local::new(Vec2::new(Fixed::ONE, Fixed::ZERO), Angle::ZERO),
);
parents.insert(arm.index(), Parent(hub));
let mut scratch = Scratch::new();
let counts = propagate(&mut scratch, &entities, &parents, &locals, &mut globals);
assert_eq!(counts.nodes, 2);
let placed: Global = *globals.get(arm.index()).expect("placed");
assert_eq!(placed.translation(), Vec2::new(Fixed::ZERO, Fixed::ONE));
assert_eq!(placed.rotation(), Angle::QUARTER);