use crate::prelude::Vector;
use num_traits::{
Float as FloatT, Num as NumT, NumAssignOps, NumAssignRef, NumCast, NumOps, NumRef,
};
use rand::{self, distributions::uniform::SampleUniform, Rng};
use std::ops::{AddAssign, Range};
use once_cell::sync::Lazy;
pub use std::f64::consts::*;
pub trait Num:
NumT + NumOps + NumAssignOps + NumAssignRef + Copy + Default + PartialOrd + PartialEq
{
}
pub trait Float: Num + FloatT {}
impl<T> Num for T where
T: NumT
+ NumOps
+ NumRef
+ NumAssignOps
+ NumAssignRef
+ Copy
+ Default
+ PartialOrd
+ PartialEq
{
}
impl<T> Float for T where T: Num + FloatT {}
const PERLIN_YWRAPB: usize = 4;
const PERLIN_YWRAP: usize = 1 << PERLIN_YWRAPB;
const PERLIN_ZWRAPB: usize = 8;
const PERLIN_ZWRAP: usize = 1 << PERLIN_ZWRAPB;
const PERLIN_SIZE: usize = 4095;
static PERLIN: Lazy<Vec<f64>> = Lazy::new(|| {
let mut perlin = Vec::with_capacity(PERLIN_SIZE + 1);
for _ in 0..=PERLIN_SIZE {
perlin.push(random(1.0));
}
perlin
});
pub fn random_rng<T, R>(val: R) -> T
where
T: SampleUniform + PartialOrd,
R: Into<Range<T>>,
{
let val = val.into();
rand::thread_rng().gen_range(val)
}
pub fn random<T>(val: T) -> T
where
T: Num + SampleUniform + PartialOrd,
{
if val > T::zero() {
random_rng(T::zero()..val)
} else {
random_rng(val..T::zero())
}
}
pub fn noise<V, const N: usize>(vector: V) -> f64
where
V: Into<Vector<f64, N>>,
{
let v = vector.into();
let values = v.coords();
let x = values.first().unwrap_or(&0.0).abs();
let y = values.get(1).unwrap_or(&0.0).abs();
let z = values.get(2).unwrap_or(&0.0).abs();
let mut xi: usize = x.trunc() as usize;
let mut yi: usize = y.trunc() as usize;
let mut zi: usize = z.trunc() as usize;
let mut xf = x.fract();
let mut yf = y.fract();
let mut zf = z.fract();
let (mut rxf, mut ryf);
let mut noise_result = 0.0;
let mut ampl = 0.5;
let (mut n1, mut n2, mut n3);
let scaled_cosine = |i: f64| 0.5 * (1.0 - (i - PI).cos());
let perlin_octaves = 4; let perlin_amp_falloff = 0.5; for _ in 0..perlin_octaves {
let mut of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB);
rxf = scaled_cosine(xf);
ryf = scaled_cosine(yf);
n1 = PERLIN[of & PERLIN_SIZE];
n1 += rxf * (PERLIN[(of + 1) & PERLIN_SIZE] - n1);
n2 = PERLIN[(of + PERLIN_YWRAP) & PERLIN_SIZE];
n2 += rxf * (PERLIN[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2);
n1 += ryf * (n2 - n1);
of += PERLIN_ZWRAP;
n2 = PERLIN[of & PERLIN_SIZE];
n2 += rxf * (PERLIN[(of + 1) & PERLIN_SIZE] - n2);
n3 = PERLIN[(of + PERLIN_YWRAP) & PERLIN_SIZE];
n3 += rxf * (PERLIN[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3);
n2 += ryf * (n3 - n2);
n1 += scaled_cosine(zf) * (n2 - n1);
noise_result += n1 * ampl;
ampl *= perlin_amp_falloff;
xi <<= 1;
xf *= 2.0;
yi <<= 1;
yf *= 2.0;
zi <<= 1;
zf *= 2.0;
if xf >= 1.0 {
xi += 1;
xf -= 1.0;
}
if yf >= 1.0 {
yi += 1;
yf -= 1.0;
}
if zf >= 1.0 {
zi += 1;
zf -= 1.0;
}
}
noise_result
}
#[macro_export]
macro_rules! random {
() => {
$crate::math::random(1.0)
};
($v:expr) => {
$crate::math::random($v)
};
($s:expr, $e:expr$(,)?) => {
$crate::math::random_rng($s..$e)
};
}
#[macro_export]
macro_rules! noise {
($x:expr$(,)?) => {
$crate::math::noise([$x])
};
($x:expr, $y:expr$(,)?) => {
$crate::math::noise([$x, $y])
};
($x:expr, $y:expr, $z:expr$(,)?) => {
$crate::math::noise([$x, $y, $z])
};
}
pub fn map<T>(value: T, start1: T, end1: T, start2: T, end2: T) -> T
where
T: NumCast + Into<f64> + PartialOrd + Copy,
{
let default = end2;
let start1 = start1.into();
let end1 = end1.into();
let start2 = start2.into();
let end2 = end2.into();
let value = value.into();
let new_val = ((value - start1) / (end1 - start1)).mul_add(end2 - start2, start2);
NumCast::from(new_val.clamp(start2, end2)).unwrap_or(default)
}
pub fn lerp<T>(start: T, end: T, amount: T) -> T
where
T: Num + Copy + PartialOrd,
{
(T::one() - amount) * start + amount * end
}
pub fn lerp_map<T>(start1: T, end1: T, start2: T, end2: T) -> Vec<T>
where
T: Num + NumCast + Copy + PartialOrd + AddAssign,
{
if start1 == end1 {
vec![start2]
} else {
let size: usize = NumCast::from(end1 - start1).unwrap_or(4);
let mut values = Vec::with_capacity(size);
let a = (end2 - start2) / (end1 - start1);
let mut d = start2;
let mut i = start1;
while i <= end1 {
values.push(d);
d += a;
i += T::one();
}
values
}
}