//! Should the settled pose come from each element's `rest()`, or from the //! **screen's** settle instant? //! //! Three iterations have measured how badly `rest()`'s dwell fallback behaves — //! 2 305 elements where it decides, 1 457 of them handed the element's *maximum* //! alpha, and by construction none of those poses is held. What has been missing //! is a proposal. //! //! `UiBuild::settle_time()` already exists: the midpoint of the longest //! keyframe-free interval **across the whole build**. That is the port agent's //! "re-key on the screen's span rather than the element's", and its shipped path //! poses `pose_at(hold)` and agrees with every capture it holds at 0.01 %. //! //! ⚠️ **Control first.** On elements where `rest()` is already sound — the plateau //! path, a pose the element genuinely holds — `pose_at(settle)` must AGREE. If it //! disagrees there, it is not a better rule, it is a different one. //! //! ⚠️ `ui-settle-time.md` records that 42 % of bundles have a settle window under //! 10 units and never settle at all. Bundles are split on that here rather than //! averaged over. //! //! cargo run -p sylpheed-formats --example rest_vs_settle 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(); // control population (plateau) and test population (fallback), each split by // whether the bundle settles at all let (mut ctl_n, mut ctl_cov, mut ctl_agree) = (0usize, 0usize, 0usize); let (mut fb_n, mut fb_rest_vis, mut fb_settle_vis) = (0usize, 0usize, 0usize); let mut narrow = 0usize; for pak in &paks { let Ok(ar) = PakArchive::open(pak) else { continue }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; let Some(b) = ui_layout::parse_build(&by) else { continue }; let Some((lo, hi)) = b.settle_window() else { continue }; if hi - lo < 10 { narrow += 1; continue } // this bundle never settles let st = lo + (hi - lo) / 2; for el in &b.elements { if el.keyframes.len() < 2 { continue } let 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); let (Some(r), Some(s)) = (el.rest(), el.pose_at(st)) else { continue }; let (ra, sa) = ((r.fade >> 24) & 0xff, (s.fade >> 24) & 0xff); if plateau { // ⚠️ The first version of this control compared EVERY plateau // element and got 46.6 % agreement — then I asked what that // means physically. `rest()` finds *a* held pose; many // elements hold one during the build-in and then move on. // `pose_at(settle)` asks what is on screen WHEN THE SCREEN HAS // SETTLED. Those are different questions, so disagreement // proves nothing. The fair control is the subset where the // held interval actually CONTAINS the settle instant. ctl_n += 1; let covers = el.keyframes.windows(2).any(|w| { let held = 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; match (w[0].time, w[1].time) { (Some(a), Some(bb)) => held && a <= st && st <= bb, _ => false, } }); if covers { ctl_cov += 1; if ra == sa && r.x == s.x && r.y == s.y && r.scale_x == s.scale_x && r.scale_y == s.scale_y { ctl_agree += 1 } } } else { fb_n += 1; if ra > 0 { fb_rest_vis += 1 } if sa > 0 { fb_settle_vis += 1 } } } } } println!("bundles skipped as never-settling (window < 10 units): {narrow}\n"); println!("CONTROL — elements where rest() takes the SOUND plateau path:"); println!(" {ctl_n} plateau elements in settling bundles"); println!(" {ctl_cov} of them HOLD ACROSS the settle instant — the fair control"); println!(" pose_at(settle) agrees with rest() on {ctl_agree} of those ({:.1} %)", 100.0 * ctl_agree as f64 / ctl_cov.max(1) as f64); println!("\nTEST — elements where the unsound dwell fallback decides:"); println!(" {fb_n} elements"); println!(" rest() returns a VISIBLE pose on {fb_rest_vis} ({:.1} %)", 100.0 * fb_rest_vis as f64 / fb_n.max(1) as f64); println!(" pose_at(settle) returns a VISIBLE pose on {fb_settle_vis} ({:.1} %)", 100.0 * fb_settle_vis as f64 / fb_n.max(1) as f64); println!("\n--- END (if this line is missing, the run did not finish) ---"); }