#![warn(missing_docs)]
use std::iter::{
self,
Extend
};
use std::f64;
use rand::{
Rng,
SeedableRng,
distributions::{
Standard
}
};
use rand_xorshift::{
XorShiftRng
};
use concurrent_hashmap::{
Options as HashMapOptions,
ConcHashMap
};
use fnv::FnvBuildHasher;
#[cfg(not(feature = "web"))]
use rayon::prelude::*;
pub struct WorleyNoise {
permutation_table: Vec<u128>,
permutation_mask: usize,
density: f64,
point_count_table: Vec<u32>,
cache: ConcHashMap<(i32, i32, i32), Vec<(f64, f64, f64)>, FnvBuildHasher>,
distance_function: Box<Fn(f64, f64, f64) -> f64 + Sync>,
value_function: Box<Fn(Vec<f64>) -> f64 + Sync>,
radius_x: u16,
radius_z: u16,
radius_y: u16
}
impl WorleyNoise {
const MIN_POINTS: u32 = 1;
const MAX_POINTS: u32 = 9;
const POINT_COUNT_TABLE_LEN: usize = 150;
const CONCURRENCY: u16 = 64;
pub const DEFAULT_RADIUS: u16 = 1;
pub const DEFAULT_PERMUTATION_BITS: usize = 10;
pub const DEFAULT_DENSITY: f64 = 3.0;
pub const DEFAULT_CACHE_CAPACITY: usize = 10000;
pub fn new() -> Self {
Self::with_cache_capacity(Self::DEFAULT_CACHE_CAPACITY)
}
pub fn with_cache_capacity(capacity: usize) -> Self {
let default_distance_function = |x, y, z| x * x + y * y + z * z;
let default_value_function = |distances: Vec<f64>| distances.iter()
.cloned()
.fold(f64::MAX, f64::min);
let cache_options = HashMapOptions {
capacity: capacity,
hasher_factory: FnvBuildHasher::default(),
concurrency: Self::CONCURRENCY
};
let mut noise = WorleyNoise {
permutation_table: Vec::new(),
permutation_mask: 0,
density: 0.0,
point_count_table: Vec::new(),
cache: ConcHashMap::with_options(cache_options),
distance_function: Box::new(default_distance_function),
value_function: Box::new(default_value_function),
radius_x: Self::DEFAULT_RADIUS,
radius_y: Self::DEFAULT_RADIUS,
radius_z: Self::DEFAULT_RADIUS
};
noise.set_density(Self::DEFAULT_DENSITY);
noise.permutate(Self::DEFAULT_PERMUTATION_BITS);
noise
}
fn feature_point_count(&self, probability: f64) -> u32 {
let index = (self.point_count_table.len() as f64 * probability).floor() as usize;
self.point_count_table[index]
}
fn hash(&self, x: i32, y: i32, z: i32) -> [u8; 16] {
let mut hash = self.permutation_table[x as usize & self.permutation_mask];
hash = self.permutation_table[(hash as usize ^ y as usize) & self.permutation_mask];
hash = self.permutation_table[(hash as usize ^ z as usize) & self.permutation_mask];
hash.to_be_bytes()
}
fn feature_points(&self, cube_x: i32, cube_y: i32, cube_z: i32, collector: &mut Vec<(f64, f64, f64)>) {
if let Some(cached) = self.cache.find(&(cube_x, cube_y, cube_z)) {
collector.extend_from_slice(cached.get());
return;
}
let mut points = Vec::new();
let seed = self.hash(cube_x, cube_y, cube_z);
let mut rng = XorShiftRng::from_seed(seed);
let count = self.feature_point_count(rng.gen());
for _ in 0 .. count {
let x = rng.gen::<f64>() + cube_x as f64;
let y = rng.gen::<f64>() + cube_y as f64;
let z = rng.gen::<f64>() + cube_z as f64;
points.push((x, y, z));
}
collector.extend_from_slice(&points);
self.cache.insert((cube_x, cube_y, cube_z), points);
}
fn adjacent_feature_points(&self, cube_x: i32, cube_y: i32, cube_z: i32) -> Vec<(f64, f64, f64)> {
let cubes_around = (1 + self.radius_x * 2) * (1 + self.radius_y * 2) * (1 + self.radius_z * 2);
let expected_point_count = ((self.density + 1.0) * cubes_around as f64) as usize;
let mut points = Vec::with_capacity(expected_point_count);
let radius_x = self.radius_x as i32;
let radius_y = self.radius_y as i32;
let radius_z = self.radius_z as i32;
let start_x = cube_x - radius_x;
let start_y = cube_y - radius_y;
let start_z = cube_z - radius_z;
let end_x = cube_x + radius_x;
let end_y = cube_y + radius_y;
let end_z = cube_z + radius_z;
for x in start_x ..= end_x {
for y in start_y ..= end_y {
for z in start_z ..= end_z {
self.feature_points(x, y, z, &mut points);
}
}
}
points
}
pub fn permutate(&mut self, permutation_table_bit_length: usize) {
self.permutate_seeded(permutation_table_bit_length, rand::random());
}
pub fn permutate_seeded(&mut self, permutation_table_bit_length: usize, seed: u128) {
let seed = seed.to_be_bytes();
let mut rng = XorShiftRng::from_seed(seed);
let length = 1 << permutation_table_bit_length;
self.permutation_table.clear();
self.permutation_table.reserve(length);
self.permutation_table.extend(rng.sample_iter::<u128, Standard>(&Standard).take(length));
self.permutation_mask = length - 1;
}
pub fn set_density(&mut self, density: f64) {
self.point_count_table.clear();
self.point_count_table.reserve(Self::POINT_COUNT_TABLE_LEN);
for i in Self::MIN_POINTS ..= Self::MAX_POINTS {
let poisson = density.powi(i as i32) * f64::consts::E.powf(-density) / factorial(i as u16) as f64;
let count = (poisson * Self::POINT_COUNT_TABLE_LEN as f64).round() as usize;
self.point_count_table.extend(iter::repeat(i).take(count));
}
self.density = density;
}
pub fn set_distance_function<F>(&mut self, function: F) where F: Fn(f64, f64, f64) -> f64 + Sync + 'static {
self.distance_function = Box::new(function);
}
pub fn set_value_function<F>(&mut self, function: F) where F: Fn(Vec<f64>) -> f64 + Sync + 'static {
self.value_function = Box::new(function);
}
pub fn set_radius(&mut self, radius: u16) {
self.radius_x = radius;
self.radius_y = radius;
self.radius_z = radius;
}
pub fn set_radius_x(&mut self, radius: u16) {
self.radius_x = radius;
}
pub fn set_radius_y(&mut self, radius: u16) {
self.radius_y = radius;
}
pub fn set_radius_z(&mut self, radius: u16) {
self.radius_z = radius;
}
pub fn value_3d(&self, x: f64, y: f64, z: f64) -> f64 {
let points = self.adjacent_feature_points(x.floor() as i32, y.floor() as i32, z.floor() as i32);
let distances = points.iter()
.map(|&(px, py, pz)| (px - x, py - y, pz - z))
.map(|(x, y, z)| (self.distance_function)(x, y, z))
.collect();
(self.value_function)(distances)
}
pub fn value_2d(&self, x: f64, y: f64) -> f64 {
self.value_3d(x, y, 0.0)
}
pub fn values_3d(&self, points: &Vec<(f64, f64, f64)>) -> Vec<f64> {
let mut values = Vec::with_capacity(points.len());
#[cfg(not(feature = "web"))]
{
points.par_iter()
.map(|&(x, y, z)| self.value_3d(x, y, z))
.collect_into_vec(&mut values);
}
#[cfg(feature = "web")]
{
points.iter()
.map(|&(x, y, z)| self.value_3d(x, y, z))
.for_each(|val| values.push(val));
}
values
}
pub fn values_2d(&self, points: &Vec<(f64, f64)>) -> Vec<f64> {
let points_3d = points.iter()
.map(|(x, y)| (*x, *y, 0.0))
.collect();
self.values_3d(&points_3d)
}
pub fn clear_cache(&mut self) {
self.cache.clear();
}
}
fn factorial(x: u16) -> u32 {
let mut val = 1;
for i in 2 .. x as u32 + 1 {
val *= i;
}
val
}