use crate::error::Result;
use crate::shared::{align_reconstructions, SharedPointOptions};
use crate::umeyama::{RobustUmeyamaOptions, RobustUmeyamaResult};
use cumulus3d_core::{Reconstruction, Sim3};
#[derive(Clone, Debug, Default)]
pub struct AnchorChain {
zone_to_latest: Vec<Sim3>,
linked: Vec<bool>,
}
impl AnchorChain {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.zone_to_latest.len()
}
pub fn is_empty(&self) -> bool {
self.zone_to_latest.is_empty()
}
pub fn push(&mut self, new_from_prev: Option<Sim3>) -> usize {
if let Some(t) = new_from_prev {
for s in &mut self.zone_to_latest {
*s = t.compose(s);
}
}
self.zone_to_latest.push(Sim3::identity());
self.linked.push(new_from_prev.is_some() && !self.linked.is_empty());
self.zone_to_latest.len() - 1
}
pub fn push_model(
&mut self,
prev: Option<&Reconstruction>,
new: &Reconstruction,
shared: &SharedPointOptions,
robust: &RobustUmeyamaOptions,
) -> Result<Option<RobustUmeyamaResult>> {
let Some(prev) = prev else {
self.push(None);
return Ok(None);
};
match align_reconstructions(prev, new, shared, robust) {
Ok((r, _)) => {
self.push(Some(r.sim3));
Ok(Some(r))
}
Err(e) => {
self.push(None);
Err(e)
}
}
}
pub fn zone_to_latest(&self, zone: usize) -> Option<Sim3> {
self.zone_to_latest.get(zone).copied()
}
pub fn is_linked(&self, zone: usize) -> bool {
self.linked.get(zone).copied().unwrap_or(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::umeyama::tests::{rand_vec, random_sim3};
use cumulus3d_core::Vec3;
#[test]
fn chain_maps_all_zones_to_latest() {
let mut rng = cumulus3d_core::ransac::make_rng(Some(31));
let zs: Vec<Sim3> = (0..4).map(|_| random_sim3(&mut rng)).collect();
let mut chain = AnchorChain::new();
chain.push(None);
for k in 1..zs.len() {
chain.push(Some(zs[k].compose(&zs[k - 1].inverse())));
}
let x = rand_vec(&mut rng, 10.0);
let last = zs.last().unwrap();
for (j, z) in zs.iter().enumerate() {
let in_zone = z.transform_point(&x);
let got = chain.zone_to_latest(j).unwrap().transform_point(&in_zone);
let want = last.transform_point(&x);
assert!((got - want).norm() / want.norm().max(1.0) < 1e-9, "zone {j}");
}
assert!(!chain.is_linked(0) && chain.is_linked(3));
let before = chain.zone_to_latest(1).unwrap();
chain.push(None);
assert_eq!(chain.zone_to_latest(1).unwrap(), before);
assert_eq!(chain.zone_to_latest(4).unwrap().transform_point(&Vec3::x()), Vec3::x());
}
}