worley-noise 2.0.5

Worley noise implementation
Documentation
/*
Copyright 2018 Johannes Boczek

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

extern crate worley_noise;
extern crate image;

use std::f64;
use worley_noise::WorleyNoise;
use image::{ImageBuffer, ImageRgb8};

const WIDTH: u32 = 1920;
const HEIGHT: u32 = 1080;
const SCALE: f64 = 0.003;
const COLORS: [(u8, u8, u8); 5] = [(0, 0, 0), (0, 100, 25), (10, 150, 50), (50, 200, 100), (100, 250, 200)];
const OUTPUT_PATH: &'static str = "noise.png";

/// Create a color table from an array of base colors
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
}

/// Generate noise values and store them as a file
fn main() {
	let capacity = ((WIDTH * HEIGHT) as f64 * SCALE) as usize;
	let mut noise = WorleyNoise::with_cache_capacity(capacity);
	let mut points = Vec::with_capacity(capacity);
	let color_table = create_color_table(&COLORS);
	
	/*
	 * Fill sample points
	 */
	for y in 0 .. HEIGHT {
		for x in 0 .. WIDTH {
			points.push((x as f64 * SCALE, y as f64 * SCALE));
		}
	}
	
	/*
	 * Value function: Subtract smallest from second smallest value
	 */
	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;
			}
		}
		
		second_min - min
	});
	
	/*
	 * Sample points, find maximum for normalization
	 */
	let values = noise.values(&points);
	let max = values.iter().fold(0.0, |x, y| f64::max(x, *y));
	let mut color_values = Vec::with_capacity(values.len() * 3);
	
	/*
	 * Normalize values and map them to colors using the color table
	 */
	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);
	}
	
	/*
	 * Save the generated data as an image
	 */
	let buffer = ImageBuffer::from_vec(WIDTH, HEIGHT, color_values)
		.unwrap();
	
	ImageRgb8(buffer).save(OUTPUT_PATH)
		.unwrap();
}