Created
September 25, 2015 20:10
-
-
Save neildanson/73312225395acf733482 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//add rand and bmp as dependencies in toml file | |
extern crate rand; | |
extern crate bmp; | |
use bmp::Image; | |
use rand::Rng; | |
#[derive(Clone)] | |
enum Expr { | |
VariableX, | |
VariableY, | |
Constant, | |
Sum(Box<Expr>, Box<Expr>), | |
Product(Box<Expr>, Box<Expr>), | |
Mod(Box<Expr>, Box<Expr>), | |
Well(Box<Expr>), | |
Tent(Box<Expr>), | |
Sin(Box<Expr>), | |
Level(Box<Expr>,Box<Expr>,Box<Expr>), | |
Mix(Box<Expr>, Box<Expr>, Box<Expr>) | |
} | |
fn average(c1:(f64,f64,f64), c2:(f64,f64,f64), w:f64) -> (f64,f64,f64) { | |
let (r1,g1,b1) = c1; | |
let (r2,g2,b2) = c2; | |
let r = w * r1 + (1.0 - w) * r2; | |
let g = w * g1 + (1.0 - w) * g2; | |
let b = w * b1 + (1.0 - w) * b2; | |
(r,g,b) | |
} | |
fn well(x:f64) -> f64 { | |
1.0 - 2.0 / (1.0 + x * x).powf(8.0) | |
} | |
fn tent(x:f64) -> f64 { | |
1.0 - 2.0 * x.abs() | |
} | |
fn eval(expr:Box<Expr>) -> Box<Fn(f64,f64) -> (f64, f64,f64)> { | |
let mut rng = rand::thread_rng(); | |
let expr = *expr; | |
match expr { | |
Expr::VariableX => Box::new(move |x,_| (x, x, x)), | |
Expr::VariableY => Box::new(move |_,y| (y, y, y)), | |
Expr::Constant => { | |
let r = rng.gen(); | |
let g = rng.gen(); | |
let b = rng.gen(); | |
Box::new(move |_,_| (r,g,b)) | |
}, | |
Expr::Sum(e1,e2) => { | |
let f1 = eval(e1); | |
let f2 = eval(e2); | |
Box::new(move |x,y| average(f1(x,y), f2(x,y), 0.5)) | |
}, | |
Expr::Product(e1,e2) => { | |
let f1 = eval(e1); | |
let f2 = eval(e2); | |
Box::new(move |x,y| { | |
let (r1,g1,b1) = f1(x,y); | |
let (r2,g2,b2) = f2(x,y); | |
(r1*r2, g1*g2, b1*b2) | |
}) | |
}, | |
Expr::Mod(e1,e2) => { | |
let f1 = eval(e1); | |
let f2 = eval(e2); | |
Box::new(move |x,y| { | |
let (r1,g1,b1) = f1(x,y); | |
let (r2,g2,b2) = f2(x,y); | |
(r1%r2, g1%g2, b1%b2) | |
}) | |
}, | |
Expr::Well(e) => { | |
let f = eval(e); | |
Box::new(move |x,y| { | |
let (r,g,b) = f(x,y); | |
(well(r), well(g), well(b)) | |
}) | |
}, | |
Expr::Tent(e) => { | |
let f = eval(e); | |
Box::new(move |x,y| { | |
let (r,g,b) = f(x,y); | |
(tent(r), tent(g), tent(b)) | |
}) | |
}, | |
Expr::Sin(e) => { | |
let f = eval(e); | |
let phase = rng.gen::<f64>() * 3.14; | |
let freq = (rng.gen::<f64>() * 5.0) + 1.0; | |
Box::new(move |x,y| { | |
let (r,g,b) = f(x,y); | |
let r :f64 = phase + r * freq; | |
let g :f64 = phase + g * freq; | |
let b :f64 = phase + b * freq; | |
(r.sin(), g.sin(), b.sin()) | |
}) | |
}, | |
Expr::Level(e1,e2,e3) => { | |
let f1 = eval(e1); | |
let f2 = eval(e2); | |
let f3 = eval(e3); | |
let threshold = (rng.gen::<f64>() * 2.0) - 1.0; | |
Box::new(move |x,y| { | |
let (r1,g1,b1) = f1(x,y); | |
let (r2,g2,b2) = f2(x,y); | |
let (r3,g3,b3) = f3(x,y); | |
let r = if r1 < threshold { r2 } else { r3 }; | |
let g = if g1 < threshold { g2 } else { g3 }; | |
let b = if b1 < threshold { b2 } else { b3 }; | |
(r,g,b) | |
}) | |
}, | |
Expr::Mix(e1, e2, e3) => { | |
let f1 = eval(e1); | |
let f2 = eval(e2); | |
let f3 = eval(e3); | |
Box::new(move |x,y| { | |
let (n,_,_) = f1(x,y); | |
let w = 0.5 * (n + 1.0); | |
let c1 = f2(x,y); | |
let c2 = f3(x,y); | |
average(c1,c2,w) | |
}) | |
}, | |
} | |
} | |
fn rgb(r:f64, g:f64, b:f64) -> (u8, u8, u8) { | |
let r = (128.0 * (r + 1.0)).min(255.0).max(0.0) as u8; | |
let g = (128.0 * (g + 1.0)).min(255.0).max(0.0) as u8; | |
let b = (128.0 * (b + 1.0)).min(255.0).max(0.0) as u8; | |
(r,g,b) | |
} | |
fn gen(k:i32) -> Box<Expr> { | |
let mut rng = rand::thread_rng(); | |
if k <= 0 || (rng.gen::<f64>()) < 0.01 { | |
let i = rng.gen_range(0, 3); | |
let terminals = [Expr::VariableX, Expr::VariableY, Expr::Constant]; | |
let t = terminals[i].clone(); | |
Box::new(t) | |
} else { | |
let i = rng.gen_range(0 ,8) as i32; | |
let mut n = || rng.gen_range(0, k); | |
let expr = match i { | |
0 => Expr::Sum(gen(n()), gen(n())), | |
1 => Expr::Product(gen(n()), gen(n())), | |
2 => Expr::Mod(gen(n()), gen(n())), | |
3 => Expr::Well(gen(n())), | |
4 => Expr::Tent(gen(n())), | |
5 => Expr::Sin(gen(n())), | |
6 => Expr::Level(gen(n()), gen(n()), gen(n())), | |
7 => Expr::Mix(gen(n()), gen(n()), gen(n())), | |
_ => panic!("Whoops"), | |
}; | |
Box::new(expr) | |
} | |
} | |
fn draw(f:Box<Fn(f64,f64) -> (f64, f64,f64)>, n:i32) { | |
let width = 512; | |
let height = 384; | |
let n = n as f64; | |
let mut img = Image::new(width, height); | |
for y in 0..height { | |
for x in 0..width { | |
let width = width as f64; | |
let height = height as f64; | |
let x1 = x as f64; | |
let y1 = y as f64; | |
let x1 = -1.0 + ((x1 + (n / 2.0)) * 2.0) / width; | |
let y1 = -1.0 + ((y1 + (n / 2.0)) * 2.0) / height; | |
let (r,g,b) = f(x1,y1); | |
let (r,g,b) = rgb(r,g,b); | |
img.set_pixel(x,y, bmp::Pixel { r: r, g: g, b: b }); | |
} | |
} | |
let filename = format!("c:/temp/test{}.bmp", n); | |
let _ = img.save(&filename); | |
} | |
fn show(n:i32) { | |
let e = gen(50); | |
let f = eval(e); | |
draw(f, n) | |
} | |
fn main() { | |
for n in 0 .. 1000 { | |
show(n); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment