//! When the resting-pose DWELL FALLBACK actually runs, does it pick a visible pose? //! //! `ui-resting-pose.md` argues the fallback is structurally unsound — the gap it //! maximises is time spent *interpolating*, so neither endpoint is held. Its one //! worked example, `GP_TITLE` build 7's `ptlogo_eff3.t32`, **no longer //! discriminates**: under the corrected keyframe-record layout the longest gap //! moved from `61→103` to `0→46`, and both ends of that are `a = 0`. The page's //! listing still shows the stale parser's trailing `-`. //! //! Losing the example is not the same as closing the question, so: disc-wide, how //! often does the fallback fire, and when it does, does it return something the //! player would see? An element resting at `a = 0` is harmless whichever end the //! rule lands on. //! //! cargo run -p sylpheed-formats --example rest_fallback_census use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let mut paks: Vec = std::fs::read_dir(root.join("dat")) .expect("dat/") .filter_map(|e| e.ok().map(|e| e.path())) .filter(|p| p.extension().is_some_and(|x| x == "pak")) .collect(); paks.sort(); let (mut elements, mut plateau, mut fallback, mut fb_visible) = (0usize, 0, 0, 0); let mut worst: Vec<(u32, String, String)> = Vec::new(); let mut per_pak: std::collections::BTreeMap = Default::default(); for pak in &paks { let Ok(ar) = PakArchive::open(pak) else { continue }; let name = pak.file_name().unwrap().to_string_lossy().to_string(); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; let Some(b) = ui_layout::parse_build(&by) else { continue }; for el in &b.elements { if el.keyframes.len() < 2 { continue } elements += 1; // a plateau is two ADJACENT poses that are equal — the same test // the plateau path makes before the fallback can run let has_plateau = el.keyframes.windows(2).any(|w| { w[0].x == w[1].x && w[0].y == w[1].y && w[0].scale_x == w[1].scale_x && w[0].scale_y == w[1].scale_y && w[0].fade == w[1].fade }); if has_plateau { plateau += 1; continue } fallback += 1; per_pak.entry(name.clone()).or_default().0 += 1; let Some(r) = el.rest() else { continue }; let a = (r.fade >> 24) & 0xff; if a > 0 { fb_visible += 1; per_pak.entry(name.clone()).or_default().1 += 1; worst.push((a, name.clone(), format!("e{i}/{}", el.name))); } } } } println!("POPULATION: {elements} elements with >= 2 keyframes, over {} archives", paks.len()); println!("COVERAGE: {plateau} have a plateau (fallback never runs)"); println!(" {fallback} have NONE -> the dwell fallback decides"); println!(" {fb_visible} of those rest at alpha > 0 -- i.e. VISIBLE\n"); println!("PER ARCHIVE — fallback fires / of those, rests VISIBLE:"); let mut rows: Vec<_> = per_pak.into_iter().collect(); rows.sort_by(|a, b| b.1.1.cmp(&a.1.1)); for (pak, (fires, vis)) in &rows { println!(" {pak:34} {fires:5} fires {vis:5} visible"); } println!(); worst.sort_by(|a, b| b.0.cmp(&a.0)); for (a, pak, el) in worst.iter().take(6) { println!(" a={a:3} {pak} {el}"); } println!("\n--- END OF CENSUS (if this line is missing, the run did not finish) ---"); }