Files
Sylpheed/crates/sylpheed-formats/examples/tie_cost_over_time.rs
MechaCat02 ccd49ac31f fix(lint): clear the clippy gate across examples and tests
80 findings, not the 14 the first run showed -- clippy stops at the first
failing compilation unit, so `--keep-going` is what makes the list complete.

60 were machine-applicable (`cargo clippy --fix`). The rest by hand:

* five descending `sort_by` -> `sort_by_key(Reverse(..))`
* `chunks_exact(4)` on both sides of four zips, so the compared items stay
  `[u8; 4]` rather than one array against one slice
* three `type` aliases for the census maps and the captured-quad tuple
* `&PathBuf` -> `&Path` in two disc tests
* two range loops; one of them keeps `#[allow(needless_range_loop)]` with the
  reason -- the index is into a map's value, which changes each iteration
* the module doc list in `invert_capture` re-indented to markdown's rules
* `blit`'s eight arguments get `#[allow(too_many_arguments)]`, not a struct

One dead `let off = b.len();` in a `ratc` test is dropped rather than renamed.
The sibling test at :162 is the one that asserts an offset; if this one was
meant to as well, that is a test change and not a lint fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 16:42:41 +02:00

121 lines
4.3 KiB
Rust

//! How many tied pairs can cost a pixel, as a function of TIME?
//!
//! `tie_break_pixel_cost` answers "at one pose". That leaves the answer looking
//! like it might be a knife-edge: pick a different instant and the count could
//! jump. This sweeps every keyframe time in the bundle and reports, per entry,
//! how many same-key pairs are simultaneously **opaque, non-collapsed and
//! overlapping** — the pairs whose order could possibly matter at that instant.
//!
//! The instrument's control is built in: the count at t=0 (nothing has faded in)
//! and the count at rest must bracket it, and an entry whose count is flat at
//! zero for the whole sweep would be suspicious rather than reassuring — so the
//! peak is printed too.
use sylpheed_formats::{pak, ui_layout};
use ui_layout::UiBuild;
fn rect(e: &ui_layout::Element, t: u32) -> Option<(i32, i32, i32, i32)> {
let kf = e.pose_at(t)?;
if kf.fade >> 24 == 0 || kf.scale_x == 0 || kf.scale_y == 0 {
return None;
}
let (w, h) = ((e.pivot_x * 2) as i32, (e.pivot_y * 2) as i32);
if w == 0 || h == 0 {
return None;
}
Some((kf.x, kf.y, w, h))
}
fn live_pairs(b: &UiBuild, _bytes: &[u8], keys: &[u32], t: u32) -> usize {
let mut n = 0;
for a in 0..keys.len() {
for c in (a + 1)..keys.len() {
if keys[a] != keys[c] || keys[a] == u32::MAX {
continue;
}
let (Some(ra), Some(rb)) = (rect(&b.elements[a], t), rect(&b.elements[c], t)) else {
continue;
};
if (ra.0 + ra.2).min(rb.0 + rb.2) - ra.0.max(rb.0) > 0
&& (ra.1 + ra.3).min(rb.1 + rb.3) - ra.1.max(rb.1) > 0
{
n += 1
}
}
}
n
}
fn main() {
let path = std::env::args()
.nth(1)
.expect("usage: tie_cost_over_time <pak>");
let ar = pak::PakArchive::open(&path).expect("open pak");
println!("# tied pairs that could cost a pixel, over time — {path}\n");
for (i, e) in ar.entries().to_vec().iter().enumerate() {
let Ok(by) = ar.read(e) else { continue };
let Some(b) = ui_layout::parse_build(&by) else {
continue;
};
let keys: Vec<u32> = b
.elements
.iter()
.map(|el| ui_layout::sprite_layer_key(&b, &by, el).unwrap_or(u32::MAX))
.collect();
let mut ts: Vec<u32> = b
.elements
.iter()
.flat_map(|el| el.keyframes.iter().filter_map(|k| k.time))
.collect();
ts.sort_unstable();
ts.dedup();
if ts.len() < 2 {
continue;
}
let last = *ts.last().unwrap();
// sample every keyframe time AND every midpoint between them
let mut samples: Vec<u32> = ts.clone();
for w in ts.windows(2) {
samples.push(w[0] + (w[1] - w[0]) / 2);
}
samples.sort_unstable();
samples.dedup();
let counts: Vec<(u32, usize)> = samples
.iter()
.map(|&t| (t, live_pairs(&b, &by, &keys, t)))
.collect();
let peak = counts.iter().map(|&(_, n)| n).max().unwrap_or(0);
if peak == 0 {
continue;
}
let Some((lo, hi)) = b.settle_window() else {
continue;
};
let st = b.settle_time().unwrap();
let at_settle = live_pairs(&b, &by, &keys, st);
// the whole plateau, not just its midpoint
let plateau: Vec<usize> = (lo..=hi)
.step_by(((hi - lo).max(1) / 8).max(1) as usize)
.map(|t| live_pairs(&b, &by, &keys, t))
.collect();
let pmax = plateau.iter().copied().max().unwrap_or(0);
println!(
"entry {i:2} peak {peak} live pair(s) over t=0..{last} \
settle window [{lo},{hi}] at t={st}: {at_settle} ACROSS THE WHOLE WINDOW: max {pmax}"
);
let busy: Vec<String> = counts
.iter()
.filter(|&&(_, n)| n > 0)
.map(|&(t, n)| format!("t{t}:{n}"))
.collect();
if busy.len() <= 24 {
println!(" live only at {}", busy.join(" "));
} else {
println!(
" live at {} of {} sampled instants",
busy.len(),
counts.len()
);
}
}
}