Skip to main content

petaplot_core/lod/
builder.rs

1use crate::compute::simd::{reduce_min_max_chunk, reduce_min_max_pairs};
2use crate::lod::pyramid::{LodLevel, LodPyramid, DEFAULT_LOD_FACTOR};
3
4/// Generador de la pirámide de niveles de detalle (LOD).
5pub struct LodBuilder {
6    factor: usize,
7    min_top_samples: usize,
8}
9
10impl Default for LodBuilder {
11    fn default() -> Self {
12        Self {
13            factor: DEFAULT_LOD_FACTOR,
14            min_top_samples: 100,
15        }
16    }
17}
18
19impl LodBuilder {
20    pub fn new() -> Self {
21        Self::default()
22    }
23
24    pub fn with_factor(mut self, factor: usize) -> Self {
25        self.factor = factor.max(2);
26        self
27    }
28
29    pub fn with_min_top_samples(mut self, min_top_samples: usize) -> Self {
30        self.min_top_samples = min_top_samples.max(1);
31        self
32    }
33
34    /// Construye una pirámide de niveles de detalle completa a partir de una serie temporal raw `&[f32]`.
35    pub fn build_from_slice(&self, raw_data: &[f32]) -> LodPyramid {
36        let total_samples = raw_data.len();
37        let mut pyramid = LodPyramid::new(total_samples, self.factor);
38
39        if raw_data.is_empty() {
40            return pyramid;
41        }
42
43        // Nivel 0 (LOD 0): Primer resumen de la serie raw en bins base de tamaño `self.factor`
44        let level_0_pairs = reduce_min_max_chunk(raw_data, self.factor);
45        pyramid.levels.push(LodLevel {
46            level_index: 0,
47            step_factor: self.factor,
48            pairs: level_0_pairs,
49        });
50
51        // Construcción jerárquica iterativa para niveles superiores (LOD 1, LOD 2, ...)
52        let mut current_level_index = 1;
53        let mut current_step_factor = self.factor * self.factor;
54
55        while let Some(prev_level) = pyramid.levels.last() {
56            if prev_level.pairs.len() <= self.min_top_samples {
57                break;
58            }
59
60            let next_pairs = reduce_min_max_pairs(&prev_level.pairs, self.factor);
61            if next_pairs.is_empty() {
62                break;
63            }
64
65            pyramid.levels.push(LodLevel {
66                level_index: current_level_index,
67                step_factor: current_step_factor,
68                pairs: next_pairs,
69            });
70
71            current_level_index += 1;
72            current_step_factor *= self.factor;
73        }
74
75        pyramid
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_lod_builder_pyramid_generation() {
85        let raw_data: Vec<f32> = (0..100_000).map(|i| (i as f32).sin()).collect();
86
87        let builder = LodBuilder::new().with_factor(10).with_min_top_samples(20);
88        let pyramid = builder.build_from_slice(&raw_data);
89
90        assert!(pyramid.num_levels() >= 3);
91        assert_eq!(pyramid.levels[0].pairs.len(), 10_000);
92        assert_eq!(pyramid.levels[1].pairs.len(), 1_000);
93        assert_eq!(pyramid.levels[2].pairs.len(), 100);
94    }
95}