1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
extern crate num;

use num::Complex;

fn pixel_to_point(
    pixel: (usize, usize),
    pixel_dimensions: (usize, usize),
    region: (Complex<f64>, Complex<f64>),
) -> Complex<f64> {
    assert!(pixel.0 < pixel_dimensions.0);
    assert!(pixel.1 < pixel_dimensions.1);

    let (upper_left, lower_right) = region;

    let re_step_size = (lower_right.re - upper_left.re) / pixel_dimensions.0 as f64;
    let im_step_size = (lower_right.im - upper_left.im) / pixel_dimensions.1 as f64;

    // Add half a pixel, so we're sampling from the middle of the pixel, not the corner
    let re = (pixel.0 as f64 + 0.5) * re_step_size + upper_left.re;
    let im = (pixel.1 as f64 + 0.5) * im_step_size + upper_left.im;

    Complex { re, im }
}

fn escape_time_algorithm(c: Complex<f64>, limit: u32) -> Option<u32> {
    let mut z = Complex { re: 0.0, im: 0.0 };
    for i in 0..limit {
        z = z * z + c;
        if z.re > 2.0 || z.im > 2.0 {
            return Some(i);
        }
    }

    None
}

pub fn escape_times_region(
    pixel_dimensions: (usize, usize),
    region: (Complex<f64>, Complex<f64>),
    limit: u32,
) -> Vec<Option<u32>> {
    let mut buffer = vec![None; pixel_dimensions.0 * pixel_dimensions.1];

    for (i, buf_value) in buffer.iter_mut().enumerate() {
        let pixel = (i % pixel_dimensions.0, i / pixel_dimensions.0);
        let point = pixel_to_point(pixel, pixel_dimensions, region);
        *buf_value = escape_time_algorithm(point, limit);
    }

    buffer
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pixel_to_point() {
        let upper_left = Complex { re: 0.0, im: 1.0 };
        let lower_right = Complex { re: 1.0, im: -1.0 };
        let region = (upper_left, lower_right);
        let pixel_dimensions = (20, 10);

        let pixels = vec![(0, 0), (0, 9), (19, 0), (19, 9)];

        let expected = vec![
            Complex { re: 0.025, im: 0.9 },
            Complex {
                re: 0.025,
                im: -0.9,
            },
            Complex { re: 0.975, im: 0.9 },
            Complex {
                re: 0.975,
                im: -0.9,
            },
        ];

        for (pixel, expected) in pixels.into_iter().zip(expected.into_iter()) {
            let result = pixel_to_point(pixel, pixel_dimensions, region);
            println!("{:?}", result);
            println!("{:?}", expected);
            println!("{:?}", (expected - result).norm());
            assert!((expected - result).norm() < 0.001);
        }
    }

    #[test]
    fn test_escape_time_algorithm_some() {
        let values = vec![(2.0, 0.0), (0.0, 2.0), (1.0, 3.0), (1.0, -2.0)];
        let limit = 100;
        for (re, im) in values.into_iter() {
            let value = Complex { re, im };
            assert!(escape_time_algorithm(value, limit).is_some());
        }
    }

    #[test]
    fn test_escape_time_algorithm_none() {
        let values = vec![(0.0, 0.0), (0.0, 0.1), (-0.03, 0.03)];
        let limit = 100;
        for (re, im) in values.into_iter() {
            let value = Complex { re, im };
            assert!(escape_time_algorithm(value, limit).is_none());
        }
    }

}