//! The FULL extent of the two title sweep leaves, across their whole cycle. //! //! `ptloop_leaf_sweep_at.rs` samples t=340..540 — a window chosen to compare two //! competing fits — so it never showed how far the leaves travel. That gap let a //! claim stand that `ptloop01/02` "do not free-run", measured over the PARENT's //! 200x90 rect, which is a pivot anchor the leaf spends almost no time inside. //! `sylpheed-port` reports x tracks of -639..1521 and -839..1721 from their //! export; this checks that against the disc. //! //! cargo run -p sylpheed-formats --example ptloop_leaf_extent use std::path::PathBuf; use sylpheed_formats::{pak::PakArchive, ui_layout}; fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE.pak"); for entry in [4usize, 5, 7] { let Ok(by) = ar.read(&ar.entries()[entry]) else { continue; }; let Some(b) = ui_layout::parse_build(&by) else { continue; }; println!("\n######## GP_TITLE entry {entry} ########"); for parent in ["ptloop01", "ptloop02"] { let Some(el) = b.elements.iter().find(|e| e.name.starts_with(parent)) else { println!(" {parent}: not present in this build"); continue; }; let Some(&(off, size)) = b.records.get(&el.name) else { println!(" {}: no nested record", el.name); continue; }; let span = u32::from_be_bytes(by[off + 8..off + 12].try_into().unwrap()); let Some(lb) = ui_layout::parse_build(&by[off..off + size]) else { println!(" {}: leaf will not parse", el.name); continue; }; println!( " {} parent rest ({},{}) nested cycle span {span}", el.name, el.keyframes.last().map(|k| k.x).unwrap_or(0), el.keyframes.last().map(|k| k.y).unwrap_or(0) ); for le in &lb.elements { let (mut lo, mut hi) = (i64::MAX, i64::MIN); let (mut sxs, mut sys) = (Vec::new(), Vec::new()); for t in 0..=span { if let Some(k) = le.pose_at(t) { lo = lo.min(k.x as i64); hi = hi.max(k.x as i64); if !sxs.contains(&k.scale_x) { sxs.push(k.scale_x) } if !sys.contains(&k.scale_y) { sys.push(k.scale_y) } } } // A CYCLE LENGTH IS NOT A MOTION DURATION. Find the last t at // which x still changes: sylpheed-port reports the final segment // HOLDS, which would make px/unit larger than cycle-based maths. let mut last_move = 0u32; let mut prev = None; for t in 0..=span { if let Some(k) = le.pose_at(t) { if prev.is_some_and(|p| p != k.x) { last_move = t } prev = Some(k.x); } } let w = (le.pivot_x * 2) as i64; println!( " leaf {:<12} pivot {}x{} quad w={w} x track {lo} .. {hi} \ (centre {} .. {}) scale_x {:?} scale_y {:?}", le.name, le.pivot_x, le.pivot_y, lo + le.pivot_x as i64, hi + le.pivot_x as i64, sxs, sys ); println!(" motion ends at t={last_move} of a {span}-unit cycle -> {:.3} px/unit over the MOVING span (vs {:.3} over the cycle)", (hi - lo) as f64 / last_move.max(1) as f64, (hi - lo) as f64 / span as f64); } } } println!("--- END ---"); }