Skip to main content

proof_engine/fractal/
progressive.rs

1//! Progressive rendering — low-res first, refine over multiple frames.
2
3use super::mandelbrot::{MandelbrotRenderer, MandelbrotParams, FractalPixel};
4
5/// Progressive fractal renderer: renders at increasing resolution over multiple frames.
6pub struct ProgressiveRenderer {
7    pub target_width: u32,
8    pub target_height: u32,
9    pub current_level: u32,
10    pub max_level: u32,
11    pub pixels: Vec<FractalPixel>,
12    pub complete: bool,
13}
14
15impl ProgressiveRenderer {
16    pub fn new(width: u32, height: u32) -> Self {
17        let max_level = ((width.max(height) as f32).log2().ceil() as u32).max(2);
18        Self { target_width: width, target_height: height, current_level: 0, max_level, pixels: Vec::new(), complete: false }
19    }
20
21    /// Render the next refinement level. Returns true if rendering is complete.
22    pub fn step(&mut self, params: &MandelbrotParams) -> bool {
23        if self.complete { return true; }
24
25        let scale = 1u32 << (self.max_level - self.current_level);
26        let w = (self.target_width / scale).max(1);
27        let h = (self.target_height / scale).max(1);
28
29        let level_params = MandelbrotParams { width: w, height: h, ..params.clone() };
30        self.pixels = MandelbrotRenderer::render(&level_params);
31
32        self.current_level += 1;
33        self.complete = self.current_level >= self.max_level;
34        self.complete
35    }
36
37    /// Current resolution being rendered.
38    pub fn current_resolution(&self) -> (u32, u32) {
39        let scale = 1u32 << (self.max_level - self.current_level.min(self.max_level));
40        ((self.target_width / scale).max(1), (self.target_height / scale).max(1))
41    }
42
43    /// Progress as a fraction (0.0 to 1.0).
44    pub fn progress(&self) -> f32 { self.current_level as f32 / self.max_level as f32 }
45
46    /// Reset to start rendering from scratch.
47    pub fn reset(&mut self) { self.current_level = 0; self.complete = false; self.pixels.clear(); }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    #[test]
54    fn progressive_refines() {
55        let mut pr = ProgressiveRenderer::new(64, 64);
56        let params = MandelbrotParams { width: 64, height: 64, max_iter: 50, ..Default::default() };
57        let (w1, h1) = pr.current_resolution();
58        pr.step(&params);
59        let (w2, h2) = pr.current_resolution();
60        assert!(w2 >= w1, "Resolution should increase: {w1} → {w2}");
61    }
62    #[test]
63    fn progressive_completes() {
64        let mut pr = ProgressiveRenderer::new(16, 16);
65        let params = MandelbrotParams { width: 16, height: 16, max_iter: 20, ..Default::default() };
66        for _ in 0..20 { if pr.step(&params) { break; } }
67        assert!(pr.complete);
68    }
69}