`cargo fmt --all -- --check` has failed on every run in this repository's history, identically on `main` and on every branch. This is #12. Mechanical: `cargo fmt --all`, nothing else. 154 files, all `.rs`, no other extension touched. `cargo check --workspace` exits 0 afterwards, so nothing changed semantically. ON THE ORDERING, WHICH WAS THE REAL QUESTION. HANDOFF-2026-09-06 section 7 warns this is the expensive fix: a whole-tree reformat before #7 and #8 return "would put a conflict in every file of 861 commits and make the reviews those items exist to enable unreadable". That is measurably too pessimistic, and it had been reasoned rather than tested. Measured here by three-way merging a rustfmt'd `main` against both unmerged branches, file by file: file/branch pairs tested 32 merges CLEAN 28 merges CONFLICTING 4 (8 conflict hunks total) sylpheed-cli/src/main.rs 1 hunk sylpheed-export/src/check.rs 1 sylpheed-export/src/screen.rs 4 sylpheed-export/src/video.rs 2 All four are against `auto/frame-blend-draw-path` only; `auto/port-p6-audio` does not conflict anywhere. The earlier framing -- 154 dirty files, 133 that cannot collide, 21 that can, the collision set carrying 147 of 774 hunks (19%) -- reproduces exactly. What it did not say is that most of the 21 still merge cleanly, because rustfmt's edits and the branches' edits rarely land on the same lines. So the cost of sweeping now is 4 files and 8 hunks for one branch, against a check that is otherwise red forever. Deliberately NOT folded into the WASM PR: 154 reformatted files would make that one unreviewable. Closes #12
211 lines
7.3 KiB
Rust
211 lines
7.3 KiB
Rust
//! Verify static ship assembly against multi-snapshot, multi-instance F10
|
||
//! captures: cluster every part's ship-relative transform across ALL ships in
|
||
//! ALL snapshots, and compare the dominant clusters with `assemble_ship`.
|
||
//!
|
||
//! cargo run --release --example capture_verify -- <stage.xpr> <ship_id> <log> [log…]
|
||
//!
|
||
//! Every draw is position-validated against the ship's parts (any LOD, set
|
||
//! based). Each `bdy_04`(-LOD) draw acts as a ship reference; every validated
|
||
//! part draw within ship range contributes `rel = WV_ref⁻¹ ∘ WV_part`. The true
|
||
//! per-part placements appear as tight clusters repeated once PER SHIP per
|
||
//! snapshot; cross-ship pairings scatter and stay below the cluster threshold.
|
||
use std::collections::HashSet;
|
||
use sylpheed_formats::mesh::{xbg7_resource_names, Xbg7Model};
|
||
use sylpheed_formats::ship::{assemble_ship, is_base_part, ship_id_of};
|
||
use sylpheed_formats::ship_capture::{parse_capture, CapturedDraw};
|
||
|
||
type M3 = [[f64; 3]; 3];
|
||
|
||
fn transpose(m: &M3) -> M3 {
|
||
[
|
||
[m[0][0], m[1][0], m[2][0]],
|
||
[m[0][1], m[1][1], m[2][1]],
|
||
[m[0][2], m[1][2], m[2][2]],
|
||
]
|
||
}
|
||
fn mmul(a: &M3, b: &M3) -> M3 {
|
||
let mut o = [[0.0; 3]; 3];
|
||
for i in 0..3 {
|
||
for j in 0..3 {
|
||
o[i][j] = (0..3).map(|k| a[i][k] * b[k][j]).sum();
|
||
}
|
||
}
|
||
o
|
||
}
|
||
fn mv(m: &M3, v: [f64; 3]) -> [f64; 3] {
|
||
[
|
||
m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
|
||
m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
|
||
m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2],
|
||
]
|
||
}
|
||
|
||
fn main() {
|
||
let a: Vec<String> = std::env::args().collect();
|
||
let bytes = std::fs::read(&a[1]).unwrap();
|
||
let id = &a[2];
|
||
|
||
// Part position sets (base + LODs share the local frame) + per-variant vcounts.
|
||
let names = xbg7_resource_names(&bytes);
|
||
let base_parts: Vec<String> = names
|
||
.iter()
|
||
.filter(|n| is_base_part(n) && ship_id_of(n) == Some(id.as_str()))
|
||
.cloned()
|
||
.collect();
|
||
let mut want: HashSet<String> = base_parts.iter().cloned().collect();
|
||
for p in &base_parts {
|
||
for suf in ["_m", "_l", "_d"] {
|
||
let c = format!("{p}{suf}");
|
||
if names.contains(&c) {
|
||
want.insert(c);
|
||
}
|
||
}
|
||
}
|
||
let models = Xbg7Model::models_named(&bytes, &want, &|| false);
|
||
struct Part {
|
||
base: String,
|
||
vcounts: Vec<u32>,
|
||
pos: Vec<[f32; 3]>, // union of variants
|
||
}
|
||
let mut parts: Vec<Part> = Vec::new();
|
||
for p in &base_parts {
|
||
let mut vcounts = Vec::new();
|
||
let mut pos = Vec::new();
|
||
for cand in [
|
||
p.clone(),
|
||
format!("{p}_m"),
|
||
format!("{p}_l"),
|
||
format!("{p}_d"),
|
||
] {
|
||
if let Some(m) = models.iter().find(|m| m.name == cand) {
|
||
let mp: Vec<[f32; 3]> = m
|
||
.meshes
|
||
.iter()
|
||
.flat_map(|s| s.positions.iter().copied())
|
||
.collect();
|
||
vcounts.push(mp.len() as u32);
|
||
pos.extend(mp);
|
||
}
|
||
}
|
||
parts.push(Part {
|
||
base: p.clone(),
|
||
vcounts,
|
||
pos,
|
||
});
|
||
}
|
||
|
||
// Validate a draw against a part (direct or X-mirrored), like ship_capture.
|
||
let validate = |d: &CapturedDraw, p: &Part| -> Option<bool> {
|
||
if !p.vcounts.contains(&d.vcount) || d.pos.is_empty() {
|
||
return None;
|
||
}
|
||
let near = |a: &[f32; 3], b: &[f32; 3]| {
|
||
(a[0] - b[0]).abs() <= 1e-2
|
||
&& (a[1] - b[1]).abs() <= 1e-2
|
||
&& (a[2] - b[2]).abs() <= 1e-2
|
||
};
|
||
let (mut direct, mut mirror) = (0usize, 0usize);
|
||
for q in &d.pos {
|
||
if p.pos.iter().any(|v| near(q, v)) {
|
||
direct += 1;
|
||
}
|
||
let qm = [-q[0], q[1], q[2]];
|
||
if p.pos.iter().any(|v| near(&qm, v)) {
|
||
mirror += 1;
|
||
}
|
||
}
|
||
let need = d.pos.len().div_ceil(2);
|
||
if direct >= need && direct >= mirror {
|
||
Some(false)
|
||
} else if mirror >= need {
|
||
Some(true)
|
||
} else {
|
||
None
|
||
}
|
||
};
|
||
|
||
// Collect rel samples per part across all logs.
|
||
let mut samples: std::collections::BTreeMap<String, Vec<[f64; 3]>> = Default::default();
|
||
for log in &a[3..] {
|
||
let text = std::fs::read_to_string(log).unwrap();
|
||
let draws = parse_capture(&text);
|
||
// label validated draws
|
||
let mut labeled: Vec<(usize, usize, bool)> = Vec::new(); // (draw, part, mirrored)
|
||
for (di, d) in draws.iter().enumerate() {
|
||
for (pi, p) in parts.iter().enumerate() {
|
||
if let Some(mir) = validate(d, p) {
|
||
labeled.push((di, pi, mir));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
let refs: Vec<usize> = labeled
|
||
.iter()
|
||
.filter(|(_, pi, _)| parts[*pi].base.ends_with("bdy_04"))
|
||
.map(|(di, ..)| *di)
|
||
.collect();
|
||
for &ri in &refs {
|
||
let rf = &draws[ri];
|
||
let rt = transpose(&rf.r);
|
||
for &(di, pi, _mir) in &labeled {
|
||
let d = &draws[di];
|
||
let dt = [d.t[0] - rf.t[0], d.t[1] - rf.t[1], d.t[2] - rf.t[2]];
|
||
let rel = mv(&rt, dt);
|
||
if rel.iter().map(|v| v * v).sum::<f64>().sqrt() < 2600.0 {
|
||
// also require the rel rotation be finite-sane
|
||
let _rm = mmul(&rt, &d.r);
|
||
samples.entry(parts[pi].base.clone()).or_default().push(rel);
|
||
}
|
||
}
|
||
}
|
||
eprintln!(
|
||
"{log}: {} validated draws, {} bdy_04 refs",
|
||
labeled.len(),
|
||
refs.len()
|
||
);
|
||
}
|
||
|
||
// Cluster per part (greedy, 8-unit radius), print clusters with ≥3 samples.
|
||
println!("\n== dominant ship-relative placements (clusters ≥3 samples) ==");
|
||
for (part, mut v) in samples {
|
||
v.sort_by(|x, y| x[0].partial_cmp(&y[0]).unwrap());
|
||
let mut clusters: Vec<([f64; 3], usize)> = Vec::new();
|
||
for s in &v {
|
||
match clusters.iter_mut().find(|(c, _)| {
|
||
(c[0] - s[0]).abs() < 8.0 && (c[1] - s[1]).abs() < 8.0 && (c[2] - s[2]).abs() < 8.0
|
||
}) {
|
||
Some((c, n)) => {
|
||
for k in 0..3 {
|
||
c[k] = (c[k] * *n as f64 + s[k]) / (*n as f64 + 1.0);
|
||
}
|
||
*n += 1;
|
||
}
|
||
None => clusters.push((*s, 1)),
|
||
}
|
||
}
|
||
clusters.sort_by(|x, y| y.1.cmp(&x.1));
|
||
let tops: Vec<String> = clusters
|
||
.iter()
|
||
.filter(|(_, n)| *n >= 3)
|
||
.take(6)
|
||
.map(|(c, n)| format!("[{:7.1} {:7.1} {:7.1}]×{n}", c[0], c[1], c[2]))
|
||
.collect();
|
||
println!(" {part:20} {}", tops.join(" "));
|
||
}
|
||
|
||
// Static assembly rel bdy_04 for comparison.
|
||
println!("\n== static assemble_ship rel bdy_04 ==");
|
||
let placed = assemble_ship(&bytes, id, true);
|
||
if let Some(r4) = placed.iter().find(|p| p.resource == format!("{id}_bdy_04")) {
|
||
for p in &placed {
|
||
println!(
|
||
" {:20} [{:7.1} {:7.1} {:7.1}]",
|
||
p.resource,
|
||
p.t[0] - r4.t[0],
|
||
p.t[1] - r4.t[1],
|
||
p.t[2] - r4.t[2]
|
||
);
|
||
}
|
||
}
|
||
}
|