use crate::compute::simd::MinMaxPair;
use crate::error::{Result, TeraError};
pub const DEFAULT_LOD_FACTOR: usize = 10;
#[derive(Debug, Clone)]
pub struct LodLevel {
pub level_index: usize,
pub step_factor: usize,
pub pairs: Vec<MinMaxPair>,
}
#[derive(Debug, Clone)]
pub struct LodPyramid {
pub total_samples: usize,
pub factor: usize,
pub levels: Vec<LodLevel>,
}
impl LodPyramid {
pub fn new(total_samples: usize, factor: usize) -> Self {
Self {
total_samples,
factor: factor.max(2),
levels: Vec::new(),
}
}
pub fn num_levels(&self) -> usize {
self.levels.len()
}
pub fn select_optimal_lod(&self, visible_samples: usize, screen_width_px: f32, oversampling: f32) -> usize {
if self.levels.is_empty() || visible_samples == 0 || screen_width_px <= 0.0 {
return 0;
}
let target_bins = screen_width_px * oversampling.max(1.0);
let ratio = visible_samples as f32 / target_bins;
if ratio <= 1.0 {
return 0;
}
let lod_float = ratio.log(self.factor as f32).floor();
let target_lod = lod_float as usize;
target_lod.min(self.levels.len() - 1)
}
pub fn get_level(&self, level_index: usize) -> Result<&LodLevel> {
self.levels.get(level_index).ok_or_else(|| {
TeraError::Lod(format!(
"Nivel LOD {} no existe en la pirámide (niveles totales: {})",
level_index,
self.levels.len()
))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lod_level_selection() {
let mut pyramid = LodPyramid::new(1_000_000, 10);
pyramid.levels.push(LodLevel { level_index: 0, step_factor: 1, pairs: vec![] });
pyramid.levels.push(LodLevel { level_index: 1, step_factor: 10, pairs: vec![] });
pyramid.levels.push(LodLevel { level_index: 2, step_factor: 100, pairs: vec![] });
pyramid.levels.push(LodLevel { level_index: 3, step_factor: 1000, pairs: vec![] });
let selected = pyramid.select_optimal_lod(100_000, 1000.0, 1.0);
assert_eq!(selected, 2);
let selected_zoom = pyramid.select_optimal_lod(500, 1000.0, 1.0);
assert_eq!(selected_zoom, 0);
}
}