/* 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. */ use std::f64; use worley_noise::WorleyNoise; use image::RgbImage; 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 an image fn main() { let capacity = ((WIDTH * HEIGHT) as f64 * SCALE) as usize; let mut noise = WorleyNoise::with_cache_capacity(capacity); let color_table = create_color_table(&COLORS); /* * We're not using the 3d dimension so we can set the search radius to 0 to improve performance */ noise.set_radius_z(0); /* * Value function: Subtract smallest from second smallest value * Generates a crystal-like pattern */ 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 and find maximum for normalization */ let end_point = (WIDTH as f64 * SCALE, HEIGHT as f64 * SCALE); let values = noise.values_2d_range((0.0, 0.0), end_point, (WIDTH, HEIGHT)); let max = values.iter().fold(0.0, |a, b| f64::max(a, *b)); /* * Normalize values and map them to colors using the color table */ 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); } /* * Save the generated data as an image */ RgbImage::from_vec(WIDTH, HEIGHT, color_values) .unwrap() .save(OUTPUT_PATH) .unwrap(); }