use crate::compute::simd::{reduce_min_max_chunk, reduce_min_max_pairs};
use crate::lod::pyramid::{LodLevel, LodPyramid, DEFAULT_LOD_FACTOR};
pub struct LodBuilder {
factor: usize,
min_top_samples: usize,
}
impl Default for LodBuilder {
fn default() -> Self {
Self {
factor: DEFAULT_LOD_FACTOR,
min_top_samples: 100,
}
}
}
impl LodBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_factor(mut self, factor: usize) -> Self {
self.factor = factor.max(2);
self
}
pub fn with_min_top_samples(mut self, min_top_samples: usize) -> Self {
self.min_top_samples = min_top_samples.max(1);
self
}
pub fn build_from_slice(&self, raw_data: &[f32]) -> LodPyramid {
let total_samples = raw_data.len();
let mut pyramid = LodPyramid::new(total_samples, self.factor);
if raw_data.is_empty() {
return pyramid;
}
let level_0_pairs = reduce_min_max_chunk(raw_data, self.factor);
pyramid.levels.push(LodLevel {
level_index: 0,
step_factor: self.factor,
pairs: level_0_pairs,
});
let mut current_level_index = 1;
let mut current_step_factor = self.factor * self.factor;
while let Some(prev_level) = pyramid.levels.last() {
if prev_level.pairs.len() <= self.min_top_samples {
break;
}
let next_pairs = reduce_min_max_pairs(&prev_level.pairs, self.factor);
if next_pairs.is_empty() {
break;
}
pyramid.levels.push(LodLevel {
level_index: current_level_index,
step_factor: current_step_factor,
pairs: next_pairs,
});
current_level_index += 1;
current_step_factor *= self.factor;
}
pyramid
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lod_builder_pyramid_generation() {
let raw_data: Vec<f32> = (0..100_000).map(|i| (i as f32).sin()).collect();
let builder = LodBuilder::new().with_factor(10).with_min_top_samples(20);
let pyramid = builder.build_from_slice(&raw_data);
assert!(pyramid.num_levels() >= 3);
assert_eq!(pyramid.levels[0].pairs.len(), 10_000);
assert_eq!(pyramid.levels[1].pairs.len(), 1_000);
assert_eq!(pyramid.levels[2].pairs.len(), 100);
}
}