use std::f64;
use std::fs::File;
use std::io::Result as IoResult;
use worley_noise::WorleyNoise;
use gif::{
Frame,
Encoder,
ExtensionData,
Repeat
};
const WIDTH: u16 = 250;
const HEIGHT: u16 = 250;
const SCALE: f64 = 0.025;
const MAX_DEPTH: f64 = 3.0;
const DEPTH_STEP: f64 = 0.05;
const COLORS: [(u8, u8, u8); 5] = [(0, 50, 100), (10, 75, 125), (25, 125, 150), (175, 225, 250), (250, 255, 255)];
const OUTPUT_PATH: &'static str = "noise.gif";
fn create_color_table(colors: &[(u8, u8, u8)]) -> Vec<(u8, u8, u8)> {
let mut table = Vec::new();
for i in 0 .. colors.len() - 1 {
let (current_red, current_green, current_blue) = colors[i];
let (next_red, next_green, next_blue) = colors[i + 1];
let (diff_red, diff_green, diff_blue) = (
next_red as i16 - current_red as i16,
next_green as i16 - current_green as i16,
next_blue as i16 - current_blue as i16
);
let diff_max = diff_red.abs()
.max(diff_green.abs())
.max(diff_blue.abs());
let (change_red, change_green, change_blue) = (
diff_red as f64 / diff_max as f64,
diff_green as f64 / diff_max as f64,
diff_blue as f64 / diff_max as f64
);
for j in 0 .. diff_max {
let red = (current_red as f64 + (change_red * j as f64))
.min(255.0)
.max(0.0) as u8;
let green = (current_green as f64 + (change_green * j as f64))
.min(255.0)
.max(0.0) as u8;
let blue = (current_blue as f64 + (change_blue * j as f64))
.min(255.0)
.max(0.0) as u8;
table.push((red, green, blue));
}
}
table
}
fn main() -> IoResult<()> {
let capacity = ((WIDTH * HEIGHT) as f64 * SCALE) as usize;
let mut noise = WorleyNoise::with_cache_capacity(capacity);
let color_table = create_color_table(&COLORS);
let mut encoder = Encoder::new(File::create(OUTPUT_PATH)?, WIDTH, HEIGHT, &[]).unwrap();
let mut z = 0.0;
let mut z_progress = 0.0;
while z < MAX_DEPTH {
let mut points = Vec::with_capacity(capacity);
for y in 0 .. HEIGHT {
for x in 0 .. WIDTH {
points.push((x as f64 * SCALE, y as f64 * SCALE, z as f64));
}
}
noise.set_value_function(|distances| {
let mut min = f64::MAX;
let mut second_min = f64::MAX;
for &distance in distances.iter() {
if distance < min {
second_min = min;
min = distance;
} else if distance < second_min {
second_min = distance;
}
}
min
});
let values = noise.values_3d(&points);
let max = values.iter().fold(0.0, |a, b| f64::max(a, *b));
let mut color_values = Vec::with_capacity(values.len() * 3);
for val in values {
let val = (val / max).abs().min(1.0);
let index = (val * (color_table.len() - 1) as f64) as usize;
let (red, green, blue) = color_table[index];
color_values.push(red);
color_values.push(green);
color_values.push(blue);
}
let frame = Frame::from_rgb(WIDTH, HEIGHT, &color_values);
encoder.write_frame(&frame).unwrap();
z += DEPTH_STEP;
z_progress += DEPTH_STEP;
if z_progress > 2.0 {
noise.clear_cache();
z_progress = 0.0;
}
}
encoder.write_extension(ExtensionData::Repetitions(Repeat::Infinite)).unwrap();
Ok(())
}