Skip to content

Instantly share code, notes, and snippets.

@neildanson
Created September 25, 2015 20:10
Show Gist options
  • Save neildanson/73312225395acf733482 to your computer and use it in GitHub Desktop.
Save neildanson/73312225395acf733482 to your computer and use it in GitHub Desktop.
//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