use crate::frame_tree::{FrameId, FrameTree};
use crate::ref_frame_state::RefFrameState;
pub trait FrameStorage {
type Id: Copy + Eq + std::fmt::Debug;
fn parent(&self, id: Self::Id) -> Option<Self::Id>;
fn state(&self, id: Self::Id) -> RefFrameState;
}
pub fn compose_to_ancestor<S: FrameStorage>(
storage: &S,
id: S::Id,
ancestor: S::Id,
) -> RefFrameState {
if id == ancestor {
return RefFrameState::default();
}
let mut composed = storage.state(id);
let mut current = id;
while let Some(parent) = storage.parent(current) {
if parent == ancestor {
return composed;
}
composed.incr_left(&storage.state(parent));
current = parent;
}
panic!(
"compose_to_ancestor: frame {id:?} is not a descendant of \
ancestor {ancestor:?}; common_ancestor walk should have caught this"
);
}
pub fn common_ancestor<S: FrameStorage>(storage: &S, a: S::Id, b: S::Id) -> Option<S::Id> {
let mut da = depth(storage, a);
let mut db = depth(storage, b);
let mut ca = a;
let mut cb = b;
while da > db {
ca = storage.parent(ca)?;
da -= 1;
}
while db > da {
cb = storage.parent(cb)?;
db -= 1;
}
while ca != cb {
ca = storage.parent(ca)?;
cb = storage.parent(cb)?;
}
Some(ca)
}
fn depth<S: FrameStorage>(storage: &S, id: S::Id) -> usize {
let mut d = 0usize;
let mut current = id;
while let Some(parent) = storage.parent(current) {
d += 1;
current = parent;
}
d
}
pub fn compute_relative_state<S: FrameStorage>(
storage: &S,
from: S::Id,
to: S::Id,
) -> RefFrameState {
if from == to {
return RefFrameState::default();
}
let ancestor = common_ancestor(storage, from, to).unwrap_or_else(|| {
panic!(
"compute_relative_state: frames {from:?} and {to:?} do not \
share a common ancestor"
)
});
let state_from = compose_to_ancestor(storage, from, ancestor);
let state_to = compose_to_ancestor(storage, to, ancestor);
let from_negated = RefFrameState::negate(&state_from);
from_negated.incr_right(&state_to)
}
impl FrameStorage for FrameTree {
type Id = FrameId;
fn parent(&self, id: FrameId) -> Option<FrameId> {
FrameTree::parent(self, id)
}
fn state(&self, id: FrameId) -> RefFrameState {
self.get(id).state
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ref_frame_state::{RefFrameKind, RefFrameRot, RefFrameTrans};
use glam::DVec3;
fn build_tree() -> (FrameTree, FrameId, FrameId, FrameId) {
let mut tree = FrameTree::new();
let root = tree.add_root("root".into(), RefFrameKind::Inertial);
let a = tree.add_child(
root,
"A".into(),
RefFrameKind::Body,
RefFrameState {
trans: RefFrameTrans {
position: DVec3::new(1.0e6, 2.0e6, 3.0e6),
velocity: DVec3::new(10.0, 20.0, 30.0),
},
rot: RefFrameRot::default(),
},
);
let b = tree.add_child(
a,
"B".into(),
RefFrameKind::Body,
RefFrameState {
trans: RefFrameTrans {
position: DVec3::new(100.0, 200.0, 300.0),
velocity: DVec3::new(1.0, 2.0, 3.0),
},
rot: RefFrameRot::default(),
},
);
(tree, root, a, b)
}
#[test]
fn shared_compute_relative_state_matches_inherent() {
let (tree, root, _a, b) = build_tree();
let inherent = tree.compute_relative_state(root, b);
let shared = compute_relative_state(&tree, root, b);
assert_eq!(inherent.trans.position, shared.trans.position);
assert_eq!(inherent.trans.velocity, shared.trans.velocity);
assert_eq!(inherent.rot.t_parent_this, shared.rot.t_parent_this);
assert_eq!(inherent.rot.ang_vel_this, shared.rot.ang_vel_this);
}
#[test]
fn shared_common_ancestor_matches_inherent() {
let (tree, root, a, b) = build_tree();
let mut tree = tree;
let _c = tree.add_child(
root,
"C".into(),
RefFrameKind::Body,
RefFrameState::default(),
);
assert_eq!(common_ancestor(&tree, b, a), Some(a));
assert_eq!(common_ancestor(&tree, b, root), Some(root));
assert_eq!(common_ancestor(&tree, a, b), Some(a));
}
}