//! Deserialization of the test-vector files written by `scripts/export_spike_vectors.py`. //! //! Field-for-field with the exporter. Nothing here computes; see `coverage`, `energy`, `gradient`. use serde::{Deserialize, Serialize}; use std::collections::HashMap; pub type Pt = [f64; 2]; #[derive(Debug, Deserialize)] pub struct Edge { pub id: i64, #[allow(dead_code)] pub node_a: i64, #[allow(dead_code)] pub node_b: i64, pub left_label: i64, pub right_label: i64, /// True when neither side is the exterior face — exactly `geometry_gradient`'s filter. pub interior: bool, /// One inner Vec per cubic, each holding `flatten_samples` fixed-t sampled points. pub cubics: Vec>, } #[derive(Debug, Deserialize)] pub struct Face { pub label: i64, /// Each loop is an ordered list of `[edge_id, direction]` darts (direction is +1 / -1). pub loops: Vec>, } #[derive(Debug, Deserialize)] pub struct Golden { pub e_data: f64, pub coverage_checksums: HashMap, pub coverage_samples: Vec, pub vertex_gradients: Vec, } #[derive(Debug, Deserialize, Serialize, Clone, Copy)] pub struct Checksum { pub sum: f64, pub sumsq: f64, } #[derive(Debug, Deserialize)] pub struct CoverageSample { pub label: i64, pub row: usize, pub col: usize, pub value: f64, } #[derive(Debug, Deserialize)] pub struct GradientSample { pub edge: i64, pub cubic: usize, pub vertex: usize, pub gx: f64, pub gy: f64, } #[derive(Debug, Deserialize)] pub struct Vectors { pub name: String, pub kind: String, pub seed: u64, pub width: usize, pub height: usize, pub background: f64, pub l0: f64, pub colors255: HashMap, pub edges: Vec, pub faces: Vec, pub target_u8: Vec, pub golden: Golden, } impl Vectors { pub fn edge_by_id(&self) -> HashMap { self.edges.iter().map(|e| (e.id, e)).collect() } /// `target = clip(u8 / 255, 0, 1)`, matching `refine_graph_analytic`. u8/255 is always in /// range, so the clip is a no-op here and is omitted rather than faked. pub fn target(&self) -> Vec { self.target_u8.iter().map(|&b| b as f64 / 255.0).collect() } /// Per-label RGB in [0,1]: `clip(colors/255, 0, 1)`, as both `render` and `geometry_gradient` /// do. The clip is real here — VarPro colors can land outside [0,255]. pub fn colors01(&self) -> HashMap { self.colors255 .iter() .map(|(k, v)| { let lb: i64 = k.parse().expect("label key must be an integer"); let c = [ (v[0] / 255.0).clamp(0.0, 1.0), (v[1] / 255.0).clamp(0.0, 1.0), (v[2] / 255.0).clamp(0.0, 1.0), ]; (lb, c) }) .collect() } }