summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 8196108dc339cee0c3fe51911b545164975b4db3 (plain)
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
use async_std::{fs::File, io, prelude::*, task, io::BufWriter};

fn lerp(fraction: f64, from: f64, to: f64) -> f64 {
    from + fraction * (to - from)
}

fn compute_pixel(re: f64, im: f64) -> u32 {
    let mut a = re;
    let mut b = im;
    let mut a2 = a * a;
    let mut b2 = b * b;
    let mut niters = 0;
    while a2 + b2 < 4.0 {
        b = 2.0 * a * b + im;
        a = a2 - b2 + re;
        a2 = a * a;
        b2 = b * b;
        niters += 1;
        if niters >= 10240 { break; }
    }
    niters
}

async fn compute_row(im: f64, re_min: f64, re_max: f64, width: usize) -> Vec<u32> {
    let mut row = Vec::new();
    row.resize(width, 0);
    for i in 0..width {
        row[i] = compute_pixel(lerp((i as f64) / (width as f64), re_min, re_max), im);
    }
    row
}

async fn write_image(fname: &str, rows: &Vec<Vec<u32>>) -> io::Result<()> {
    let width = rows[0].len();
    let height = rows.len();
    let maxval = rows.iter().map(|row| row.iter().max().unwrap()).max().unwrap();

    let mut out_vec: Vec<u8> = Vec::new();
    let mut writer = BufWriter::new(&mut out_vec);
    write!(writer, "P2\n{} {}\n{}\n", width, height, maxval).await?;
    for row in rows {
        for item in row {
            write!(writer, "{} ", item).await?;
        }
        writeln!(writer).await?;
    }

    let mut file = File::create(fname).await?;
    file.write(&out_vec).await?;
    file.flush().await?;

    Ok(())
}

async fn compute_brot(fname: &str, width: usize, height: usize, left_top: (f64, f64), right_bottom: (f64, f64)) -> io::Result<()> {
    println!("Spawning...");
    let mut join_handles = Vec::with_capacity(height);
    for i in 0..height {
        let im = lerp(i as f64 / height as f64, left_top.1, right_bottom.1);
        // Note: spawn to not run within the same thread
        join_handles.push(task::spawn(compute_row(im, left_top.0, right_bottom.0, width)));
    }

    println!("Awaiting...");
    let mut rows = Vec::with_capacity(height);
    for jh in join_handles {
        rows.push(jh.await);
    }

    println!("Writing...");
    write_image(fname, &rows).await
}

fn main() -> io::Result<()> {
    // Note: the following does not work:
    //    task::block_on(task::spawn(compute_brot("out.ppm", 1920, 1080, (-1.5, 1.0), (1.0, -1.0))))
    // You get a scary large futures error about Send.

    task::block_on(async {
        let width = 1920;
        let height = 1080;
        let left = -1.8;
        let right = 1.5;
        let height_im = height as f64 / width as f64 * (right - left);
        let top = height_im / 2.0;
        let bottom = -height_im / 2.0;
        compute_brot("out.ppm", width, height, (left, top), (right, bottom)).await
    })
}

// vim: set sw=4 ts=4 et: