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>
121 lines
4.3 KiB
Rust
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()
|
|
);
|
|
}
|
|
}
|
|
}
|