File size: 2,998 Bytes
5e84645
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! 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<Vec<Pt>>,
}

#[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<Vec<[i64; 2]>>,
}

#[derive(Debug, Deserialize)]
pub struct Golden {
    pub e_data: f64,
    pub coverage_checksums: HashMap<String, Checksum>,
    pub coverage_samples: Vec<CoverageSample>,
    pub vertex_gradients: Vec<GradientSample>,
}

#[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<String, [f64; 3]>,
    pub edges: Vec<Edge>,
    pub faces: Vec<Face>,
    pub target_u8: Vec<u8>,
    pub golden: Golden,
}

impl Vectors {
    pub fn edge_by_id(&self) -> HashMap<i64, &Edge> {
        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<f64> {
        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<i64, [f64; 3]> {
        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()
    }
}