1use crate::hebbian::HebbianState;
7use crate::mirror::MirrorState;
8use crate::reader::MicroscopeReader;
9use crate::thought_graph::ThoughtGraphState;
10use std::io::Write;
11use std::path::Path;
12
13#[repr(C, packed)]
15#[derive(Clone, Copy)]
16pub struct VizBlock {
17 pub x: f32,
18 pub y: f32,
19 pub z: f32,
20 pub dx: f32,
21 pub dy: f32,
22 pub dz: f32,
23 pub energy: f32,
24 pub layer_id: u8,
25 pub depth: u8,
26 pub flags: u8,
27 pub padding: u8,
28}
29
30pub fn export_binary_snapshot(
32 reader: &MicroscopeReader,
33 hebb: &HebbianState,
34 mirror: &MirrorState,
35 thought_graph: &ThoughtGraphState,
36) -> Vec<u8> {
37 let block_count = reader.block_count;
38 let mut buf = Vec::with_capacity(12 + block_count * 32 + 200 * 12 + 5000);
39
40 buf.extend_from_slice(b"VIZ1");
42 buf.extend_from_slice(&(block_count as u32).to_le_bytes());
43
44 for i in 0..block_count {
46 let h = reader.header(i);
47 let energy = hebb.energy(i);
48 let (dx, dy, dz) = if i < hebb.activations.len() {
49 let rec = &hebb.activations[i];
50 (rec.drift_x, rec.drift_y, rec.drift_z)
51 } else {
52 (0.0, 0.0, 0.0)
53 };
54
55 let mirror_boost = mirror.boost_for(i as u32);
56 let flags = if mirror_boost > 0.5 { 1 } else { 0 };
57
58 let vb = VizBlock {
59 x: h.x,
60 y: h.y,
61 z: h.z,
62 dx,
63 dy,
64 dz,
65 energy,
66 layer_id: h.layer_id,
67 depth: h.depth,
68 flags,
69 padding: 0,
70 };
71
72 let bytes: [u8; 32] = unsafe { std::mem::transmute(vb) };
73 buf.extend_from_slice(&bytes);
74 }
75
76 let recent_nodes = if thought_graph.nodes.len() > 100 {
78 &thought_graph.nodes[thought_graph.nodes.len() - 100..]
79 } else {
80 &thought_graph.nodes
81 };
82 buf.extend_from_slice(&(recent_nodes.len() as u32).to_le_bytes());
83 for n in recent_nodes {
84 buf.extend_from_slice(&n.query_hash.to_le_bytes());
85 buf.extend_from_slice(&n.timestamp_ms.to_le_bytes());
86 buf.extend_from_slice(&(n.result_count as u32).to_le_bytes());
87 buf.push(n.dominant_layer);
88 buf.push(n.centroid_hash);
89 buf.extend_from_slice(&[0u8; 2]); }
91
92 let top_patterns = thought_graph.top_patterns(20);
94 buf.extend_from_slice(&(top_patterns.len() as u32).to_le_bytes());
95 for p in top_patterns {
96 buf.extend_from_slice(&p.id.to_le_bytes());
97 buf.extend_from_slice(&p.frequency.to_le_bytes());
98 buf.extend_from_slice(&p.strength.to_le_bytes());
99 buf.extend_from_slice(&(p.sequence.len() as u8).to_le_bytes());
100 for &h in p.sequence.iter().take(5) {
101 buf.extend_from_slice(&h.to_le_bytes());
102 }
103 for _ in p.sequence.len()..5 {
104 buf.extend_from_slice(&0u64.to_le_bytes());
105 }
106 }
107
108 let mut pairs: Vec<_> = hebb.coactivations.values().collect();
110 pairs.sort_by(|a, b| b.count.cmp(&a.count));
111 pairs.truncate(200);
112
113 buf.extend_from_slice(&(pairs.len() as u32).to_le_bytes());
114 for p in pairs {
115 buf.extend_from_slice(&p.block_a.to_le_bytes());
116 buf.extend_from_slice(&p.block_b.to_le_bytes());
117 buf.extend_from_slice(&p.count.to_le_bytes());
118 }
119
120 buf
121}
122
123pub fn export_to_file(
124 _output_dir: &Path,
125 reader: &MicroscopeReader,
126 hebb: &HebbianState,
127 mirror: &MirrorState,
128 thought_graph: &ThoughtGraphState,
129 dest: &Path,
130) -> Result<(), String> {
131 let buf = export_binary_snapshot(reader, hebb, mirror, thought_graph);
132 let mut file = std::fs::File::create(dest).map_err(|e| format!("create viz file: {}", e))?;
133 file.write_all(&buf)
134 .map_err(|e| format!("write viz file: {}", e))
135}
136pub fn export_density_map(
137 _hebb: &HebbianState,
138 _headers: &[(f32, f32, f32)],
139 _grid: u16,
140) -> Vec<u8> {
141 vec![]
142}
143pub fn layer_heatmap(
144 _hebb: &HebbianState,
145 _reader: &MicroscopeReader,
146) -> std::collections::HashMap<String, f32> {
147 std::collections::HashMap::new()
148}