petaplot_core/lod/
pyramid.rs1use crate::compute::simd::MinMaxPair;
2use crate::error::{Result, TeraError};
3
4pub const DEFAULT_LOD_FACTOR: usize = 10;
6
7#[derive(Debug, Clone)]
9pub struct LodLevel {
10 pub level_index: usize,
11 pub step_factor: usize,
12 pub pairs: Vec<MinMaxPair>,
13}
14
15#[derive(Debug, Clone)]
17pub struct LodPyramid {
18 pub total_samples: usize,
19 pub factor: usize,
20 pub levels: Vec<LodLevel>,
21}
22
23impl LodPyramid {
24 pub fn new(total_samples: usize, factor: usize) -> Self {
26 Self {
27 total_samples,
28 factor: factor.max(2),
29 levels: Vec::new(),
30 }
31 }
32
33 pub fn num_levels(&self) -> usize {
35 self.levels.len()
36 }
37
38 pub fn select_optimal_lod(&self, visible_samples: usize, screen_width_px: f32, oversampling: f32) -> usize {
40 if self.levels.is_empty() || visible_samples == 0 || screen_width_px <= 0.0 {
41 return 0;
42 }
43
44 let target_bins = screen_width_px * oversampling.max(1.0);
45 let ratio = visible_samples as f32 / target_bins;
46
47 if ratio <= 1.0 {
48 return 0;
49 }
50
51 let lod_float = ratio.log(self.factor as f32).floor();
52 let target_lod = lod_float as usize;
53
54 target_lod.min(self.levels.len() - 1)
55 }
56
57 pub fn get_level(&self, level_index: usize) -> Result<&LodLevel> {
59 self.levels.get(level_index).ok_or_else(|| {
60 TeraError::Lod(format!(
61 "Nivel LOD {} no existe en la pirámide (niveles totales: {})",
62 level_index,
63 self.levels.len()
64 ))
65 })
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_lod_level_selection() {
75 let mut pyramid = LodPyramid::new(1_000_000, 10);
76 pyramid.levels.push(LodLevel { level_index: 0, step_factor: 1, pairs: vec![] });
77 pyramid.levels.push(LodLevel { level_index: 1, step_factor: 10, pairs: vec![] });
78 pyramid.levels.push(LodLevel { level_index: 2, step_factor: 100, pairs: vec![] });
79 pyramid.levels.push(LodLevel { level_index: 3, step_factor: 1000, pairs: vec![] });
80
81 let selected = pyramid.select_optimal_lod(100_000, 1000.0, 1.0);
82 assert_eq!(selected, 2);
83
84 let selected_zoom = pyramid.select_optimal_lod(500, 1000.0, 1.0);
85 assert_eq!(selected_zoom, 0);
86 }
87}