use crate::{
gradient,
math::vectors::{Vector, Vector2, Vector3, Vector4},
permutationtable::NoiseHasher,
};
use num_traits::{Float, NumCast};
fn grad1(hash: u8) -> f64 {
let h = hash & 15;
let gx = (1 + (h & 7)) as f64; match h & 8 {
0 => -gx,
1 => gx, _ => unreachable!(),
}
}
fn skew_factor<F>(n: usize) -> F
where
F: Float,
{
let n: F = NumCast::from(n).unwrap();
((n + F::one()).sqrt() - F::one()) / n
}
fn unskew_factor<F>(n: usize) -> F
where
F: Float,
{
let n: F = NumCast::from(n).unwrap();
(F::one() - (F::one() / (n + F::one()).sqrt())) / n
}
#[inline(always)]
pub fn simplex_1d<NH>(x: f64, hasher: &NH) -> (f64, f64)
where
NH: NoiseHasher + ?Sized,
{
let cell = x.floor() as isize;
let near_distance = x - cell as f64;
let far_distance = near_distance - 1.0;
let gi0 = hasher.hash(&[cell]);
let gi1 = hasher.hash(&[cell + 1]);
struct SurfletComponents {
value: f64,
t: f64,
t2: f64,
t4: f64,
gradient: f64,
x2: f64,
}
fn surflet(gradient_index: usize, x: f64) -> SurfletComponents {
let x2 = x * x;
let t = 1.0 - x2;
let gradient = grad1((gradient_index % 0xff) as u8);
let t2 = t * t;
let t4 = t2 * t2;
let value = t4 * gradient * x;
SurfletComponents {
value,
t,
t2,
t4,
gradient,
x2,
}
}
let corner0 = surflet(gi0, near_distance);
let corner1 = surflet(gi1, far_distance);
let noise = 0.395 * (corner0.value + corner1.value);
let mut dnoise_dx = corner0.t2 * corner0.t * corner0.gradient * corner0.x2;
dnoise_dx += corner1.t2 * corner1.t * corner1.gradient * corner1.x2;
dnoise_dx *= -8.0;
dnoise_dx += corner0.t4 * corner0.gradient + corner1.t4 * corner1.gradient;
dnoise_dx *= 0.395;
(noise, dnoise_dx)
}
#[inline(always)]
pub fn simplex_2d<NH>(point: [f64; 2], hasher: &NH) -> (f64, [f64; 2])
where
NH: NoiseHasher + ?Sized,
{
let f2: f64 = skew_factor(2);
let g2: f64 = unskew_factor(2);
let point = Vector2::from(point);
let skew = point.sum() * f2;
let skewed = point + Vector2::broadcast(skew);
let cell: Vector2<isize> = skewed.floor().numcast().unwrap();
let unskew: f64 = cell.sum() as f64 * g2;
let unskewed = cell.numcast().unwrap() - Vector2::broadcast(unskew);
let distance = point - unskewed;
let offset = if distance.x > distance.y {
Vector2::from([1, 0])
} else {
Vector2::from([0, 1])
};
let distance1 = distance - offset.numcast().unwrap() + Vector2::broadcast(g2);
let distance2 = distance - Vector2::broadcast(1.0 + 2.0 * g2);
let gi0 = hasher.hash(&cell.into_array());
let gi1 = hasher.hash(&(cell + distance1.numcast().unwrap()).into_array());
let gi2 = hasher.hash(&(cell + Vector2::one()).into_array());
struct SurfletComponents {
value: f64,
t: f64,
t2: f64,
t4: f64,
gradient: Vector2<f64>,
}
impl SurfletComponents {
fn zeros() -> Self {
Self {
value: 0.0,
t: 0.0,
t2: 0.0,
t4: 0.0,
gradient: Vector2::zero(),
}
}
}
fn surflet(gradient_index: usize, point: Vector2<f64>) -> SurfletComponents {
let t = 0.5 - point.magnitude_squared();
if t > 0.0 {
let gradient = Vector2::from(gradient::grad2(gradient_index));
let t2 = t * t;
let t4 = t2 * t2;
SurfletComponents {
value: t4 * gradient.dot(point),
t,
t2,
t4,
gradient,
}
} else {
SurfletComponents::zeros()
}
}
let corner0 = surflet(gi0, distance);
let corner1 = surflet(gi1, distance1);
let corner2 = surflet(gi2, distance2);
let noise = 40.0 * (corner0.value + corner1.value + corner2.value);
let temp0 = corner0.t2 * corner0.t * corner0.gradient.dot(distance);
let mut dnoise = distance + Vector2::broadcast(temp0);
let temp1 = corner1.t2 * corner1.t * corner1.gradient.dot(distance1);
dnoise += distance1 * temp1;
let temp2 = corner2.t2 * corner2.t * corner2.gradient.dot(distance2);
dnoise += distance2 * temp2;
dnoise *= -8.0;
dnoise += corner0.gradient * corner0.t4
+ corner1.gradient * corner1.t4
+ corner2.gradient * corner2.t4;
dnoise *= 40.0;
(noise, dnoise.into())
}
#[inline(always)]
pub fn simplex_3d<NH>(point: [f64; 3], hasher: &NH) -> (f64, [f64; 3])
where
NH: NoiseHasher + ?Sized,
{
let f3: f64 = skew_factor(3);
let g3: f64 = unskew_factor(3);
let point = Vector3::from(point);
let skew = point.sum() * f3;
let skewed = point + Vector3::broadcast(skew);
let cell: Vector3<isize> = skewed.floor().numcast().unwrap();
let unskew = cell.sum() as f64 * g3;
let unskewed = cell.numcast().unwrap() - Vector3::broadcast(unskew);
let distance = point - unskewed;
let (order1, order2): (Vector3<isize>, Vector3<isize>) = if distance.x >= distance.y {
if distance.y >= distance.z {
(Vector3::new(1, 0, 0), Vector3::new(1, 1, 0))
} else if distance.x >= distance.z {
(Vector3::new(1, 0, 0), Vector3::new(1, 0, 1))
} else {
(Vector3::new(0, 0, 1), Vector3::new(1, 0, 1))
}
} else {
if distance.y < distance.z {
(Vector3::new(0, 0, 1), Vector3::new(0, 1, 1))
} else if distance.x < distance.z {
(Vector3::new(0, 1, 0), Vector3::new(0, 1, 1))
} else {
(Vector3::new(0, 1, 0), Vector3::new(1, 1, 0))
}
};
let offset1 = distance - order1.numcast().unwrap() + Vector3::broadcast(g3);
let offset2 = distance - order2.numcast().unwrap() + Vector3::broadcast(2.0 * g3);
let offset3 = distance - Vector3::one() + Vector3::broadcast(3.0 * g3);
let gi0 = hasher.hash(&cell.into_array());
let gi1 = hasher.hash(&(cell + order1).into_array());
let gi2 = hasher.hash(&(cell + order2).into_array());
let gi3 = hasher.hash(&(cell + Vector3::one()).into_array());
struct SurfletComponents {
value: f64,
t: f64,
t2: f64,
t4: f64,
gradient: Vector3<f64>,
}
impl SurfletComponents {
fn zeros() -> Self {
Self {
value: 0.0,
t: 0.0,
t2: 0.0,
t4: 0.0,
gradient: Vector3::zero(),
}
}
}
fn surflet(gradient_index: usize, point: Vector3<f64>) -> SurfletComponents {
let t = 0.5 - point.magnitude_squared();
if t > 0.0 {
let gradient = Vector3::from(gradient::grad3(gradient_index));
let t2 = t * t;
let t4 = t2 * t2;
SurfletComponents {
value: t4 * gradient.dot(point),
t,
t2,
t4,
gradient,
}
} else {
SurfletComponents::zeros()
}
}
let corner0 = surflet(gi0, distance);
let corner1 = surflet(gi1, offset1);
let corner2 = surflet(gi2, offset2);
let corner3 = surflet(gi3, offset3);
let noise = 28.0 * (corner0.value + corner1.value + corner2.value + corner3.value);
let temp0 = corner0.t2 * corner0.t * corner0.gradient.dot(distance);
let mut dnoise = distance * temp0;
let temp1 = corner1.t2 * corner1.t * corner1.gradient.dot(offset1);
dnoise += offset1 * temp1;
let temp2 = corner2.t2 * corner2.t * corner2.gradient.dot(offset2);
dnoise += offset2 * temp2;
let temp3 = corner3.t2 * corner3.t * corner3.gradient.dot(offset3);
dnoise += offset3 * temp3;
dnoise *= -8.0;
dnoise += corner0.gradient * corner0.t4
+ corner1.gradient * corner1.t4
+ corner2.gradient * corner2.t4
+ corner3.gradient * corner3.t4;
dnoise *= 28.0;
(noise, dnoise.into())
}
#[inline(always)]
pub fn simplex_4d<NH>(point: [f64; 4], hasher: &NH) -> (f64, [f64; 4])
where
NH: NoiseHasher + ?Sized,
{
let f4: f64 = skew_factor(4);
let g4: f64 = unskew_factor(4);
let point = Vector4::from(point);
let skew = point.sum() * f4;
let skewed = point + Vector4::broadcast(skew);
let cell: Vector4<isize> = skewed.numcast().unwrap();
let unskew = cell.sum() as f64 * g4;
let unskewed = cell.numcast().unwrap() - Vector4::broadcast(unskew);
let distance = point - unskewed;
let c1 = if distance.x > distance.y { 32 } else { 0 };
let c2 = if distance.x > distance.z { 16 } else { 0 };
let c3 = if distance.y > distance.z { 8 } else { 0 };
let c4 = if distance.x > distance.w { 4 } else { 0 };
let c5 = if distance.y > distance.w { 2 } else { 0 };
let c6 = if distance.z > distance.w { 1 } else { 0 };
let c = c1 | c2 | c3 | c4 | c5 | c6;
let [i1, j1, k1, l1]: [isize; 4]; let [i2, j2, k2, l2]: [isize; 4]; let [i3, j3, k3, l3]: [isize; 4];
i1 = if SIMPLEX[c][0] >= 3 { 1 } else { 0 };
j1 = if SIMPLEX[c][1] >= 3 { 1 } else { 0 };
k1 = if SIMPLEX[c][2] >= 3 { 1 } else { 0 };
l1 = if SIMPLEX[c][3] >= 3 { 1 } else { 0 };
i2 = if SIMPLEX[c][0] >= 2 { 1 } else { 0 };
j2 = if SIMPLEX[c][1] >= 2 { 1 } else { 0 };
k2 = if SIMPLEX[c][2] >= 2 { 1 } else { 0 };
l2 = if SIMPLEX[c][3] >= 2 { 1 } else { 0 };
i3 = if SIMPLEX[c][0] >= 1 { 1 } else { 0 };
j3 = if SIMPLEX[c][1] >= 1 { 1 } else { 0 };
k3 = if SIMPLEX[c][2] >= 1 { 1 } else { 0 };
l3 = if SIMPLEX[c][3] >= 1 { 1 } else { 0 };
let order1 = Vector4::new(i1, j1, k1, l1);
let order2 = Vector4::new(i2, j2, k2, l2);
let order3 = Vector4::new(i3, j3, k3, l3);
let offset1 = distance - order1.numcast().unwrap() + Vector4::broadcast(g4);
let offset2 = distance - order2.numcast().unwrap() + Vector4::broadcast(2.0 * g4);
let offset3 = distance - order3.numcast().unwrap() + Vector4::broadcast(3.0 * g4);
let offset4 = distance - Vector4::one() + Vector4::broadcast(4.0 * g4);
let gi0 = hasher.hash(&cell.into_array());
let gi1 = hasher.hash(&(cell + order1).into_array());
let gi2 = hasher.hash(&(cell + order2).into_array());
let gi3 = hasher.hash(&(cell + order2).into_array());
let gi4 = hasher.hash(&(cell + Vector4::one()).into_array());
struct SurfletComponents {
value: f64,
t: f64,
t2: f64,
t4: f64,
gradient: Vector4<f64>,
}
impl SurfletComponents {
fn zeros() -> Self {
Self {
value: 0.0,
t: 0.0,
t2: 0.0,
t4: 0.0,
gradient: Vector4::zero(),
}
}
}
fn surflet(gradient_index: usize, point: Vector4<f64>) -> SurfletComponents {
let t = 0.6 - point.magnitude_squared();
if t > 0.0 {
let gradient = Vector4::from(gradient::grad4(gradient_index));
let t2 = t * t;
let t4 = t2 * t2;
SurfletComponents {
value: t4 * gradient.dot(point),
t,
t2,
t4,
gradient,
}
} else {
SurfletComponents::zeros()
}
}
let corner0 = surflet(gi0, distance);
let corner1 = surflet(gi1, offset1);
let corner2 = surflet(gi2, offset2);
let corner3 = surflet(gi3, offset3);
let corner4 = surflet(gi4, offset4);
let noise =
27.0 * (corner0.value + corner1.value + corner2.value + corner3.value + corner4.value);
let temp0 = corner0.t2 * corner0.t * (corner0.gradient.dot(distance));
let mut dnoise = distance * temp0;
let temp1 = corner1.t2 * corner1.t * (corner1.gradient.dot(offset1));
dnoise += offset1 * temp1;
let temp2 = corner2.t2 * corner2.t * (corner2.gradient.dot(offset2));
dnoise += offset2 * temp2;
let temp3 = corner3.t2 * corner3.t * (corner3.gradient.dot(offset3));
dnoise += offset3 * temp3;
let temp4 = corner4.t2 * corner4.t * (corner4.gradient.dot(offset4));
dnoise += offset4 * temp4;
dnoise *= -8.0;
dnoise += corner0.gradient * corner0.t4
+ corner1.gradient * corner1.t4
+ corner2.gradient * corner2.t4
+ corner3.gradient * corner3.t4
+ corner4.gradient * corner4.t4;
dnoise *= 28.0;
(noise, dnoise.into())
}
#[rustfmt::skip]
const SIMPLEX: [[u8; 4]; 64] = [
[0, 1, 2, 3], [0, 1, 3, 2], [0, 0, 0, 0], [0, 2, 3, 1], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [1, 2, 3, 0],
[0, 2, 1, 3], [0, 0, 0, 0], [0, 3, 1, 2], [0, 3, 2, 1], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [1, 3, 2, 0],
[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0],
[1, 2, 0, 3], [0, 0, 0, 0], [1, 3, 0, 2], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [2, 3, 0, 1], [2, 3, 1, 0],
[1, 0, 2, 3], [1, 0, 3, 2], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [2, 0, 3, 1], [0, 0, 0, 0], [2, 1, 3, 0],
[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0],
[2, 0, 1, 3], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [3, 0, 1, 2], [3, 0, 2, 1], [0, 0, 0, 0], [3, 1, 2, 0],
[2, 1, 0, 3], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [3, 1, 0, 2], [0, 0, 0, 0], [3, 2, 0, 1], [3, 2, 1, 0],
];