tawasuyu

tawasuyu/brahman

Rust 328TOML 124JSON 17Markdown 144636 Sutichaykuna

⎇ main crates/modules/pineal/heatmap/src/encoder.rs Rust · 57 siq'ikuna · 2.0 KB Ñawpa willay Pi imata llamkarqan ch'uya

feat(pineal): cierra stub heatmap — matrix + viridis + encoder + paint 4528e08e0 · sergio ·

1//! Encoder: `HeatmapMatrix` → buffer ARGB para subir como textura.
2
3use crate::matrix::HeatmapMatrix;
4use crate::palette::Ramp;
5
6/// Normaliza cada celda a `[0,1]` por min/max, la mapea por la rampa y
7/// empaqueta el resultado como `u32` ARGB (0xAARRGGBB), fila por fila.
8///
9/// El backend GPUI sube este buffer como una textura y la rendea con un
10/// solo `drawImageRect`, en vez de N draw calls.
11pub fn encode_argb(matrix: &HeatmapMatrix, ramp: Ramp) -> Vec<u32> {
12    let (min, max) = matrix.min_max();
13    let span = max - min;
14    let mut out = Vec::with_capacity(matrix.width() * matrix.height());
15    for &v in matrix.data() {
16        let t = if span > 0.0 { (v - min) / span } else { 0.0 };
17        let c = ramp.sample(t);
18        out.push(pack_argb(c.a, c.r, c.g, c.b));
19    }
20    out
21}
22
23/// Empaqueta 4 canales `f32` `[0,1]` en `0xAARRGGBB`.
24fn pack_argb(a: f32, r: f32, g: f32, b: f32) -> u32 {
25    let q = |v: f32| (v.clamp(0.0, 1.0) * 255.0).round() as u32;
26    (q(a) << 24) | (q(r) << 16) | (q(g) << 8) | q(b)
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn encodes_one_pixel_per_cell() {
35        let m = HeatmapMatrix::from_data(vec![0.0, 1.0, 2.0, 3.0], 2, 2).unwrap();
36        let buf = encode_argb(&m, Ramp::Grayscale);
37        assert_eq!(buf.len(), 4);
38    }
39
40    #[test]
41    fn normalizes_min_to_ramp_start() {
42        // Grayscale: min → negro (0x000000), max → blanco (0xffffff).
43        let m = HeatmapMatrix::from_data(vec![10.0, 20.0], 2, 1).unwrap();
44        let buf = encode_argb(&m, Ramp::Grayscale);
45        assert_eq!(buf[0] & 0x00ff_ffff, 0x0000_0000); // min
46        assert_eq!(buf[1] & 0x00ff_ffff, 0x00ff_ffff); // max
47        assert_eq!(buf[0] >> 24, 0xff); // alpha opaco
48    }
49
50    #[test]
51    fn flat_matrix_does_not_divide_by_zero() {
52        let m = HeatmapMatrix::from_data(vec![5.0; 4], 2, 2).unwrap();
53        let buf = encode_argb(&m, Ramp::Viridis);
54        assert_eq!(buf.len(), 4);
55        assert!(buf.iter().all(|&p| p == buf[0]));
56    }
57}