This branch predates CI on `main`. `cargo fmt --all` only; no behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
135 lines
6.0 KiB
Rust
135 lines
6.0 KiB
Rust
//! 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 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 mut paks: Vec<PathBuf> = 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) ---");
|
|
}
|