//! 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 "); 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 = b .elements .iter() .map(|el| ui_layout::sprite_layer_key(&b, &by, el).unwrap_or(u32::MAX)) .collect(); let mut ts: Vec = 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 = 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 = (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 = 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() ); } } }