use rand::Rng;
use crate::error::Error;
const DEFAULT_GRADIENTS: [[f64; 2]; 4] = [[1.0,1.0],[1.0,-1.0],[-1.0,1.0],[-1.0,-1.0]];
pub struct PerlinNoise {
image_width: u32,
gradient_point_nbr: u32,
tilable: bool,
gradient_grid_width: f64,
gradient_grid: Vec<Vec<usize>>
}
impl PerlinNoise {
pub fn new(image_width: u32, gradient_point_nbr: u32, tilable: bool) -> Result<PerlinNoise, Error> {
if gradient_point_nbr > image_width {
return Err(Error::TooManyGradientPointsRequested);
}
let mut rng = rand::thread_rng();
let mut gradient_grid: Vec<Vec<usize>> = Vec::new();
for _ in 0..gradient_point_nbr {
let mut gradient_grid_line: Vec<usize> = Vec::new();
for _ in 0..gradient_point_nbr {
gradient_grid_line.push((rng.gen::<f64>()*4.0) as usize);
}
if tilable {
gradient_grid_line.push(gradient_grid_line[0]);
}
gradient_grid.push(gradient_grid_line);
}
let mut gradient_grid_line: Vec<usize> = Vec::new();
if tilable {
for i in 0..gradient_point_nbr {
gradient_grid_line.push(gradient_grid[0][i as usize]);
}
}
gradient_grid.push(gradient_grid_line);
Ok(PerlinNoise {
image_width,
gradient_point_nbr,
tilable,
gradient_grid_width: image_width as f64 / (gradient_point_nbr-1) as f64,
gradient_grid
})
}
pub fn get_pixel(&self, x: u32, y: u32) -> f64 {
let po: [u32; 2] = [(y as f64/self.gradient_grid_width) as u32, (x as f64/self.gradient_grid_width) as u32];
let pm: [f64; 2] = [
(y as f64 % self.gradient_grid_width)/self.gradient_grid_width,
(x as f64 % self.gradient_grid_width)/self.gradient_grid_width
];
let d0 = PerlinNoise::dot([-pm[0],pm[1]], DEFAULT_GRADIENTS[self.gradient_grid[po[0] as usize][po[1] as usize]]);
let d1 = PerlinNoise::dot([-pm[0],pm[1]-1.0], DEFAULT_GRADIENTS[self.gradient_grid[po[0] as usize][po[1] as usize+1]]);
let d2 = PerlinNoise::dot([-pm[0]+1.0,pm[1]], DEFAULT_GRADIENTS[self.gradient_grid[po[0] as usize+1][po[1] as usize]]);
let d3 = PerlinNoise::dot([-pm[0]+1.0,pm[1]-1.0], DEFAULT_GRADIENTS[self.gradient_grid[po[0] as usize+1][po[1] as usize+1]]);
PerlinNoise::double_lerp(d0, d1, d2, d3, pm[1], pm[0])
}
pub fn get_pixel_value(&self, x: u32, y: u32) -> u8 {
(self.get_pixel(x, y) * 256.0) as u8
}
pub fn get_image_width(&self) -> u32 {
self.image_width
}
pub fn get_tilable(&self) -> bool {
self.tilable
}
pub fn get_gradient_point_nbr(&self) -> u32 {
self.gradient_point_nbr
}
pub fn get_gradient_grid_width(&self) -> f64 {
self.gradient_grid_width
}
fn dot(vect1: [f64;2], vect2: [f64;2]) -> f64 {
(((vect1[0]*vect2[0]+vect1[1]*vect2[1]))+1.0)/2.0
}
fn fade(value: f64) -> f64 {
((6.0*value-15.0)*value+10.0)*value*value*value
}
fn lerp(v1: f64, v2: f64, t: f64) -> f64 {
v1+(v2-v1)*PerlinNoise::fade(t)
}
fn double_lerp(v1:f64, v2:f64, v3:f64, v4:f64, t1:f64, t2:f64) -> f64 {
PerlinNoise::lerp(PerlinNoise::lerp(v1, v2, t1), PerlinNoise::lerp(v3, v4, t1), t2)
}
}