tawasuyu

tawasuyu/pineal

Backend-agnostic GPU visualization — a catalog of painters over one Canvas trait, in Rust.

⎇ df7773b4f2df1f1227adb0afd7afd60935e384e9 00_unanchay/pineal/pineal-core/src/buffer.rs Rust · 128 lines · 3.5 KB History Who touched what raw

chore: refresco desde el monorepo (pin tawasuyu.git v0.2.0) df7773b4f · Sergio ·

1//! `DataBuffer` — buffer interleaved `[x0, y0, x1, y1, ...]` con
2//! revision counter para invalidación de cachés.
3//!
4//! Es la primitiva universal de Lapaloma: todo serie cartesiana,
5//! todo grafo de nodos, todo OHLC vive en uno de estos (o en una
6//! variante con stride distinto). El layout `f32` x `f32` es lo
7//! que el GPU consume sin transformación.
8
9/// Buffer de coordenadas planas `[x, y]` empacadas.
10///
11/// La longitud lógica (número de puntos) es `coords.len() / 2`.
12/// Mutar in-place (`set_xy`, `push`) bumpea `revision` — los
13/// painters comparan su `last_seen_revision` para decidir si
14/// rebuilear su caché.
15#[derive(Debug, Clone, Default)]
16pub struct DataBuffer {
17    coords: Vec<f32>,
18    revision: u64,
19}
20
21impl DataBuffer {
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    /// Reserva espacio para `n` puntos sin agregarlos. Úsalo al
27    /// montar el widget para que `push` no realloque después.
28    pub fn with_capacity(n: usize) -> Self {
29        Self {
30            coords: Vec::with_capacity(n * 2),
31            revision: 0,
32        }
33    }
34
35    /// Construye a partir de coords interleaved ya armadas.
36    /// Útil en tests y carga inicial.
37    pub fn from_interleaved(coords: Vec<f32>) -> Self {
38        assert!(coords.len() % 2 == 0, "interleaved coords deben ser pares");
39        Self {
40            coords,
41            revision: 0,
42        }
43    }
44
45    pub fn push(&mut self, x: f32, y: f32) {
46        self.coords.push(x);
47        self.coords.push(y);
48        self.revision = self.revision.wrapping_add(1);
49    }
50
51    /// Sobrescribe un punto existente. `i` es el índice de punto
52    /// (no de float), 0-based.
53    pub fn set_xy(&mut self, i: usize, x: f32, y: f32) {
54        self.coords[i * 2] = x;
55        self.coords[i * 2 + 1] = y;
56        self.revision = self.revision.wrapping_add(1);
57    }
58
59    /// Pisa el contenido completo con la nueva slice.
60    /// Útil para hidratar el buffer en un solo memcpy.
61    pub fn replace_from(&mut self, src: &[f32]) {
62        assert!(src.len() % 2 == 0);
63        self.coords.clear();
64        self.coords.extend_from_slice(src);
65        self.revision = self.revision.wrapping_add(1);
66    }
67
68    pub fn clear(&mut self) {
69        self.coords.clear();
70        self.revision = self.revision.wrapping_add(1);
71    }
72
73    pub fn len(&self) -> usize {
74        self.coords.len() / 2
75    }
76
77    pub fn is_empty(&self) -> bool {
78        self.coords.is_empty()
79    }
80
81    pub fn xy(&self, i: usize) -> (f32, f32) {
82        (self.coords[i * 2], self.coords[i * 2 + 1])
83    }
84
85    /// Slice plana lista para `drawRawPoints` / `wgpu::Buffer`
86    /// / `<polyline points>`. No realiza copia.
87    pub fn coords(&self) -> &[f32] {
88        &self.coords
89    }
90
91    pub fn revision(&self) -> u64 {
92        self.revision
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn push_y_len() {
102        let mut b = DataBuffer::with_capacity(4);
103        b.push(0.0, 1.0);
104        b.push(1.0, 2.0);
105        assert_eq!(b.len(), 2);
106        assert_eq!(b.xy(1), (1.0, 2.0));
107    }
108
109    #[test]
110    fn revision_bumps() {
111        let mut b = DataBuffer::new();
112        let r0 = b.revision();
113        b.push(0.0, 0.0);
114        let r1 = b.revision();
115        b.set_xy(0, 1.0, 1.0);
116        let r2 = b.revision();
117        assert_ne!(r0, r1);
118        assert_ne!(r1, r2);
119    }
120
121    #[test]
122    fn coords_slice_is_zero_copy() {
123        let raw = vec![0.0, 0.0, 1.0, 1.0, 2.0, 2.0];
124        let b = DataBuffer::from_interleaved(raw);
125        assert_eq!(b.coords(), &[0.0, 0.0, 1.0, 1.0, 2.0, 2.0]);
126        assert_eq!(b.len(), 3);
127    }
128}