Finishes #16 in the three places its earlier remedies missed. `tests/`: the last four local `disc_root()` copies now use `tests/common`, and with them goes the one real hardcoded fallback — `ui_keyframe_record_disc.rs` fell back to an absolute path on one machine, which made `unset SYLPHEED_DISC` a no-op there. Control: with the corpus absent that suite now finishes in 0.00s instead of 57.55s, so it skips rather than finding a disc of its own. `examples/`: seventeen examples defaulted to `/disc`, the mount point inside the CI container. Redundant there — `docker/ci/run` sets `SYLPHEED_DISC=/disc` — and wrong everywhere else, where a missing corpus turned into a file-not-found against a path that has never existed on the host. They now name the variable to set, like the other hundred examples already did. `docker/ci/run`: mount `$SYLPHEED_RES3D` and `$SYLPHEED_ISO` alongside the disc. Only the disc was mounted, so an in-container run sat out the res3d and iso suites while looking like a full one — the defect this issue is about, in the runner itself. Measured in the container on this desktop with all three corpora present: 45 suites / 377 passed / 0 failed / 14 ignored, and `sylpheed-corpus-report.txt` now reports PRESENT for all three rather than for the disc alone. Refs #16. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
97 lines
3.8 KiB
Rust
97 lines
3.8 KiB
Rust
//! Which keyless primitives have their paint position FORCED by occlusion?
|
|
//!
|
|
//! An opaque full-screen quad must sort below every element visible at any
|
|
//! instant it is opaque. Where that set is *every* other element, its position is
|
|
//! forced to first — derived from the file, not analogised from a neighbour.
|
|
//!
|
|
//! Controls, both measured in the running game and both reproduced here:
|
|
//! * `palogo_eff0.prm` is measured painting FIRST — and comes out forced first.
|
|
//! * `pteff00.prm` is measured painting LAST — and is forced below only a
|
|
//! handful, so the constraint permits it on top.
|
|
//!
|
|
//! ⚠️ Assumes straight alpha-over blending. Blend mode is ❔ in
|
|
//! `ui-prm-primitives.md`; an additive quad at alpha 255 would not occlude.
|
|
use sylpheed_formats::{pak, ratc, ui_layout};
|
|
|
|
fn main() {
|
|
let root =
|
|
std::env::var("SYLPHEED_DISC").expect("set SYLPHEED_DISC to the extracted disc root");
|
|
let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat"))
|
|
.expect("dat/")
|
|
.flatten()
|
|
.map(|e| e.path())
|
|
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak"))
|
|
.collect();
|
|
paks.sort();
|
|
let (mut forced, mut partial, mut free) = (0usize, 0usize, 0usize);
|
|
let mut names: std::collections::BTreeMap<String, (usize, usize)> = Default::default();
|
|
for p in &paks {
|
|
let Ok(ar) = pak::PakArchive::open(p) else {
|
|
continue;
|
|
};
|
|
for e in ar.entries() {
|
|
let Ok(by) = ar.read(e) else { continue };
|
|
if !ratc::is_ratc(&by) {
|
|
continue;
|
|
}
|
|
let Some(b) = ui_layout::parse_build(&by) else {
|
|
continue;
|
|
};
|
|
let tmax = b
|
|
.elements
|
|
.iter()
|
|
.flat_map(|el| el.keyframes.iter().filter_map(|k| k.time))
|
|
.max()
|
|
.unwrap_or(0);
|
|
if tmax == 0 {
|
|
continue;
|
|
}
|
|
for el in &b.elements {
|
|
if ui_layout::sprite_layer_key(&b, &by, el).is_some() {
|
|
continue;
|
|
}
|
|
// full-screen only: a quad that does not cover cannot occlude
|
|
if el.pivot_x * 2 < 1280 || el.pivot_y * 2 < 720 {
|
|
continue;
|
|
}
|
|
let op: Vec<u32> = (0..=tmax)
|
|
.filter(|&t| el.pose_at(t).map(|k| k.fade >> 24) == Some(255))
|
|
.collect();
|
|
if op.is_empty() {
|
|
continue;
|
|
}
|
|
let others: Vec<&ui_layout::Element> =
|
|
b.elements.iter().filter(|o| o.index != el.index).collect();
|
|
if others.is_empty() {
|
|
continue;
|
|
}
|
|
let below = others
|
|
.iter()
|
|
.filter(|o| {
|
|
op.iter()
|
|
.any(|&t| o.pose_at(t).map(|k| k.fade >> 24).unwrap_or(0) > 0)
|
|
})
|
|
.count();
|
|
let ent = names.entry(el.name.clone()).or_default();
|
|
ent.1 += 1;
|
|
if below == others.len() {
|
|
forced += 1;
|
|
ent.0 += 1
|
|
} else if below > 0 {
|
|
partial += 1
|
|
} else {
|
|
free += 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
println!("keyless FULL-SCREEN primitives with an opaque interval:");
|
|
println!(" position FORCED FIRST (below every other element) : {forced}");
|
|
println!(" forced below SOME but not all : {partial}");
|
|
println!(" occludes nothing : {free}");
|
|
println!("\nby name — instances forced first / total:");
|
|
for (n, (f, t)) in &names {
|
|
println!(" {n:>24} {f:>4} / {t}");
|
|
}
|
|
}
|