This branch predates CI on `main`. `cargo fmt --all` only; no behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
93 lines
4.1 KiB
Rust
93 lines
4.1 KiB
Rust
//! 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.map_or(false, |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 ---");
|
|
}
|