diff --git a/examples/power.rs b/examples/power.rs index 399cf51e..d96e75aa 100644 --- a/examples/power.rs +++ b/examples/power.rs @@ -1,6 +1,6 @@ extern crate noise; -use noise::{utils::*, Perlin, Power, Seedable}; +use noise::{utils::*, Perlin, Power}; fn main() { let perlin1 = Perlin::default(); diff --git a/src/core/open_simplex.rs b/src/core/open_simplex.rs index 933b5fd8..466ddbe6 100644 --- a/src/core/open_simplex.rs +++ b/src/core/open_simplex.rs @@ -4,18 +4,20 @@ use crate::{ permutationtable::NoiseHasher, }; -pub fn open_simplex_2d(point: [f64; 2], hasher: &impl NoiseHasher) -> f64 { +pub fn open_simplex_2d(point: [f64; 2], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ const STRETCH_CONSTANT: f64 = -0.211_324_865_405_187; //(1/sqrt(2+1)-1)/2; const SQUISH_CONSTANT: f64 = 0.366_025_403_784_439; //(sqrt(2+1)-1)/2; const NORM_CONSTANT: f64 = 1.0 / 14.0; - fn gradient(hasher: &impl NoiseHasher, vertex: Vector2, pos: Vector2) -> f64 { - let attn = 2.0 - pos.magnitude_squared(); + fn surflet(index: usize, point: Vector2) -> f64 { + let t = 2.0 - point.magnitude_squared(); - if attn > 0.0 { - let index = hasher.hash(&vertex.numcast().unwrap().into_array()); - let vec = Vector2::from(gradient::grad2(index)); - attn.powi(4) * pos.dot(vec) + if t > 0.0 { + let gradient = Vector2::from(gradient::grad2(index)); + t.powi(4) * point.dot(gradient) } else { 0.0 } @@ -48,9 +50,10 @@ pub fn open_simplex_2d(point: [f64; 2], hasher: &impl NoiseHasher) -> f64 { { let offset = Vector2::new($x, $y); let vertex = stretched_floor + offset; + let index = hasher.hash(&vertex.numcast().unwrap().into_array()); let dpos = rel_pos - (Vector2::broadcast(SQUISH_CONSTANT) * offset.sum()) - offset; - gradient(hasher, vertex, dpos) + surflet(index, dpos) } } ); @@ -82,18 +85,20 @@ pub fn open_simplex_2d(point: [f64; 2], hasher: &impl NoiseHasher) -> f64 { value * NORM_CONSTANT } -pub fn open_simplex_3d(point: [f64; 3], hasher: &impl NoiseHasher) -> f64 { +pub fn open_simplex_3d(point: [f64; 3], hasher: &NH) -> f64 +where + NH: NoiseHasher, +{ const STRETCH_CONSTANT: f64 = -1.0 / 6.0; //(1/Math.sqrt(3+1)-1)/3; const SQUISH_CONSTANT: f64 = 1.0 / 3.0; //(Math.sqrt(3+1)-1)/3; const NORM_CONSTANT: f64 = 1.0 / 14.0; - fn gradient(hasher: &impl NoiseHasher, vertex: Vector3, pos: Vector3) -> f64 { - let attn = 2.0 - pos.magnitude_squared(); + fn surflet(index: usize, point: Vector3) -> f64 { + let t = 2.0 - point.magnitude_squared(); - if attn > 0.0 { - let index = hasher.hash(&vertex.numcast().unwrap().into_array()); - let vec = Vector3::from(gradient::grad3(index)); - attn.powi(4) * pos.dot(vec) + if t > 0.0 { + let gradient = Vector3::from(gradient::grad3(index)); + t.powi(4) * point.dot(gradient) } else { 0.0 } @@ -128,9 +133,10 @@ pub fn open_simplex_3d(point: [f64; 3], hasher: &impl NoiseHasher) -> f64 { { let offset = Vector3::new($x, $y, $z); let vertex = stretched_floor + offset; + let index = hasher.hash(&vertex.numcast().unwrap().into_array()); let dpos = rel_pos - (Vector3::broadcast(SQUISH_CONSTANT) * offset.sum()) - offset; - gradient(hasher, vertex, dpos) + surflet(index, dpos) } } ); @@ -190,20 +196,21 @@ pub fn open_simplex_3d(point: [f64; 3], hasher: &impl NoiseHasher) -> f64 { value * NORM_CONSTANT } -pub fn open_simplex_4d(point: [f64; 4], hasher: &impl NoiseHasher) -> f64 { +pub fn open_simplex_4d(point: [f64; 4], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ const STRETCH_CONSTANT: f64 = -0.138_196_601_125_011; //(Math.sqrt(4+1)-1)/4; const SQUISH_CONSTANT: f64 = 0.309_016_994_374_947; //(Math.sqrt(4+1)-1)/4; const NORM_CONSTANT: f64 = 1.0 / 6.869_909_007_095_662_5; - #[inline(always)] - fn gradient(hasher: &impl NoiseHasher, vertex: Vector4, pos: Vector4) -> f64 { - let attn = 2.0 - pos.magnitude_squared(); + fn surflet(index: usize, point: Vector4) -> f64 { + let t = 2.0 - point.magnitude_squared(); - if attn > 0.0 { - let index = hasher.hash(&vertex.numcast().unwrap().into_array()); - let vec = Vector4::from(gradient::grad4(index)); - attn.powi(4) * pos.dot(vec) + if t > 0.0 { + let gradient = Vector4::from(gradient::grad4(index)); + t.powi(4) * point.dot(gradient) } else { 0.0 } @@ -240,9 +247,10 @@ pub fn open_simplex_4d(point: [f64; 4], hasher: &impl NoiseHasher) -> f64 { { let offset = Vector4::new($x, $y, $z, $w); let vertex = stretched_floor + offset; + let index = hasher.hash(&vertex.numcast().unwrap().into_array()); let dpos = rel_pos - (Vector4::broadcast(SQUISH_CONSTANT) * offset.sum()) - offset; - gradient(hasher, vertex, dpos) + surflet(index, dpos) } } ); diff --git a/src/core/perlin.rs b/src/core/perlin.rs index 88401d44..2938f543 100644 --- a/src/core/perlin.rs +++ b/src/core/perlin.rs @@ -5,171 +5,218 @@ use crate::{ }, permutationtable::NoiseHasher, }; -use core::f64; - -#[inline(always)] -pub fn perlin_2d(point: [f64; 2], hasher: &dyn NoiseHasher) -> f64 { - // Unscaled range of linearly interpolated perlin noise should be (-sqrt(N)/2, sqrt(N)/2). - // Need to invert this value and multiply the unscaled result by the value to get a scaled - // range of (-1, 1). - // - // 1/(sqrt(N)/2), N=2 -> sqrt(2) - const SCALE_FACTOR: f64 = f64::consts::SQRT_2; +use num_traits::{Float, NumCast}; + +// Scale Factor +// +// 1 2 +// F = ------------- = --------- +// sqrt(N) / 2 sqrt(N) +fn scale_factor(n: usize) -> F +where + F: Float, +{ + let n: F = NumCast::from(n).unwrap(); + + F::from(2.0).unwrap() / n.sqrt() +} - let point = Vector2::from(point); +macro_rules! perlin_2d { + ($name:ident, $f:ty) => { + pub fn $name(point: [$f; 2], hasher: &NH) -> $f + where + NH: NoiseHasher + ?Sized, + { + // Unscaled range of linearly interpolated perlin noise should be (-sqrt(N)/2, sqrt(N)/2). + // Need to invert this value and multiply the unscaled result by the value to get a scaled + // range of (-1, 1). + // + // 1/(sqrt(N)/2), N=2 -> sqrt(2) + let scale_factor: $f = scale_factor(2); + + let point = Vector2::from(point); #[inline(always)] - #[rustfmt::skip] - fn gradient_dot_v(perm: usize, point: Vector2) -> f64 { - let [x, y] = point.into_array(); - - match perm & 0b11 { - 0 => x + y, // ( 1, 1) - 1 => -x + y, // (-1, 1) - 2 => x - y, // ( 1, -1) - 3 => -x - y, // (-1, -1) - _ => unreachable!(), - } - } - - let floored = point.floor(); - let corner: Vector2 = floored.numcast().unwrap(); - let distance = point - floored; - - macro_rules! call_gradient( - ($x:expr, $y:expr) => { - { - let offset = Vector2::new($x, $y); - gradient_dot_v( - hasher.hash(&(corner + offset).into_array()), - distance - offset.numcast().unwrap() - ) + #[rustfmt::skip] + fn gradient_dot_v(perm: usize, point: Vector2<$f>) -> $f { + let [x, y] = point.into_array(); + + match perm & 0b11 { + 0 => x + y, // ( 1, 1) + 1 => -x + y, // (-1, 1) + 2 => x - y, // ( 1, -1) + 3 => -x - y, // (-1, -1) + _ => unreachable!(), + } } - } - ); - - let g00 = call_gradient!(0, 0); - let g10 = call_gradient!(1, 0); - let g01 = call_gradient!(0, 1); - let g11 = call_gradient!(1, 1); - - let [u, v] = distance.map_quintic().into_array(); - - let unscaled_result = bilinear_interpolation(u, v, g00, g01, g10, g11); - - let scaled_result = unscaled_result * SCALE_FACTOR; - - // At this point, we should be really damn close to the (-1, 1) range, but some float errors - // could have accumulated, so let's just clamp the results to (-1, 1) to cut off any - // outliers and return it. - scaled_result.clamp(-1.0, 1.0) -} -#[inline(always)] -fn bilinear_interpolation(u: f64, v: f64, g00: f64, g01: f64, g10: f64, g11: f64) -> f64 { - let k0 = g00; - let k1 = g10 - g00; - let k2 = g01 - g00; - let k3 = g00 + g11 - g10 - g01; - - k0 + k1 * u + k2 * v + k3 * u * v + let floored = point.floor(); + let corner: Vector2 = floored.numcast().unwrap(); + let distance = point - floored; + + macro_rules! call_gradient( + ($x:expr, $y:expr) => { + { + let offset = Vector2::new($x, $y); + gradient_dot_v( + hasher.hash(&(corner + offset).into_array()), + distance - offset.numcast().unwrap() + ) + } + } + ); + + let g00 = call_gradient!(0, 0); + let g10 = call_gradient!(1, 0); + let g01 = call_gradient!(0, 1); + let g11 = call_gradient!(1, 1); + + let [u, v] = distance.map_quintic().into_array(); + + let unscaled_result = { + let k0 = g00; + let k1 = g10 - g00; + let k2 = g01 - g00; + let k3 = g00 + g11 - g10 - g01; + + k0 + k1 * u + k2 * v + k3 * u * v + }; + + let scaled_result = unscaled_result * scale_factor; + + // At this point, we should be really damn close to the (-1, 1) range, but some float errors + // could have accumulated, so let's just clamp the results to (-1, 1) to cut off any + // outliers and return it. + scaled_result.clamp(-1.0, 1.0) + } + }; } -#[inline(always)] -pub fn perlin_3d(point: [f64; 3], hasher: &dyn NoiseHasher) -> f64 { - // Unscaled range of linearly interpolated perlin noise should be (-sqrt(N)/2, sqrt(N)/2). - // Need to invert this value and multiply the unscaled result by the value to get a scaled - // range of (-1, 1). - // - // 1/(sqrt(N)/2), N=3 -> 2/sqrt(3) - // sqrt() is not a const function, so use a high-precision value instead. - // TODO: Replace fixed const values with const fn if sqrt() ever becomes a const function. - // 2/sqrt(3) = 1.1547005383792515290182975610039149112952035025402537520372046529 - const SCALE_FACTOR: f64 = 1.154_700_538_379_251_5; +perlin_2d!(perlin_2d_f32, f32); +perlin_2d!(perlin_2d_f64, f64); + +// #[inline(always)] +// fn bilinear_interpolation(u: f64, v: f64, g00: f64, g01: f64, g10: f64, g11: f64) -> f64 { +// let k0 = g00; +// let k1 = g10 - g00; +// let k2 = g01 - g00; +// let k3 = g00 + g11 - g10 - g01; +// +// k0 + k1 * u + k2 * v + k3 * u * v +// } + +macro_rules! perlin_3d { + ($name:ident, $f:ty) => { + pub fn $name(point: [$f; 3], hasher: &NH) -> $f + where + NH: NoiseHasher + ?Sized, + { + // Unscaled range of linearly interpolated perlin noise should be (-sqrt(N)/2, sqrt(N)/2). + // Need to invert this value and multiply the unscaled result by the value to get a scaled + // range of (-1, 1). + // + // 1/(sqrt(N)/2), N=3 -> 2/sqrt(3) + // sqrt() is not a const function, so use a high-precision value instead. + // TODO: Replace fixed const values with const fn if sqrt() ever becomes a const function. + // 2/sqrt(3) = 1.1547005383792515290182975610039149112952035025402537520372046529 + let scale_factor: $f = scale_factor(3); + + let point = Vector3::from(point); - let point = Vector3::from(point); - - #[inline(always)] #[rustfmt::skip] - fn gradient_dot_v(perm: usize, point: Vector3) -> f64 { - let [x, y, z] = point.into_array(); - - match perm & 0b1111 { - 0 | 12 => x + y , // ( 1, 1, 0) - 1 | 13 => -x + y , // (-1, 1, 0) - 2 => x - y , // ( 1, -1, 0) - 3 => -x - y , // (-1, -1, 0) - 4 => x + z, // ( 1, 0, 1) - 5 => -x + z, // (-1, 0, 1) - 6 => x - z, // ( 1, 0, -1) - 7 => -x - z, // (-1, 0, -1) - 8 => y + z, // ( 0, 1, 1) - 9 | 14 => -y + z, // ( 0, -1, 1) - 10 => y - z, // ( 0, 1, -1) - 11 | 15 => -y - z, // ( 0, -1, -1) - _ => unreachable!(), - } - } - - let floored = point.floor(); - let corner: Vector3 = floored.numcast().unwrap(); - let distance = point - floored; - - macro_rules! call_gradient( - ($x:expr, $y:expr, $z:expr) => { - { - let offset = Vector3::new($x, $y, $z); - gradient_dot_v( - hasher.hash(&(corner + offset).into_array()), - distance - offset.numcast().unwrap() - ) + fn gradient_dot_v(perm: usize, point: Vector3<$f>) -> $f { + let [x, y, z] = point.into_array(); + + match perm & 0b1111 { + 0 | 12 => x + y , // ( 1, 1, 0) + 1 | 13 => -x + y , // (-1, 1, 0) + 2 => x - y , // ( 1, -1, 0) + 3 => -x - y , // (-1, -1, 0) + 4 => x + z, // ( 1, 0, 1) + 5 => -x + z, // (-1, 0, 1) + 6 => x - z, // ( 1, 0, -1) + 7 => -x - z, // (-1, 0, -1) + 8 => y + z, // ( 0, 1, 1) + 9 | 14 => -y + z, // ( 0, -1, 1) + 10 => y - z, // ( 0, 1, -1) + 11 | 15 => -y - z, // ( 0, -1, -1) + _ => unreachable!(), + } } + + let floored = point.floor(); + let corner: Vector3 = floored.numcast().unwrap(); + let distance = point - floored; + + macro_rules! call_gradient( + ($x:expr, $y:expr, $z:expr) => { + { + let offset = Vector3::new($x, $y, $z); + gradient_dot_v( + hasher.hash(&(corner + offset).into_array()), + distance - offset.numcast().unwrap() + ) + } + } + ); + + let g000 = call_gradient!(0, 0, 0); + let g100 = call_gradient!(1, 0, 0); + let g010 = call_gradient!(0, 1, 0); + let g110 = call_gradient!(1, 1, 0); + let g001 = call_gradient!(0, 0, 1); + let g101 = call_gradient!(1, 0, 1); + let g011 = call_gradient!(0, 1, 1); + let g111 = call_gradient!(1, 1, 1); + + let [a, b, c] = distance.map_quintic().into_array(); + + let k0 = g000; + let k1 = g100 - g000; + let k2 = g010 - g000; + let k3 = g001 - g000; + let k4 = g000 + g110 - g100 - g010; + let k5 = g000 + g101 - g100 - g001; + let k6 = g000 + g011 - g010 - g001; + let k7 = g100 + g010 + g001 + g111 - g000 - g110 - g101 - g011; + + let unscaled_result = k0 + + k1 * a + + k2 * b + + k3 * c + + k4 * a * b + + k5 * a * c + + k6 * b * c + + k7 * a * b * c; + + let scaled_result = unscaled_result * scale_factor; + + // At this point, we should be really damn close to the (-1, 1) range, but some float errors + // could have accumulated, so let's just clamp the results to (-1, 1) to cut off any + // outliers and return it. + scaled_result.clamp(-1.0, 1.0) } - ); - - let g000 = call_gradient!(0, 0, 0); - let g100 = call_gradient!(1, 0, 0); - let g010 = call_gradient!(0, 1, 0); - let g110 = call_gradient!(1, 1, 0); - let g001 = call_gradient!(0, 0, 1); - let g101 = call_gradient!(1, 0, 1); - let g011 = call_gradient!(0, 1, 1); - let g111 = call_gradient!(1, 1, 1); - - let [a, b, c] = distance.map_quintic().into_array(); - - let k0 = g000; - let k1 = g100 - g000; - let k2 = g010 - g000; - let k3 = g001 - g000; - let k4 = g000 + g110 - g100 - g010; - let k5 = g000 + g101 - g100 - g001; - let k6 = g000 + g011 - g010 - g001; - let k7 = g100 + g010 + g001 + g111 - g000 - g110 - g101 - g011; - - let unscaled_result = - k0 + k1 * a + k2 * b + k3 * c + k4 * a * b + k5 * a * c + k6 * b * c + k7 * a * b * c; - - let scaled_result = unscaled_result * SCALE_FACTOR; - - // At this point, we should be really damn close to the (-1, 1) range, but some float errors - // could have accumulated, so let's just clamp the results to (-1, 1) to cut off any - // outliers and return it. - scaled_result.clamp(-1.0, 1.0) + }; } -#[inline(always)] -pub fn perlin_4d(point: [f64; 4], hasher: &dyn NoiseHasher) -> f64 { - // Unscaled range of linearly interpolated perlin noise should be (-sqrt(N)/2, sqrt(N)/2). - // Need to invert this value and multiply the unscaled result by the value to get a scaled - // range of (-1, 1). - const SCALE_FACTOR: f64 = 1.0; // 1/(sqrt(N)/2), N=4 -> 2/sqrt(4) -> 2/2 -> 1 +perlin_3d!(perlin_3d_f32, f32); +perlin_3d!(perlin_3d_f64, f64); - let point = Vector4::from(point); +macro_rules! perlin_4d { + ($name:ident, $f:ty) => { + pub fn $name(point: [$f; 4], hasher: &NH) -> $f + where + NH: NoiseHasher + ?Sized, + { + // Unscaled range of linearly interpolated perlin noise should be (-sqrt(N)/2, sqrt(N)/2). + // Need to invert this value and multiply the unscaled result by the value to get a scaled + // range of (-1, 1). + let scale_factor: $f = 1.0; // 1/(sqrt(N)/2), N=4 -> 2/sqrt(4) -> 2/2 -> 1 + + let point = Vector4::from(point); - #[inline(always)] #[rustfmt::skip] - fn gradient_dot_v(perm: usize, point: Vector4) -> f64 { + fn gradient_dot_v(perm: usize, point: Vector4<$f>) -> $f { let [x, y, z, w] = point.into_array(); match perm & 0b11111 { @@ -205,79 +252,84 @@ pub fn perlin_4d(point: [f64; 4], hasher: &dyn NoiseHasher) -> f64 { } } - let floored = point.floor(); - let corner: Vector4 = floored.numcast().unwrap(); - let distance = point - floored; - - macro_rules! call_gradient( - ($x:expr, $y:expr, $z:expr, $w:expr) => { - { - let offset = Vector4::new($x, $y, $z, $w); - gradient_dot_v( - hasher.hash(&(corner + offset).into_array()), - distance - offset.numcast().unwrap() - ) - } + let floored = point.floor(); + let corner: Vector4 = floored.numcast().unwrap(); + let distance = point - floored; + + macro_rules! call_gradient( + ($x:expr, $y:expr, $z:expr, $w:expr) => { + { + let offset = Vector4::new($x, $y, $z, $w); + gradient_dot_v( + hasher.hash(&(corner + offset).into_array()), + distance - offset.numcast().unwrap() + ) + } + } + ); + + let g0000 = call_gradient!(0, 0, 0, 0); + let g1000 = call_gradient!(1, 0, 0, 0); + let g0100 = call_gradient!(0, 1, 0, 0); + let g1100 = call_gradient!(1, 1, 0, 0); + let g0010 = call_gradient!(0, 0, 1, 0); + let g1010 = call_gradient!(1, 0, 1, 0); + let g0110 = call_gradient!(0, 1, 1, 0); + let g1110 = call_gradient!(1, 1, 1, 0); + let g0001 = call_gradient!(0, 0, 0, 1); + let g1001 = call_gradient!(1, 0, 0, 1); + let g0101 = call_gradient!(0, 1, 0, 1); + let g1101 = call_gradient!(1, 1, 0, 1); + let g0011 = call_gradient!(0, 0, 1, 1); + let g1011 = call_gradient!(1, 0, 1, 1); + let g0111 = call_gradient!(0, 1, 1, 1); + let g1111 = call_gradient!(1, 1, 1, 1); + + let [a, b, c, d] = distance.map_quintic().into_array(); + + let k0 = g0000; + let k1 = g1000 - g0000; + let k2 = g0100 - g0000; + let k3 = g0010 - g0000; + let k4 = g0001 - g0000; + let k5 = g0000 + g1100 - g1000 - g0100; + let k6 = g0000 + g1010 - g1000 - g0010; + let k7 = g0000 + g1001 - g1000 - g0001; + let k8 = g0000 + g0110 - g0100 - g0010; + let k9 = g0000 + g0101 - g0100 - g0001; + let k10 = g0000 + g0011 - g0010 - g0001; + let k11 = g1110 + g1000 + g0100 + g0010 - g0000 - g0111 - g1011 - g1101; + let k12 = g1101 + g1000 + g0100 + g0001 - g0000 - g0111 - g1011 - g1110; + let k13 = g1011 + g1000 + g0010 + g0001 - g0000 - g0111 - g1101 - g1110; + let k14 = g0111 + g0100 + g0010 + g0001 - g0000 - g1011 - g1101 - g1110; + let k15 = g1111 + g1000 + g0100 + g0010 + g0001 - g0000 - g0111 - g1011 - g1101 - g1110; + + let unscaled_result = k0 + + k1 * a + + k2 * b + + k3 * c + + k4 * d + + k5 * a * b + + k6 * a * c + + k7 * a * d + + k8 * b * c + + k9 * b * d + + k10 * c * d + + k11 * a * b * c + + k12 * a * b * d + + k13 * a * c * d + + k14 * b * c * d + + k15 * a * b * c * d; + + let scaled_result = unscaled_result * scale_factor; + + // At this point, we should be really damn close to the (-1, 1) range, but some float errors + // could have accumulated, so let's just clamp the results to (-1, 1) to cut off any + // outliers and return it. + scaled_result.clamp(-1.0, 1.0) } - ); - - let g0000 = call_gradient!(0, 0, 0, 0); - let g1000 = call_gradient!(1, 0, 0, 0); - let g0100 = call_gradient!(0, 1, 0, 0); - let g1100 = call_gradient!(1, 1, 0, 0); - let g0010 = call_gradient!(0, 0, 1, 0); - let g1010 = call_gradient!(1, 0, 1, 0); - let g0110 = call_gradient!(0, 1, 1, 0); - let g1110 = call_gradient!(1, 1, 1, 0); - let g0001 = call_gradient!(0, 0, 0, 1); - let g1001 = call_gradient!(1, 0, 0, 1); - let g0101 = call_gradient!(0, 1, 0, 1); - let g1101 = call_gradient!(1, 1, 0, 1); - let g0011 = call_gradient!(0, 0, 1, 1); - let g1011 = call_gradient!(1, 0, 1, 1); - let g0111 = call_gradient!(0, 1, 1, 1); - let g1111 = call_gradient!(1, 1, 1, 1); - - let [a, b, c, d] = distance.map_quintic().into_array(); - - let k0 = g0000; - let k1 = g1000 - g0000; - let k2 = g0100 - g0000; - let k3 = g0010 - g0000; - let k4 = g0001 - g0000; - let k5 = g0000 + g1100 - g1000 - g0100; - let k6 = g0000 + g1010 - g1000 - g0010; - let k7 = g0000 + g1001 - g1000 - g0001; - let k8 = g0000 + g0110 - g0100 - g0010; - let k9 = g0000 + g0101 - g0100 - g0001; - let k10 = g0000 + g0011 - g0010 - g0001; - let k11 = g1110 + g1000 + g0100 + g0010 - g0000 - g0111 - g1011 - g1101; - let k12 = g1101 + g1000 + g0100 + g0001 - g0000 - g0111 - g1011 - g1110; - let k13 = g1011 + g1000 + g0010 + g0001 - g0000 - g0111 - g1101 - g1110; - let k14 = g0111 + g0100 + g0010 + g0001 - g0000 - g1011 - g1101 - g1110; - let k15 = g1111 + g1000 + g0100 + g0010 + g0001 - g0000 - g0111 - g1011 - g1101 - g1110; - - let unscaled_result = k0 - + k1 * a - + k2 * b - + k3 * c - + k4 * d - + k5 * a * b - + k6 * a * c - + k7 * a * d - + k8 * b * c - + k9 * b * d - + k10 * c * d - + k11 * a * b * c - + k12 * a * b * d - + k13 * a * c * d - + k14 * b * c * d - + k15 * a * b * c * d; - - let scaled_result = unscaled_result * SCALE_FACTOR; - - // At this point, we should be really damn close to the (-1, 1) range, but some float errors - // could have accumulated, so let's just clamp the results to (-1, 1) to cut off any - // outliers and return it. - scaled_result.clamp(-1.0, 1.0) + }; } + +perlin_4d!(perlin_4d_f32, f32); +perlin_4d!(perlin_4d_f64, f64); diff --git a/src/core/perlin_surflet.rs b/src/core/perlin_surflet.rs index 854ec07c..0768121b 100644 --- a/src/core/perlin_surflet.rs +++ b/src/core/perlin_surflet.rs @@ -5,18 +5,18 @@ use crate::{ }; #[inline(always)] -pub fn perlin_surflet_2d(point: [f64; 2], hasher: &dyn NoiseHasher) -> f64 { +pub fn perlin_surflet_2d(point: [f64; 2], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ const SCALE_FACTOR: f64 = 3.160_493_827_160_493_7; - #[inline(always)] - fn surflet(hasher: &dyn NoiseHasher, corner: Vector2, distance: Vector2) -> f64 { + fn surflet(index: usize, distance: Vector2) -> f64 { let attn: f64 = 1.0 - distance.magnitude_squared(); if attn > 0.0 { - attn.powi(4) - * distance.dot(Vector2::from(gradient::grad2( - hasher.hash(&corner.into_array()), - ))) + let gradient = Vector2::from(gradient::grad2(index)); + attn.powi(4) * distance.dot(gradient) } else { 0.0 } @@ -32,7 +32,8 @@ pub fn perlin_surflet_2d(point: [f64; 2], hasher: &dyn NoiseHasher) -> f64 { ($x:expr, $y:expr) => { { let offset = Vector2::new($x, $y); - surflet(hasher, corner + offset, distance - offset.numcast().unwrap()) + let index = hasher.hash(&(corner + offset).into_array()); + surflet(index, distance - offset.numcast().unwrap()) } } ); @@ -46,18 +47,19 @@ pub fn perlin_surflet_2d(point: [f64; 2], hasher: &dyn NoiseHasher) -> f64 { ((f00 + f10 + f01 + f11) * SCALE_FACTOR).clamp(-1.0, 1.0) } -pub fn perlin_surflet_3d(point: [f64; 3], hasher: &dyn NoiseHasher) -> f64 { +pub fn perlin_surflet_3d(point: [f64; 3], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ const SCALE_FACTOR: f64 = 3.889_855_325_553_107_4; #[inline(always)] - fn surflet(hasher: &dyn NoiseHasher, corner: Vector3, distance: Vector3) -> f64 { + fn surflet(index: usize, distance: Vector3) -> f64 { let attn: f64 = 1.0 - distance.magnitude_squared(); if attn > 0.0 { - attn.powi(4) - * distance.dot(Vector3::from(gradient::grad3( - hasher.hash(&corner.into_array()), - ))) + let gradient = Vector3::from(gradient::grad3(index)); + attn.powi(4) * distance.dot(gradient) } else { 0.0 } @@ -73,7 +75,8 @@ pub fn perlin_surflet_3d(point: [f64; 3], hasher: &dyn NoiseHasher) -> f64 { ($x:expr, $y:expr, $z:expr) => { { let offset = Vector3::new($x, $y, $z); - surflet(hasher, corner + offset, distance - offset.numcast().unwrap()) + let index = hasher.hash(&(corner + offset).into_array()); + surflet(index, distance - offset.numcast().unwrap()) } } ); @@ -91,18 +94,19 @@ pub fn perlin_surflet_3d(point: [f64; 3], hasher: &dyn NoiseHasher) -> f64 { ((f000 + f100 + f010 + f110 + f001 + f101 + f011 + f111) * SCALE_FACTOR).clamp(-1.0, 1.0) } -pub fn perlin_surflet_4d(point: [f64; 4], hasher: &dyn NoiseHasher) -> f64 { +pub fn perlin_surflet_4d(point: [f64; 4], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ const SCALE_FACTOR: f64 = 4.424_369_240_215_691; #[inline(always)] - fn surflet(hasher: &dyn NoiseHasher, corner: Vector4, distance: Vector4) -> f64 { + fn surflet(index: usize, distance: Vector4) -> f64 { let attn: f64 = 1.0 - distance.magnitude_squared(); if attn > 0.0 { - attn.powi(4) - * distance.dot(Vector4::from(gradient::grad4( - hasher.hash(&corner.into_array()), - ))) + let gradient = Vector4::from(gradient::grad4(index)); + attn.powi(4) * distance.dot(gradient) } else { 0.0 } @@ -118,7 +122,8 @@ pub fn perlin_surflet_4d(point: [f64; 4], hasher: &dyn NoiseHasher) -> f64 { ($x:expr, $y:expr, $z:expr, $w:expr) => { { let offset = Vector4::new($x, $y, $z, $w); - surflet(hasher, corner + offset, distance - offset.numcast().unwrap()) + let index = hasher.hash(&(corner + offset).into_array()); + surflet(index, distance - offset.numcast().unwrap()) } } ); diff --git a/src/core/simplex.rs b/src/core/simplex.rs index 3a9a8c57..03a388c7 100644 --- a/src/core/simplex.rs +++ b/src/core/simplex.rs @@ -1,3 +1,26 @@ +//! The simplex noise code was adapted from code by Stefan Gustavson, +//! +//! +//! This is Stefan Gustavson's original copyright notice: +//! +//! /* sdnoise1234, Simplex noise with true analytic
+//! \* derivative in 1D to 4D.
+//! \*
+//! \* Copyright © 2003-2011, Stefan Gustavson
+//! \*
+//! \* Contact: stefan.gustavson@gmail.com
+//! \*
+//! \* This library is public domain software, released by the author
+//! \* into the public domain in February 2011. You may do anything
+//! \* you like with it. You may even remove all attributions,
+//! \* but of course I'd appreciate it if you kept my name somewhere.
+//! \* +//! \* This library is distributed in the hope that it will be useful,
+//! \* but WITHOUT ANY WARRANTY; without even the implied warranty of
+//! \* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+//! \* General Public License for more details.
+//! \*/ + use crate::{ gradient, math::vectors::{Vector, Vector2, Vector3, Vector4}, @@ -5,9 +28,12 @@ use crate::{ }; use num_traits::{Float, NumCast}; -fn grad1(hash: u8) -> f64 { +fn grad1(hash: u8) -> F +where + F: Float, +{ let h = hash & 15; - let gx = (1 + (h & 7)) as f64; // Gradient value is one of 1.0, 2.0, ..., 8.0 + let gx = F::from(1 + (h & 7)).unwrap(); // Gradient value is one of 1.0, 2.0, ..., 8.0 match h & 8 { 0 => -gx, 1 => gx, // Make half of the gradients negative @@ -43,561 +69,573 @@ where (F::one() - (F::one() / (n + F::one()).sqrt())) / n } -/// The simplex noise code was adapted from code by Stefan Gustavson, -/// http://staffwww.itn.liu.se/~stegu/aqsis/aqsis-newnoise/sdnoise1234.c -/// -/// This is Stefan Gustavson's original copyright notice: -/// -/// /* sdnoise1234, Simplex noise with true analytic -/// * derivative in 1D to 4D. -/// * -/// * Copyright © 2003-2011, Stefan Gustavson -/// * -/// * Contact: stefan.gustavson@gmail.com -/// * -/// * This library is public domain software, released by the author -/// * into the public domain in February 2011. You may do anything -/// * you like with it. You may even remove all attributions, -/// * but of course I'd appreciate it if you kept my name somewhere. -/// * -/// * This library is distributed in the hope that it will be useful, -/// * but WITHOUT ANY WARRANTY; without even the implied warranty of -/// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -/// * General Public License for more details. -/// */ -/// -/// 1D Simplex Noise with Derivative -#[inline(always)] -pub fn simplex_1d(x: f64, hasher: &dyn NoiseHasher) -> (f64, f64) { - let cell = x.floor() as isize; - - let near_distance = x - cell as f64; - let far_distance = near_distance - 1.0; - - // Calculate gradient indexes for each corner - 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; - - // if t <= 0.0 { Never happens in 1D: x is always <= 1. - // No influence - // t0 = 0.0; - // } else { - 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, - } - } +macro_rules! simplex_1d { + ($name:ident, $f:ty) => { + /// 1D Simplex Noise with Derivative + pub fn $name(x: $f, hasher: &NH) -> ($f, $f) + where + NH: NoiseHasher + ?Sized, + { + let cell = x.floor() as isize; + + let near_distance = x - cell as $f; + let far_distance = near_distance - 1.0; + + // Calculate gradient indexes for each corner + let gi0 = hasher.hash(&[cell]); + let gi1 = hasher.hash(&[cell + 1]); + + struct SurfletComponents { + value: $f, + t: $f, + t2: $f, + t4: $f, + gradient: $f, + x2: $f, + } - let corner0 = surflet(gi0, near_distance); - let corner1 = surflet(gi1, far_distance); - - // The maximum value of this noise is 8*(3/4)^4 = 2.53125 - // A factor of 0.395 would scale to fit exactly within [-1,1], but - // to better match classic Perlin noise, we scale it down some more. - // ^-- Original note from Gustavson. - - // Since the objective of this library is to be as close to [-1, 1] as possible, we'll use the - // 0.395 scale instead. - let noise = 0.395 * (corner0.value + corner1.value); - - /* Compute derivative according to: - * dnoise_dx = -8.0 * t20 * t0 * x0 * (gx0 * x0) + t40 * gx0; - * dnoise_dx += -8.0 * t21 * t1 * x1 * (gx1 * x1) + t41 * gx1; - */ - 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; /* Scale derivative to match the noise scaling */ - - (noise, dnoise_dx) -} + fn surflet(gradient_index: usize, x: $f) -> SurfletComponents { + let x2 = x * x; + let t = 1.0 - x2; + + // if t <= 0.0 { Never happens in 1D: x is always <= 1. + // No influence + // t0 = 0.0; + // } else { + 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, + } + } -#[inline(always)] -#[inline(always)] -pub fn simplex_2d(point: [f64; 2], hasher: &dyn NoiseHasher) -> (f64, [f64; 2]) { - let f2: f64 = skew_factor(2); - let g2: f64 = unskew_factor(2); - - let point = Vector2::from(point); - - /* Skew the input space to determine which simplex cell we're in */ - let skew = point.sum() * f2; /* Hairy factor for 2D */ - let skewed = point + Vector2::broadcast(skew); - let cell: Vector2 = skewed.floor().numcast().unwrap(); - - let unskew: f64 = cell.sum() as f64 * g2; - // Unskew the cell origin back to (x,y) space - let unskewed = cell.numcast().unwrap() - Vector2::broadcast(unskew); - // The x,y distances from the cell origin - let distance = point - unskewed; - - // For the 2D case, the simplex shape is an equilateral triangle. - // Determine which simplex we are in. - let offset = if distance.x > distance.y { - /* Offsets for second (middle) corner of simplex in (i,j) coords */ - // lower triangle, XY order: (0,0)->(1,0)->(1,1) - Vector2::from([1, 0]) - } else { - // upper triangle, YX order: (0,0)->(0,1)->(1,1) - Vector2::from([0, 1]) + let corner0 = surflet(gi0, near_distance); + let corner1 = surflet(gi1, far_distance); + + // The maximum value of this noise is 8*(3/4)^4 = 2.53125 + // A factor of 0.395 would scale to fit exactly within [-1,1], but + // to better match classic Perlin noise, we scale it down some more. + // ^-- Original note from Gustavson. + + // Since the objective of this library is to be as close to [-1, 1] as possible, we'll use the + // 0.395 scale instead. + let noise = 0.395 * (corner0.value + corner1.value); + + /* Compute derivative according to: + * dnoise_dx = -8.0 * t20 * t0 * x0 * (gx0 * x0) + t40 * gx0; + * dnoise_dx += -8.0 * t21 * t1 * x1 * (gx1 * x1) + t41 * gx1; + */ + 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; /* Scale derivative to match the noise scaling */ + + (noise, dnoise_dx) + } }; +} - /* A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and - * a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where - * c = (3-sqrt(3))/6 */ - // Offsets for middle corner in (x,y) unskewed coords */ - let distance1 = distance - offset.numcast().unwrap() + Vector2::broadcast(g2); - /* Offsets for last corner in (x,y) unskewed coords */ - let distance2 = distance - Vector2::broadcast(1.0 + 2.0 * g2); - - // Calculate gradient indexes for each corner - 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, - } - - impl SurfletComponents { - fn zeros() -> Self { - Self { - value: 0.0, - t: 0.0, - t2: 0.0, - t4: 0.0, - gradient: Vector2::zero(), +simplex_1d!(simplex_1d_f32, f32); +simplex_1d!(simplex_1d_f64, f64); + +macro_rules! simplex_2d { + ($name:ident, $f:ty) => { + pub fn $name(point: [$f; 2], hasher: &NH) -> ($f, [$f; 2]) + where + NH: NoiseHasher + ?Sized, + { + let f2: $f = skew_factor(2); + let g2: $f = unskew_factor(2); + + let point = Vector2::from(point); + + /* Skew the input space to determine which simplex cell we're in */ + let skew = point.sum() * f2; /* Hairy factor for 2D */ + let skewed = point + Vector2::broadcast(skew); + let cell: Vector2 = skewed.floor().numcast().unwrap(); + + let unskew: $f = cell.sum() as $f * g2; + // Unskew the cell origin back to (x,y) space + let unskewed = cell.numcast().unwrap() - Vector2::broadcast(unskew); + // The x,y distances from the cell origin + let distance = point - unskewed; + + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + let offset = if distance.x > distance.y { + /* Offsets for second (middle) corner of simplex in (i,j) coords */ + // lower triangle, XY order: (0,0)->(1,0)->(1,1) + Vector2::from([1, 0]) + } else { + // upper triangle, YX order: (0,0)->(0,1)->(1,1) + Vector2::from([0, 1]) + }; + + /* A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + * a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + * c = (3-sqrt(3))/6 */ + // Offsets for middle corner in (x,y) unskewed coords */ + let distance1 = distance - offset.numcast().unwrap() + Vector2::broadcast(g2); + /* Offsets for last corner in (x,y) unskewed coords */ + let distance2 = distance - Vector2::broadcast(1.0 + 2.0 * g2); + + // Calculate gradient indexes for each corner + 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: $f, + t: $f, + t2: $f, + t4: $f, + gradient: Vector2<$f>, } - } - } - fn surflet(gradient_index: usize, point: Vector2) -> SurfletComponents { - // let t = 0.5 - (x * x + y * y); - let t = 0.5 - point.magnitude_squared(); - - if t <= 0.0 { - // No influence - SurfletComponents::zeros() - } else { - 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, + impl SurfletComponents { + fn zeros() -> Self { + Self { + value: 0.0, + t: 0.0, + t2: 0.0, + t4: 0.0, + gradient: Vector2::zero(), + } + } } - } - } - let corner0 = surflet(gi0, distance); - let corner1 = surflet(gi1, distance1); - let corner2 = surflet(gi2, distance2); + fn surflet(gradient_index: usize, point: Vector2<$f>) -> SurfletComponents { + // let t = 0.5 - (x * x + y * y); + 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 { + // No influence + SurfletComponents::zeros() + } + } - /* Add contributions from each corner to get the final noise value. - * The result is scaled to return values in the interval [-1, 1]. */ - let noise = 40.0 * (corner0.value + corner1.value + corner2.value); + let corner0 = surflet(gi0, distance); + let corner1 = surflet(gi1, distance1); + let corner2 = surflet(gi2, distance2); - /* A straight, unoptimised calculation would be like: - * dnoise_dx = -8.0 * t20 * t0 * x0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gx0; - * dnoise_dy = -8.0 * t20 * t0 * y0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gy0; - * dnoise_dx += -8.0 * t21 * t1 * x1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gx1; - * dnoise_dy += -8.0 * t21 * t1 * y1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gy1; - * dnoise_dx += -8.0 * t22 * t2 * x2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gx2; - * dnoise_dy += -8.0 * t22 * t2 * y2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gy2; - */ - let temp0 = corner0.t2 * corner0.t * corner0.gradient.dot(distance); - let mut dnoise = distance + Vector2::broadcast(temp0); + /* Add contributions from each corner to get the final noise value. + * The result is scaled to return values in the interval [-1, 1]. */ + let noise = 40.0 * (corner0.value + corner1.value + corner2.value); - let temp1 = corner1.t2 * corner1.t * corner1.gradient.dot(distance1); - dnoise += distance1 * temp1; + /* A straight, unoptimised calculation would be like: + * dnoise_dx = -8.0 * t20 * t0 * x0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gx0; + * dnoise_dy = -8.0 * t20 * t0 * y0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gy0; + * dnoise_dx += -8.0 * t21 * t1 * x1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gx1; + * dnoise_dy += -8.0 * t21 * t1 * y1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gy1; + * dnoise_dx += -8.0 * t22 * t2 * x2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gx2; + * dnoise_dy += -8.0 * t22 * t2 * y2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gy2; + */ + let temp0 = corner0.t2 * corner0.t * corner0.gradient.dot(distance); + let mut dnoise = distance + Vector2::broadcast(temp0); - let temp2 = corner2.t2 * corner2.t * corner2.gradient.dot(distance2); - dnoise += distance2 * temp2; + let temp1 = corner1.t2 * corner1.t * corner1.gradient.dot(distance1); + dnoise += distance1 * temp1; - dnoise *= -8.0; + let temp2 = corner2.t2 * corner2.t * corner2.gradient.dot(distance2); + dnoise += distance2 * temp2; - dnoise += corner0.gradient * corner0.t4 - + corner1.gradient * corner1.t4 - + corner2.gradient * corner2.t4; + dnoise *= -8.0; - dnoise *= 40.0; /* Scale derivative to match the noise scaling */ + dnoise += corner0.gradient * corner0.t4 + + corner1.gradient * corner1.t4 + + corner2.gradient * corner2.t4; - (noise, dnoise.into()) -} + dnoise *= 40.0; /* Scale derivative to match the noise scaling */ -#[inline(always)] -pub fn simplex_3d(point: [f64; 3], hasher: &dyn NoiseHasher) -> (f64, [f64; 3]) { - let f3: f64 = skew_factor(3); - let g3: f64 = unskew_factor(3); - - let point = Vector3::from(point); - - /* Skew the input space to determine which simplex cell we're in */ - // let skew = (x + y + z) * f3; /* Very nice and simple skew factor for 3D */ - let skew = point.sum() * f3; - let skewed = point + Vector3::broadcast(skew); - let cell: Vector3 = skewed.floor().numcast().unwrap(); - - // let unskew = (cell_x + cell_y + cell_z) as f64 * g3; - let unskew = cell.sum() as f64 * g3; - /* Unskew the cell origin back to (x,y,z) space */ - let unskewed = cell.numcast().unwrap() - Vector3::broadcast(unskew); - /* The x,y,z distances from the cell origin */ - let distance = point - unskewed; - - /* For the 3D case, the simplex shape is a slightly irregular tetrahedron. - * Determine which simplex we are in. */ - /* TODO: This code would benefit from a backport from the GLSL version! */ - let (order1, order2): (Vector3, Vector3) = if distance.x >= distance.y { - if distance.y >= distance.z { - /* X Y Z order */ - (Vector3::new(1, 0, 0), Vector3::new(1, 1, 0)) - } else if distance.x >= distance.z { - /* X Z Y order */ - (Vector3::new(1, 0, 0), Vector3::new(1, 0, 1)) - } else { - /* Z X Y order */ - (Vector3::new(0, 0, 1), Vector3::new(1, 0, 1)) - } - } else { - // x0, - } +simplex_2d!(simplex_2d_f32, f32); +simplex_2d!(simplex_2d_f64, f64); + +macro_rules! simplex_3d { + ($name:ident, $f:ty) => { + pub fn $name(point: [$f; 3], hasher: &NH) -> ($f, [$f; 3]) + where + NH: NoiseHasher + ?Sized, + { + let f3: $f = skew_factor(3); + let g3: $f = unskew_factor(3); + + let point = Vector3::from(point); + + /* Skew the input space to determine which simplex cell we're in */ + // let skew = (x + y + z) * f3; /* Very nice and simple skew factor for 3D */ + let skew = point.sum() * f3; + let skewed = point + Vector3::broadcast(skew); + let cell: Vector3 = skewed.floor().numcast().unwrap(); + + // let unskew = (cell_x + cell_y + cell_z) as $f * g3; + let unskew = cell.sum() as $f * g3; + /* Unskew the cell origin back to (x,y,z) space */ + let unskewed = cell.numcast().unwrap() - Vector3::broadcast(unskew); + /* The x,y,z distances from the cell origin */ + let distance = point - unskewed; + + /* For the 3D case, the simplex shape is a slightly irregular tetrahedron. + * Determine which simplex we are in. */ + /* TODO: This code would benefit from a backport from the GLSL version! */ + let (order1, order2): (Vector3, Vector3) = if distance.x >= distance.y { + if distance.y >= distance.z { + /* X Y Z order */ + (Vector3::new(1, 0, 0), Vector3::new(1, 1, 0)) + } else if distance.x >= distance.z { + /* X Z Y order */ + (Vector3::new(1, 0, 0), Vector3::new(1, 0, 1)) + } else { + /* Z X Y order */ + (Vector3::new(0, 0, 1), Vector3::new(1, 0, 1)) + } + } else { + // x0, + } - impl SurfletComponents { - fn zeros() -> Self { - Self { - value: 0.0, - t: 0.0, - t2: 0.0, - t4: 0.0, - gradient: Vector3::zero(), + 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) -> SurfletComponents { - let t = 0.5 - point.magnitude_squared(); - - if t <= 0.0 { - // No influence - SurfletComponents::zeros() - } else { - 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, + fn surflet(gradient_index: usize, point: Vector3<$f>) -> 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 { + // No influence + SurfletComponents::zeros() + } } - } - } - /* Calculate the contribution from the four corners */ - let corner0 = surflet(gi0, distance); - let corner1 = surflet(gi1, offset1); - let corner2 = surflet(gi2, offset2); - let corner3 = surflet(gi3, offset3); - - /* Add contributions from each corner to get the final noise value. - * The result is scaled to return values in the range [-1,1] */ - let noise = 28.0 * (corner0.value + corner1.value + corner2.value + corner3.value); - - /* A straight, unoptimised calculation would be like: - * dnoise_dx = -8.0 * t20 * t0 * x0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gx0; - * dnoise_dy = -8.0 * t20 * t0 * y0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gy0; - * dnoise_dz = -8.0 * t20 * t0 * z0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gz0; - * dnoise_dx += -8.0 * t21 * t1 * x1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gx1; - * dnoise_dy += -8.0 * t21 * t1 * y1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gy1; - * dnoise_dz += -8.0 * t21 * t1 * z1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gz1; - * dnoise_dx += -8.0 * t22 * t2 * x2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gx2; - * dnoise_dy += -8.0 * t22 * t2 * y2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gy2; - * dnoise_dz += -8.0 * t22 * t2 * z2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gz2; - * dnoise_dx += -8.0 * t23 * t3 * x3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gx3; - * dnoise_dy += -8.0 * t23 * t3 * y3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gy3; - * dnoise_dz += -8.0 * t23 * t3 * z3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gz3; - */ - 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; - - /* Scale derivative to match the noise scaling */ - dnoise *= 28.0; - - (noise, dnoise.into()) + /* Calculate the contribution from the four corners */ + let corner0 = surflet(gi0, distance); + let corner1 = surflet(gi1, offset1); + let corner2 = surflet(gi2, offset2); + let corner3 = surflet(gi3, offset3); + + /* Add contributions from each corner to get the final noise value. + * The result is scaled to return values in the range [-1,1] */ + let noise = 28.0 * (corner0.value + corner1.value + corner2.value + corner3.value); + + /* A straight, unoptimised calculation would be like: + * dnoise_dx = -8.0 * t20 * t0 * x0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gx0; + * dnoise_dy = -8.0 * t20 * t0 * y0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gy0; + * dnoise_dz = -8.0 * t20 * t0 * z0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gz0; + * dnoise_dx += -8.0 * t21 * t1 * x1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gx1; + * dnoise_dy += -8.0 * t21 * t1 * y1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gy1; + * dnoise_dz += -8.0 * t21 * t1 * z1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gz1; + * dnoise_dx += -8.0 * t22 * t2 * x2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gx2; + * dnoise_dy += -8.0 * t22 * t2 * y2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gy2; + * dnoise_dz += -8.0 * t22 * t2 * z2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gz2; + * dnoise_dx += -8.0 * t23 * t3 * x3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gx3; + * dnoise_dy += -8.0 * t23 * t3 * y3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gy3; + * dnoise_dz += -8.0 * t23 * t3 * z3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gz3; + */ + 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; + + /* Scale derivative to match the noise scaling */ + dnoise *= 28.0; + + (noise, dnoise.into()) + } + }; } -#[inline(always)] -pub fn simplex_4d(point: [f64; 4], hasher: &dyn NoiseHasher) -> (f64, [f64; 4]) { - let f4: f64 = skew_factor(4); - let g4: f64 = unskew_factor(4); - - let point = Vector4::from(point); - - // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in - // Factor for 4D skewing - let skew = point.sum() * f4; - let skewed = point + Vector4::broadcast(skew); - let cell: Vector4 = skewed.numcast().unwrap(); - - // Factor for 4D unskewing - let unskew = cell.sum() as f64 * g4; - // Unskew the cell origin back to (x,y,z,w) space - let unskewed = cell.numcast().unwrap() - Vector4::broadcast(unskew); - - // let distance_x = x - unskewed_x; // The x,y,z,w distances from the cell origin - // let distance_y = y - unskewed_y; - // let distance_z = z - unskewed_z; - // let distance_w = w - unskewed_w; - let distance = point - unskewed; - - // For the 4D case, the simplex is a 4D shape I won't even try to describe. - // To find out which of the 24 possible simplices we're in, we need to - // determine the magnitude ordering of x0, y0, z0 and w0. - // The method below is a reasonable way of finding the ordering of x,y,z,w - // and then find the correct traversal order for the simplex we're in. - // First, six pair-wise comparisons are performed between each possible pair - // of the four coordinates, and then the results are used to add up binary - // bits for an integer index into a precomputed lookup table, simplex[]. - 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; // '|' is mostly faster than '+' - - let [i1, j1, k1, l1]: [isize; 4]; // The integer offsets for the second simplex corner - let [i2, j2, k2, l2]: [isize; 4]; // The integer offsets for the third simplex corner - let [i3, j3, k3, l3]: [isize; 4]; // The integer offsets for the fourth simplex corner - - // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. - // Many values of c will never occur, since e.g. x>y>z>w makes x= 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 }; - // The number 2 in the "simplex" array is at the second largest coordinate. - 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 }; - // The number 1 in the "simplex" array is at the second smallest coordinate. - 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 }; - // The fifth corner has all coordinate offsets = 1, so no need to look that up. - - let order1 = Vector4::new(i1, j1, k1, l1); - let order2 = Vector4::new(i2, j2, k2, l2); - let order3 = Vector4::new(i3, j3, k3, l3); - - // Offsets for second corner in (x,y,z,w) coords - let offset1 = distance - order1.numcast().unwrap() + Vector4::broadcast(g4); - // Offsets for third corner in (x,y,z,w) coords - let offset2 = distance - order2.numcast().unwrap() + Vector4::broadcast(2.0 * g4); - // Offsets for fourth corner in (x,y,z,w) coords - let offset3 = distance - order3.numcast().unwrap() + Vector4::broadcast(3.0 * g4); - // Offsets for last corner in (x,y,z,w) coords - let offset4 = distance - Vector4::one() + Vector4::broadcast(4.0 * g4); - - // Calculate gradient indexes for each corner - 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, - } +simplex_3d!(simplex_3d_f32, f32); +simplex_3d!(simplex_3d_f64, f64); + +macro_rules! simplex_4d { + ($name:ident, $f:ty) => { + pub fn $name(point: [$f; 4], hasher: &NH) -> ($f, [$f; 4]) + where + NH: NoiseHasher + ?Sized, + { + let f4: $f = skew_factor(4); + let g4: $f = unskew_factor(4); + + let point = Vector4::from(point); + + // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in + // Factor for 4D skewing + let skew = point.sum() * f4; + let skewed = point + Vector4::broadcast(skew); + let cell: Vector4 = skewed.numcast().unwrap(); + + // Factor for 4D unskewing + let unskew = cell.sum() as $f * g4; + // Unskew the cell origin back to (x,y,z,w) space + let unskewed = cell.numcast().unwrap() - Vector4::broadcast(unskew); + + // let distance_x = x - unskewed_x; // The x,y,z,w distances from the cell origin + // let distance_y = y - unskewed_y; + // let distance_z = z - unskewed_z; + // let distance_w = w - unskewed_w; + let distance = point - unskewed; + + // For the 4D case, the simplex is a 4D shape I won't even try to describe. + // To find out which of the 24 possible simplices we're in, we need to + // determine the magnitude ordering of x0, y0, z0 and w0. + // The method below is a reasonable way of finding the ordering of x,y,z,w + // and then find the correct traversal order for the simplex we're in. + // First, six pair-wise comparisons are performed between each possible pair + // of the four coordinates, and then the results are used to add up binary + // bits for an integer index into a precomputed lookup table, simplex[]. + 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; // '|' is mostly faster than '+' + + let [i1, j1, k1, l1]: [isize; 4]; // The integer offsets for the second simplex corner + let [i2, j2, k2, l2]: [isize; 4]; // The integer offsets for the third simplex corner + let [i3, j3, k3, l3]: [isize; 4]; // The integer offsets for the fourth simplex corner + + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x= 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 }; + // The number 2 in the "simplex" array is at the second largest coordinate. + 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 }; + // The number 1 in the "simplex" array is at the second smallest coordinate. + 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 }; + // The fifth corner has all coordinate offsets = 1, so no need to look that up. + + let order1 = Vector4::new(i1, j1, k1, l1); + let order2 = Vector4::new(i2, j2, k2, l2); + let order3 = Vector4::new(i3, j3, k3, l3); + + // Offsets for second corner in (x,y,z,w) coords + let offset1 = distance - order1.numcast().unwrap() + Vector4::broadcast(g4); + // Offsets for third corner in (x,y,z,w) coords + let offset2 = distance - order2.numcast().unwrap() + Vector4::broadcast(2.0 * g4); + // Offsets for fourth corner in (x,y,z,w) coords + let offset3 = distance - order3.numcast().unwrap() + Vector4::broadcast(3.0 * g4); + // Offsets for last corner in (x,y,z,w) coords + let offset4 = distance - Vector4::one() + Vector4::broadcast(4.0 * g4); + + // Calculate gradient indexes for each corner + 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: $f, + t: $f, + t2: $f, + t4: $f, + gradient: Vector4<$f>, + } - impl SurfletComponents { - fn zeros() -> Self { - Self { - value: 0.0, - t: 0.0, - t2: 0.0, - t4: 0.0, - gradient: Vector4::zero(), + 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) -> SurfletComponents { - let t = 0.6 - point.magnitude_squared(); - - if t <= 0.0 { - // No influence - SurfletComponents::zeros() - } else { - 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, + fn surflet(gradient_index: usize, point: Vector4<$f>) -> 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 { + // No influence + SurfletComponents::zeros() + } } - } - } - /* Calculate the contribution from the five corners */ - 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); - - // Sum up and scale the result to cover the range [-1,1] - let noise = - 27.0 * (corner0.value + corner1.value + corner2.value + corner3.value + corner4.value); // TODO: The scale factor is preliminary! - - /* A straight, unoptimised calculation would be like: - * dnoise_dx = -8.0 * t20 * t0 * x0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gx0; - * dnoise_dy = -8.0 * t20 * t0 * y0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gy0; - * dnoise_dz = -8.0 * t20 * t0 * z0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gz0; - * dnoise_dw = -8.0 * t20 * t0 * w0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gw0; - * dnoise_dx += -8.0 * t21 * t1 * x1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gx1; - * dnoise_dy += -8.0 * t21 * t1 * y1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gy1; - * dnoise_dz += -8.0 * t21 * t1 * z1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gz1; - * dnoise_dw += -8.0 * t21 * t1 * w1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gw1; - * dnoise_dx += -8.0 * t22 * t2 * x2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gx2; - * dnoise_dy += -8.0 * t22 * t2 * y2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gy2; - * dnoise_dz += -8.0 * t22 * t2 * z2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gz2; - * dnoise_dw += -8.0 * t22 * t2 * w2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gw2; - * dnoise_dx += -8.0 * t23 * t3 * x3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gx3; - * dnoise_dy += -8.0 * t23 * t3 * y3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gy3; - * dnoise_dz += -8.0 * t23 * t3 * z3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gz3; - * dnoise_dw += -8.0 * t23 * t3 * w3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gw3; - * dnoise_dx += -8.0 * t24 * t4 * x4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gx4; - * dnoise_dy += -8.0 * t24 * t4 * y4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gy4; - * dnoise_dz += -8.0 * t24 * t4 * z4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gz4; - * dnoise_dw += -8.0 * t24 * t4 * w4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gw4; - */ - 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; - - // Scale derivative to match the noise scaling - dnoise *= 28.0; - - (noise, dnoise.into()) + /* Calculate the contribution from the five corners */ + 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); + + // Sum up and scale the result to cover the range [-1,1] + let noise = 27.0 + * (corner0.value + corner1.value + corner2.value + corner3.value + corner4.value); // TODO: The scale factor is preliminary! + + /* A straight, unoptimised calculation would be like: + * dnoise_dx = -8.0 * t20 * t0 * x0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gx0; + * dnoise_dy = -8.0 * t20 * t0 * y0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gy0; + * dnoise_dz = -8.0 * t20 * t0 * z0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gz0; + * dnoise_dw = -8.0 * t20 * t0 * w0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gw0; + * dnoise_dx += -8.0 * t21 * t1 * x1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gx1; + * dnoise_dy += -8.0 * t21 * t1 * y1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gy1; + * dnoise_dz += -8.0 * t21 * t1 * z1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gz1; + * dnoise_dw += -8.0 * t21 * t1 * w1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gw1; + * dnoise_dx += -8.0 * t22 * t2 * x2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gx2; + * dnoise_dy += -8.0 * t22 * t2 * y2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gy2; + * dnoise_dz += -8.0 * t22 * t2 * z2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gz2; + * dnoise_dw += -8.0 * t22 * t2 * w2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gw2; + * dnoise_dx += -8.0 * t23 * t3 * x3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gx3; + * dnoise_dy += -8.0 * t23 * t3 * y3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gy3; + * dnoise_dz += -8.0 * t23 * t3 * z3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gz3; + * dnoise_dw += -8.0 * t23 * t3 * w3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gw3; + * dnoise_dx += -8.0 * t24 * t4 * x4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gx4; + * dnoise_dy += -8.0 * t24 * t4 * y4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gy4; + * dnoise_dz += -8.0 * t24 * t4 * z4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gz4; + * dnoise_dw += -8.0 * t24 * t4 * w4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gw4; + */ + 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; + + // Scale derivative to match the noise scaling + dnoise *= 28.0; + + (noise, dnoise.into()) + } + }; } +simplex_4d!(simplex_4d_f32, f32); +simplex_4d!(simplex_4d_f64, f64); + // A lookup table to traverse the simplex around a given point in 4D. // Details can be found where this table is used, in the 4D noise method. /* TODO: This should not be required, backport it from Bill's GLSL code! */ diff --git a/src/core/super_simplex.rs b/src/core/super_simplex.rs index 89f4494d..d4e7f1a6 100644 --- a/src/core/super_simplex.rs +++ b/src/core/super_simplex.rs @@ -83,7 +83,10 @@ const LATTICE_LOOKUP_3D: [[i8; 3]; 4 * 16] = [0, 0, 0],[0, 1, 1],[1, 0, 1],[1, 1, 0], [1, 1, 1],[0, 1, 1],[1, 0, 1],[1, 1, 0]]; -pub fn super_simplex_2d(point: [f64; 2], hasher: &impl NoiseHasher) -> f64 { +pub fn super_simplex_2d(point: [f64; 2], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ let point = Vector2::from(point); // Transform point from real space to simplex space @@ -125,7 +128,10 @@ pub fn super_simplex_2d(point: [f64; 2], hasher: &impl NoiseHasher) -> f64 { value * NORM_CONSTANT_2D } -pub fn super_simplex_3d(point: [f64; 3], hasher: &impl NoiseHasher) -> f64 { +pub fn super_simplex_3d(point: [f64; 3], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ let point = Vector3::from(point); // Transform point from real space to simplex space diff --git a/src/core/value.rs b/src/core/value.rs index f1c88772..2ad838cc 100644 --- a/src/core/value.rs +++ b/src/core/value.rs @@ -3,21 +3,28 @@ use crate::{ permutationtable::NoiseHasher, }; -pub fn value_2d(point: [f64; 2], hasher: &impl NoiseHasher) -> f64 { - fn get(hasher: &impl NoiseHasher, corner: Vector2, offset: [isize; 2]) -> f64 { - hasher.hash(&(corner + Vector2::from(offset)).into_array()) as f64 / 255.0 - } - +pub fn value_2d(point: [f64; 2], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ let point = Vector2::from(point); let floored = point.floor(); let corner = floored.numcast().unwrap(); let weight = point - floored.map_quintic(); - let f00 = get(hasher, corner, [0, 0]); - let f10 = get(hasher, corner, [1, 0]); - let f01 = get(hasher, corner, [0, 1]); - let f11 = get(hasher, corner, [1, 1]); + macro_rules! get( + ($corner:expr, $offset:expr) => { + { + hasher.hash(&($corner + Vector2::from($offset)).into_array()) as f64 / 255.0 + } + } + ); + + let f00 = get!(corner, [0, 0]); + let f10 = get!(corner, [1, 0]); + let f01 = get!(corner, [0, 1]); + let f11 = get!(corner, [1, 1]); let d0 = interpolate::linear(f00, f10, weight.x); let d1 = interpolate::linear(f01, f11, weight.x); @@ -26,25 +33,32 @@ pub fn value_2d(point: [f64; 2], hasher: &impl NoiseHasher) -> f64 { d * 2.0 - 1.0 } -pub fn value_3d(point: [f64; 3], hasher: &impl NoiseHasher) -> f64 { - fn get(hasher: &impl NoiseHasher, corner: Vector3, offset: [isize; 3]) -> f64 { - hasher.hash(&(corner + Vector3::from(offset)).into_array()) as f64 / 255.0 - } - +pub fn value_3d(point: [f64; 3], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ let point = Vector3::from(point); let floored = point.floor(); let corner = floored.numcast().unwrap(); let weight = point - floored.map_quintic(); - let f000 = get(hasher, corner, [0, 0, 0]); - let f100 = get(hasher, corner, [1, 0, 0]); - let f010 = get(hasher, corner, [0, 1, 0]); - let f110 = get(hasher, corner, [1, 1, 0]); - let f001 = get(hasher, corner, [0, 0, 1]); - let f101 = get(hasher, corner, [1, 0, 1]); - let f011 = get(hasher, corner, [0, 1, 1]); - let f111 = get(hasher, corner, [1, 1, 1]); + macro_rules! get( + ($corner:expr, $offset:expr) => { + { + hasher.hash(&($corner + Vector3::from($offset)).into_array()) as f64 / 255.0 + } + } + ); + + let f000 = get!(corner, [0, 0, 0]); + let f100 = get!(corner, [1, 0, 0]); + let f010 = get!(corner, [0, 1, 0]); + let f110 = get!(corner, [1, 1, 0]); + let f001 = get!(corner, [0, 0, 1]); + let f101 = get!(corner, [1, 0, 1]); + let f011 = get!(corner, [0, 1, 1]); + let f111 = get!(corner, [1, 1, 1]); let d00 = interpolate::linear(f000, f100, weight.x); let d01 = interpolate::linear(f001, f101, weight.x); @@ -57,33 +71,40 @@ pub fn value_3d(point: [f64; 3], hasher: &impl NoiseHasher) -> f64 { d * 2.0 - 1.0 } -pub fn value_4d(point: [f64; 4], hasher: &impl NoiseHasher) -> f64 { - fn get(hasher: &impl NoiseHasher, corner: Vector4, offset: [isize; 4]) -> f64 { - hasher.hash(&(corner + Vector4::from(offset)).into_array()) as f64 / 255.0 - } - +pub fn value_4d(point: [f64; 4], hasher: &NH) -> f64 +where + NH: NoiseHasher + ?Sized, +{ let point = Vector4::from(point); let floored = point.floor(); let corner = floored.numcast().unwrap(); let weight = point - floored.map_quintic(); - let f0000 = get(hasher, corner, [0, 0, 0, 0]); - let f1000 = get(hasher, corner, [1, 0, 0, 0]); - let f0100 = get(hasher, corner, [0, 1, 0, 0]); - let f1100 = get(hasher, corner, [1, 1, 0, 0]); - let f0010 = get(hasher, corner, [0, 0, 1, 0]); - let f1010 = get(hasher, corner, [1, 0, 1, 0]); - let f0110 = get(hasher, corner, [0, 1, 1, 0]); - let f1110 = get(hasher, corner, [1, 1, 1, 0]); - let f0001 = get(hasher, corner, [0, 0, 0, 1]); - let f1001 = get(hasher, corner, [1, 0, 0, 1]); - let f0101 = get(hasher, corner, [0, 1, 0, 1]); - let f1101 = get(hasher, corner, [1, 1, 0, 1]); - let f0011 = get(hasher, corner, [0, 0, 1, 1]); - let f1011 = get(hasher, corner, [1, 0, 1, 1]); - let f0111 = get(hasher, corner, [0, 1, 1, 1]); - let f1111 = get(hasher, corner, [1, 1, 1, 1]); + macro_rules! get( + ($corner:expr, $offset:expr) => { + { + hasher.hash(&($corner + Vector4::from($offset)).into_array()) as f64 / 255.0 + } + } + ); + + let f0000 = get!(corner, [0, 0, 0, 0]); + let f1000 = get!(corner, [1, 0, 0, 0]); + let f0100 = get!(corner, [0, 1, 0, 0]); + let f1100 = get!(corner, [1, 1, 0, 0]); + let f0010 = get!(corner, [0, 0, 1, 0]); + let f1010 = get!(corner, [1, 0, 1, 0]); + let f0110 = get!(corner, [0, 1, 1, 0]); + let f1110 = get!(corner, [1, 1, 1, 0]); + let f0001 = get!(corner, [0, 0, 0, 1]); + let f1001 = get!(corner, [1, 0, 0, 1]); + let f0101 = get!(corner, [0, 1, 0, 1]); + let f1101 = get!(corner, [1, 1, 0, 1]); + let f0011 = get!(corner, [0, 0, 1, 1]); + let f1011 = get!(corner, [1, 0, 1, 1]); + let f0111 = get!(corner, [0, 1, 1, 1]); + let f1111 = get!(corner, [1, 1, 1, 1]); let d000 = interpolate::linear(f0000, f1000, weight.x); let d010 = interpolate::linear(f0010, f1010, weight.x); diff --git a/src/core/worley.rs b/src/core/worley.rs index 6f2396cd..081ce107 100644 --- a/src/core/worley.rs +++ b/src/core/worley.rs @@ -62,21 +62,20 @@ pub mod distance_functions { } } -#[inline(always)] -pub fn worley_2d( - hasher: &dyn NoiseHasher, +pub fn worley_2d( + hasher: &NH, distance_function: F, return_type: ReturnType, point: [f64; 2], ) -> f64 where F: Fn(&[f64], &[f64]) -> f64, + NH: NoiseHasher + ?Sized, { let point = Vector2::from(point); - #[inline] - fn get_point(hasher: &dyn NoiseHasher, whole: Vector2) -> Vector2 { - get_vec2(hasher.hash(&whole.into_array())) + whole.numcast().unwrap() + fn get_point(index: usize, whole: Vector2) -> Vector2 { + get_vec2(index) + whole.numcast().unwrap() } let cell = point.floor(); @@ -89,7 +88,8 @@ where let far = whole + half.map(|x| !x as isize); let mut seed_cell = near; - let seed_point = get_point(hasher, near); + let seed_index = hasher.hash(&near.into_array()); + let seed_point = get_point(seed_index, near); let mut distance = distance_function(&point.into_array(), &seed_point.into_array()); let range = frac.map(|x| (0.5 - x).powf(2.0)); @@ -97,11 +97,13 @@ where macro_rules! test_point( [$x:expr, $y:expr] => { { - let cur_point = get_point(hasher, Vector2::from([$x, $y])); - let cur_distance = distance_function(&point.into_array(), &cur_point.into_array()); + let test_point = Vector2::from([$x, $y]); + let index = hasher.hash(&test_point.into_array()); + let offset = get_point(index, test_point); + let cur_distance = distance_function(&point.into_array(), &offset.into_array()); if cur_distance < distance { distance = cur_distance; - seed_cell = Vector2::from([$x, $y]); + seed_cell = test_point; } } } @@ -128,7 +130,6 @@ where } #[rustfmt::skip] -#[inline] fn get_vec2(index: usize) -> Vector2 { let length = ((index & 0xF8) >> 3) as f64 * 0.5 / 31.0; let diag = length * f64::consts::FRAC_1_SQRT_2; @@ -147,19 +148,20 @@ fn get_vec2(index: usize) -> Vector2 { } #[inline(always)] -pub fn worley_3d( - hasher: &dyn NoiseHasher, +pub fn worley_3d( + hasher: &NH, distance_function: F, return_type: ReturnType, point: [f64; 3], ) -> f64 where F: Fn(&[f64], &[f64]) -> f64, + NH: NoiseHasher + ?Sized, { let point = Vector3::from(point); - fn get_point(hasher: &dyn NoiseHasher, whole: Vector3) -> Vector3 { - get_vec3(hasher.hash(&whole.into_array())) + whole.numcast().unwrap() + fn get_point(index: usize, whole: Vector3) -> Vector3 { + get_vec3(index) + whole.numcast().unwrap() } let cell = point.floor(); @@ -172,7 +174,8 @@ where let far = whole + half.map(|x| !x as isize); let mut seed_cell = near; - let seed_point = get_point(hasher, near); + let seed_index = hasher.hash(&near.into_array()); + let seed_point = get_point(seed_index, near); let mut distance = distance_function(&point.into_array(), &seed_point.into_array()); let range = frac.map(|x| (0.5 - x).powf(2.0)); @@ -180,11 +183,13 @@ where macro_rules! test_point( [$x:expr, $y:expr, $z:expr] => { { - let cur_point = get_point(hasher, Vector3::from([$x, $y, $z])); - let cur_distance = distance_function(&point.into_array(), &cur_point.into_array()); + let test_point = Vector3::from([$x, $y, $z]); + let index = hasher.hash(&test_point.into_array()); + let offset = get_point(index, test_point); + let cur_distance = distance_function(&point.into_array(), &offset.into_array()); if cur_distance < distance { distance = cur_distance; - seed_cell = Vector3::from([$x, $y, $z]); + seed_cell = test_point; } } } @@ -253,19 +258,20 @@ fn get_vec3(index: usize) -> Vector3 { #[inline(always)] #[allow(clippy::cognitive_complexity)] -pub fn worley_4d( - hasher: &dyn NoiseHasher, +pub fn worley_4d( + hasher: &NH, distance_function: F, return_type: ReturnType, point: [f64; 4], ) -> f64 where F: Fn(&[f64], &[f64]) -> f64, + NH: NoiseHasher + ?Sized, { let point = Vector4::from(point); - fn get_point(hasher: &dyn NoiseHasher, whole: Vector4) -> Vector4 { - get_vec4(hasher.hash(&whole.into_array())) + whole.numcast().unwrap() + fn get_point(index: usize, whole: Vector4) -> Vector4 { + get_vec4(index) + whole.numcast().unwrap() } let cell = point.floor(); @@ -278,24 +284,26 @@ where let far = whole + half.map(|x| !x as isize); let mut seed_cell = near; - let seed_point = get_point(hasher, near); + let seed_index = hasher.hash(&near.into_array()); + let seed_point = get_point(seed_index, near); let mut distance = distance_function(&point.into_array(), &seed_point.into_array()); - // get distance squared to center line for each axis let range = frac.map(|x| (0.5 - x).powf(2.0)); macro_rules! test_point( - [$x:expr, $y:expr, $z:expr, $w:expr] => { - { - let cur_point = get_point(hasher, Vector4::from([$x, $y, $z, $w])); - let cur_distance = distance_function(&point.into_array(), &cur_point.into_array()); - if cur_distance < distance { - distance = cur_distance; - seed_cell = Vector4::from([$x, $y, $z, $w]); - } + [$x:expr, $y:expr, $z:expr, $w:expr] => { + { + let test_point = Vector4::from([$x, $y, $z, $w]); + let index = hasher.hash(&test_point.into_array()); + let offset = get_point(index, test_point); + let cur_distance = distance_function(&point.into_array(), &offset.into_array()); + if cur_distance < distance { + distance = cur_distance; + seed_cell = test_point; } } - ); + } + ); if range.x < distance { test_point![far.x, near.y, near.z, near.w]; diff --git a/src/gradient.rs b/src/gradient.rs index 36b7679f..57e88d28 100644 --- a/src/gradient.rs +++ b/src/gradient.rs @@ -1,115 +1,123 @@ -#[inline(always)] +use num_traits::Float; + #[rustfmt::skip] -pub(crate) fn grad2(index: usize) -> [f64; 2] { +pub(crate) fn grad2(index: usize) -> [F; 2] +where + F: Float, +{ // Vectors are combinations of -1, 0, and 1 // Precompute the normalized element - const DIAG : f64 = core::f64::consts::FRAC_1_SQRT_2; + let diag = F::from(core::f64::consts::FRAC_1_SQRT_2).unwrap(); match index % 8 { - 0 => [ 1.0, 0.0], - 1 => [ -1.0, 0.0], - 2 => [ 0.0, 1.0], - 3 => [ 0.0, -1.0], - 4 => [ DIAG, DIAG], - 5 => [-DIAG, DIAG], - 6 => [ DIAG, -DIAG], - 7 => [-DIAG, -DIAG], + 0 => [ F::one(), F::zero()], + 1 => [ -F::one(), F::zero()], + 2 => [ F::zero(), F::one()], + 3 => [ F::zero(), -F::one()], + 4 => [ diag, diag], + 5 => [-diag, diag], + 6 => [ diag, -diag], + 7 => [-diag, -diag], _ => panic!("Attempt to access gradient {} of 8", index % 8), } } -#[inline(always)] #[rustfmt::skip] -pub(crate) fn grad3(index: usize) -> [f64; 3] { +pub(crate) fn grad3(index: usize) -> [F; 3] +where + F: Float, +{ // Vectors are combinations of -1, 0, and 1 // Precompute the normalized elements - const DIAG : f64 = core::f64::consts::FRAC_1_SQRT_2; - const DIAG2 : f64 = 0.577_350_269_189_625_8; + let diag = F::from(core::f64::consts::FRAC_1_SQRT_2).unwrap(); + let diag2 = F::from(0.577_350_269_189_625_8).unwrap(); match index % 32 { // 12 edges repeated twice then 8 corners - 0 | 12 => [ DIAG, DIAG, 0.0], - 1 | 13 => [ -DIAG, DIAG, 0.0], - 2 | 14 => [ DIAG, -DIAG, 0.0], - 3 | 15 => [ -DIAG, -DIAG, 0.0], - 4 | 16 => [ DIAG, 0.0, DIAG], - 5 | 17 => [ -DIAG, 0.0, DIAG], - 6 | 18 => [ DIAG, 0.0, -DIAG], - 7 | 19 => [ -DIAG, 0.0, -DIAG], - 8 | 20 => [ 0.0, DIAG, DIAG], - 9 | 21 => [ 0.0, -DIAG, DIAG], - 10 | 22 => [ 0.0, DIAG, -DIAG], - 11 | 23 => [ 0.0, -DIAG, -DIAG], - 24 => [ DIAG2, DIAG2, DIAG2], - 25 => [-DIAG2, DIAG2, DIAG2], - 26 => [ DIAG2, -DIAG2, DIAG2], - 27 => [-DIAG2, -DIAG2, DIAG2], - 28 => [ DIAG2, DIAG2, -DIAG2], - 29 => [-DIAG2, DIAG2, -DIAG2], - 30 => [ DIAG2, -DIAG2, -DIAG2], - 31 => [-DIAG2, -DIAG2, -DIAG2], + 0 | 12 => [ diag, diag, F::zero()], + 1 | 13 => [ -diag, diag, F::zero()], + 2 | 14 => [ diag, -diag, F::zero()], + 3 | 15 => [ -diag, -diag, F::zero()], + 4 | 16 => [ diag, F::zero(), diag], + 5 | 17 => [ -diag, F::zero(), diag], + 6 | 18 => [ diag, F::zero(), -diag], + 7 | 19 => [ -diag, F::zero(), -diag], + 8 | 20 => [ F::zero(), diag, diag], + 9 | 21 => [ F::zero(), -diag, diag], + 10 | 22 => [ F::zero(), diag, -diag], + 11 | 23 => [ F::zero(), -diag, -diag], + 24 => [ diag2, diag2, diag2], + 25 => [-diag2, diag2, diag2], + 26 => [ diag2, -diag2, diag2], + 27 => [-diag2, -diag2, diag2], + 28 => [ diag2, diag2, -diag2], + 29 => [-diag2, diag2, -diag2], + 30 => [ diag2, -diag2, -diag2], + 31 => [-diag2, -diag2, -diag2], _ => panic!("Attempt to access gradient {} of 32", index % 32), } } -#[inline(always)] #[rustfmt::skip] -pub(crate) fn grad4(index: usize) -> [f64; 4] { +pub(crate) fn grad4(index: usize) -> [F; 4] +where + F: Float, +{ // Vectors are combinations of -1, 0, and 1 // Precompute the normalized elements - const DIAG : f64 = 0.577_350_269_189_625_8; - const DIAG2 : f64 = 0.5; + let diag = F::from(0.577_350_269_189_625_8).unwrap(); + let diag2 = F::from(0.5).unwrap(); match index % 64 { // 32 edges then 16 corners repeated twice - 0 => [ 0.0, DIAG, DIAG, DIAG], - 1 => [ 0.0, DIAG, DIAG, -DIAG], - 2 => [ 0.0, DIAG, -DIAG, DIAG], - 3 => [ 0.0, DIAG, -DIAG, -DIAG], - 4 => [ 0.0, -DIAG, DIAG, DIAG], - 5 => [ 0.0, -DIAG, DIAG, -DIAG], - 6 => [ 0.0, -DIAG, -DIAG, DIAG], - 7 => [ 0.0, -DIAG, -DIAG, -DIAG], - 8 => [ DIAG, 0.0, DIAG, DIAG], - 9 => [ DIAG, 0.0, DIAG, -DIAG], - 10 => [ DIAG, 0.0, -DIAG, DIAG], - 11 => [ DIAG, 0.0, -DIAG, -DIAG], - 12 => [ -DIAG, 0.0, DIAG, DIAG], - 13 => [ -DIAG, 0.0, DIAG, -DIAG], - 14 => [ -DIAG, 0.0, -DIAG, DIAG], - 15 => [ -DIAG, 0.0, -DIAG, -DIAG], - 16 => [ DIAG, DIAG, 0.0, DIAG], - 17 => [ DIAG, DIAG, 0.0, -DIAG], - 18 => [ DIAG, -DIAG, 0.0, DIAG], - 19 => [ DIAG, -DIAG, 0.0, -DIAG], - 20 => [ -DIAG, DIAG, 0.0, DIAG], - 21 => [ -DIAG, DIAG, 0.0, -DIAG], - 22 => [ -DIAG, -DIAG, 0.0, DIAG], - 23 => [ -DIAG, -DIAG, 0.0, -DIAG], - 24 => [ DIAG, DIAG, DIAG, 0.0], - 25 => [ DIAG, DIAG, -DIAG, 0.0], - 26 => [ DIAG, -DIAG, DIAG, 0.0], - 27 => [ DIAG, -DIAG, -DIAG, 0.0], - 28 => [ -DIAG, DIAG, DIAG, 0.0], - 29 => [ -DIAG, DIAG, -DIAG, 0.0], - 30 => [ -DIAG, -DIAG, DIAG, 0.0], - 31 => [ -DIAG, -DIAG, -DIAG, 0.0], - 32 | 48 => [ DIAG2, DIAG2, DIAG2, DIAG2], - 33 | 49 => [-DIAG2, DIAG2, DIAG2, DIAG2], - 34 | 50 => [ DIAG2, -DIAG2, DIAG2, DIAG2], - 35 | 51 => [-DIAG2, -DIAG2, DIAG2, DIAG2], - 36 | 52 => [ DIAG2, DIAG2, -DIAG2, DIAG2], - 37 | 53 => [-DIAG2, DIAG2, -DIAG2, DIAG2], - 38 | 54 => [ DIAG2, DIAG2, DIAG2, -DIAG2], - 39 | 55 => [-DIAG2, DIAG2, DIAG2, -DIAG2], - 40 | 56 => [ DIAG2, -DIAG2, -DIAG2, DIAG2], - 41 | 57 => [-DIAG2, -DIAG2, -DIAG2, DIAG2], - 42 | 58 => [ DIAG2, -DIAG2, DIAG2, -DIAG2], - 43 | 59 => [-DIAG2, -DIAG2, DIAG2, -DIAG2], - 44 | 60 => [ DIAG2, DIAG2, -DIAG2, -DIAG2], - 45 | 61 => [-DIAG2, DIAG2, -DIAG2, -DIAG2], - 46 | 62 => [ DIAG2, -DIAG2, -DIAG2, -DIAG2], - 47 | 63 => [-DIAG2, -DIAG2, -DIAG2, -DIAG2], + 0 => [ F::zero(), diag, diag, diag], + 1 => [ F::zero(), diag, diag, -diag], + 2 => [ F::zero(), diag, -diag, diag], + 3 => [ F::zero(), diag, -diag, -diag], + 4 => [ F::zero(), -diag, diag, diag], + 5 => [ F::zero(), -diag, diag, -diag], + 6 => [ F::zero(), -diag, -diag, diag], + 7 => [ F::zero(), -diag, -diag, -diag], + 8 => [ diag, F::zero(), diag, diag], + 9 => [ diag, F::zero(), diag, -diag], + 10 => [ diag, F::zero(), -diag, diag], + 11 => [ diag, F::zero(), -diag, -diag], + 12 => [ -diag, F::zero(), diag, diag], + 13 => [ -diag, F::zero(), diag, -diag], + 14 => [ -diag, F::zero(), -diag, diag], + 15 => [ -diag, F::zero(), -diag, -diag], + 16 => [ diag, diag, F::zero(), diag], + 17 => [ diag, diag, F::zero(), -diag], + 18 => [ diag, -diag, F::zero(), diag], + 19 => [ diag, -diag, F::zero(), -diag], + 20 => [ -diag, diag, F::zero(), diag], + 21 => [ -diag, diag, F::zero(), -diag], + 22 => [ -diag, -diag, F::zero(), diag], + 23 => [ -diag, -diag, F::zero(), -diag], + 24 => [ diag, diag, diag, F::zero()], + 25 => [ diag, diag, -diag, F::zero()], + 26 => [ diag, -diag, diag, F::zero()], + 27 => [ diag, -diag, -diag, F::zero()], + 28 => [ -diag, diag, diag, F::zero()], + 29 => [ -diag, diag, -diag, F::zero()], + 30 => [ -diag, -diag, diag, F::zero()], + 31 => [ -diag, -diag, -diag, F::zero()], + 32 | 48 => [ diag2, diag2, diag2, diag2], + 33 | 49 => [-diag2, diag2, diag2, diag2], + 34 | 50 => [ diag2, -diag2, diag2, diag2], + 35 | 51 => [-diag2, -diag2, diag2, diag2], + 36 | 52 => [ diag2, diag2, -diag2, diag2], + 37 | 53 => [-diag2, diag2, -diag2, diag2], + 38 | 54 => [ diag2, diag2, diag2, -diag2], + 39 | 55 => [-diag2, diag2, diag2, -diag2], + 40 | 56 => [ diag2, -diag2, -diag2, diag2], + 41 | 57 => [-diag2, -diag2, -diag2, diag2], + 42 | 58 => [ diag2, -diag2, diag2, -diag2], + 43 | 59 => [-diag2, -diag2, diag2, -diag2], + 44 | 60 => [ diag2, diag2, -diag2, -diag2], + 45 | 61 => [-diag2, diag2, -diag2, -diag2], + 46 | 62 => [ diag2, -diag2, -diag2, -diag2], + 47 | 63 => [-diag2, -diag2, -diag2, -diag2], _ => panic!("Attempt to access gradient {} of 64", index % 64), } } diff --git a/src/math.rs b/src/math.rs index c8ecffee..97891bce 100644 --- a/src/math.rs +++ b/src/math.rs @@ -1,18 +1,20 @@ //! An ultra-light private math library to make our short lives easier as we //! implement super-complex noise stuff. +use num_traits::Float; + pub(crate) mod interpolate; pub(crate) mod s_curve; pub(crate) mod vectors; #[cfg(not(target_os = "emscripten"))] #[inline] -pub(crate) fn scale_shift(value: f64, n: f64) -> f64 { - value.abs().mul_add(n, -1.0_f64) +pub(crate) fn scale_shift(value: F, n: F) -> F { + value.abs().mul_add(n, -F::one()) } #[cfg(target_os = "emscripten")] #[inline] -pub(crate) fn scale_shift(value: f64, n: f64) -> f64 { - (value.abs() * n) + -1.0_f64 +pub(crate) fn scale_shift(value: F, n: F) -> F { + (value.abs() * n) + -F::one() } diff --git a/src/noise_fns.rs b/src/noise_fns.rs index 03e6a599..84d06a8b 100644 --- a/src/noise_fns.rs +++ b/src/noise_fns.rs @@ -24,7 +24,7 @@ mod transformers; /// in various ways. /// * Combining the output values from two noise functions in various ways. pub trait NoiseFn { - fn get(&self, point: [T; DIM]) -> f64; + fn get(&self, point: [T; DIM]) -> T; } impl<'a, T, M, const DIM: usize> NoiseFn for &'a M @@ -32,7 +32,7 @@ where M: NoiseFn + ?Sized, { #[inline] - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [T; DIM]) -> T { M::get(*self, point) } } @@ -42,7 +42,7 @@ where M: NoiseFn + ?Sized, { #[inline] - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [T; DIM]) -> T { M::get(self, point) } } diff --git a/src/noise_fns/combiners/add.rs b/src/noise_fns/combiners/add.rs index d5d8f3cc..29c3f830 100644 --- a/src/noise_fns/combiners/add.rs +++ b/src/noise_fns/combiners/add.rs @@ -1,12 +1,13 @@ use crate::noise_fns::NoiseFn; use core::marker::PhantomData; +use num_traits::Float; /// Noise function that outputs the sum of the two output values from two source /// functions. -pub struct Add +pub struct Add where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { /// Outputs a value. pub source1: Source1, @@ -14,13 +15,13 @@ where /// Outputs a value. pub source2: Source2, - phantom: PhantomData, + phantom: PhantomData, } -impl Add +impl Add where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { pub fn new(source1: Source1, source2: Source2) -> Self { Self { @@ -31,13 +32,13 @@ where } } -impl NoiseFn for Add +impl NoiseFn for Add where - T: Copy, - Source1: NoiseFn, - Source2: NoiseFn, + F: Float, + Source1: NoiseFn, + Source2: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { self.source1.get(point) + self.source2.get(point) } } diff --git a/src/noise_fns/combiners/max.rs b/src/noise_fns/combiners/max.rs index a0cf6e93..93b6de28 100644 --- a/src/noise_fns/combiners/max.rs +++ b/src/noise_fns/combiners/max.rs @@ -1,12 +1,13 @@ use crate::noise_fns::NoiseFn; use core::marker::PhantomData; +use num_traits::Float; /// Noise function that outputs the larger of the two output values from two source /// functions. -pub struct Max +pub struct Max where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { /// Outputs a value. pub source1: Source1, @@ -14,13 +15,13 @@ where /// Outputs a value. pub source2: Source2, - phantom: PhantomData, + phantom: PhantomData, } -impl Max +impl Max where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { pub fn new(source1: Source1, source2: Source2) -> Self { Self { @@ -31,13 +32,13 @@ where } } -impl NoiseFn for Max +impl NoiseFn for Max where - T: Copy, - Source1: NoiseFn, - Source2: NoiseFn, + F: Float, + Source1: NoiseFn, + Source2: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { (self.source1.get(point)).max(self.source2.get(point)) } } diff --git a/src/noise_fns/combiners/min.rs b/src/noise_fns/combiners/min.rs index c59b043c..4e005b64 100644 --- a/src/noise_fns/combiners/min.rs +++ b/src/noise_fns/combiners/min.rs @@ -1,12 +1,13 @@ use crate::noise_fns::NoiseFn; use core::marker::PhantomData; +use num_traits::Float; /// Noise function that outputs the smaller of the two output values from two source /// functions. -pub struct Min +pub struct Min where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { /// Outputs a value. pub source1: Source1, @@ -14,13 +15,13 @@ where /// Outputs a value. pub source2: Source2, - phantom: PhantomData, + phantom: PhantomData, } -impl Min +impl Min where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { pub fn new(source1: Source1, source2: Source2) -> Self { Self { @@ -31,13 +32,13 @@ where } } -impl NoiseFn for Min +impl NoiseFn for Min where - T: Copy, - Source1: NoiseFn, - Source2: NoiseFn, + F: Float, + Source1: NoiseFn, + Source2: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { (self.source1.get(point)).min(self.source2.get(point)) } } diff --git a/src/noise_fns/combiners/multiply.rs b/src/noise_fns/combiners/multiply.rs index 36ecc464..77e98aec 100644 --- a/src/noise_fns/combiners/multiply.rs +++ b/src/noise_fns/combiners/multiply.rs @@ -1,12 +1,13 @@ use crate::noise_fns::NoiseFn; use core::marker::PhantomData; +use num_traits::Float; /// Noise function that outputs the product of the two output values from two source /// functions. -pub struct Multiply +pub struct Multiply where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { /// Outputs a value. pub source1: Source1, @@ -14,13 +15,13 @@ where /// Outputs a value. pub source2: Source2, - phantom: PhantomData, + phantom: PhantomData, } -impl Multiply +impl Multiply where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { pub fn new(source1: Source1, source2: Source2) -> Self { Self { @@ -31,13 +32,13 @@ where } } -impl NoiseFn for Multiply +impl NoiseFn for Multiply where - T: Copy, - Source1: NoiseFn, - Source2: NoiseFn, + F: Float, + Source1: NoiseFn, + Source2: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { self.source1.get(point) * self.source2.get(point) } } diff --git a/src/noise_fns/combiners/power.rs b/src/noise_fns/combiners/power.rs index 671c5fee..1a8c04c1 100644 --- a/src/noise_fns/combiners/power.rs +++ b/src/noise_fns/combiners/power.rs @@ -1,12 +1,13 @@ use crate::noise_fns::NoiseFn; use core::marker::PhantomData; +use num_traits::Float; /// Noise function that raises the output value from the first source function /// to the power of the output value of the second source function. -pub struct Power +pub struct Power where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { /// Outputs a value. pub source1: Source1, @@ -14,13 +15,13 @@ where /// Outputs a value. pub source2: Source2, - phantom: PhantomData, + phantom: PhantomData, } -impl Power +impl Power where - Source1: NoiseFn, - Source2: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, { pub fn new(source1: Source1, source2: Source2) -> Self { Self { @@ -31,13 +32,13 @@ where } } -impl NoiseFn for Power +impl NoiseFn for Power where - T: Copy, - Source1: NoiseFn, - Source2: NoiseFn, + F: Float, + Source1: NoiseFn, + Source2: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { (self.source1.get(point)).powf(self.source2.get(point)) } } diff --git a/src/noise_fns/generators/constant.rs b/src/noise_fns/generators/constant.rs index 5a6b3f10..2f34f39f 100644 --- a/src/noise_fns/generators/constant.rs +++ b/src/noise_fns/generators/constant.rs @@ -8,19 +8,19 @@ use crate::noise_fns::NoiseFn; /// This function is not very useful by itself, but can be used as a source /// function for other noise functions. #[derive(Clone, Copy, Debug)] -pub struct Constant { +pub struct Constant { /// Constant value. - pub value: f64, + pub value: F, } -impl Constant { - pub fn new(value: f64) -> Self { +impl Constant { + pub fn new(value: F) -> Self { Self { value } } } -impl NoiseFn for Constant { - fn get(&self, _point: [T; N]) -> f64 { +impl NoiseFn for Constant { + fn get(&self, _point: [F; N]) -> F { self.value } } diff --git a/src/noise_fns/generators/fractals.rs b/src/noise_fns/generators/fractals.rs index 159e4d88..ecdda783 100644 --- a/src/noise_fns/generators/fractals.rs +++ b/src/noise_fns/generators/fractals.rs @@ -11,13 +11,15 @@ use crate::Seedable; /// Trait for `MultiFractal` functions pub trait MultiFractal { + type F; + fn set_octaves(self, octaves: usize) -> Self; - fn set_frequency(self, frequency: f64) -> Self; + fn set_frequency(self, frequency: Self::F) -> Self; - fn set_lacunarity(self, lacunarity: f64) -> Self; + fn set_lacunarity(self, lacunarity: Self::F) -> Self; - fn set_persistence(self, persistence: f64) -> Self; + fn set_persistence(self, persistence: Self::F) -> Self; } fn build_sources(seed: u32, octaves: usize) -> Vec diff --git a/src/noise_fns/generators/fractals/basicmulti.rs b/src/noise_fns/generators/fractals/basicmulti.rs index 3bf01324..54a36658 100644 --- a/src/noise_fns/generators/fractals/basicmulti.rs +++ b/src/noise_fns/generators/fractals/basicmulti.rs @@ -15,7 +15,7 @@ use alloc::vec::Vec; /// not be as damped and thus will grow more jagged as iteration progresses. /// #[derive(Clone, Debug)] -pub struct BasicMulti { +pub struct BasicMulti { /// Total number of frequency octaves to generate the noise with. /// /// The number of octaves control the _amount of detail_ in the noise @@ -24,7 +24,7 @@ pub struct BasicMulti { pub octaves: usize, /// The number of cycles per unit length that the noise function outputs. - pub frequency: f64, + pub frequency: F, /// A multiplier that determines how quickly the frequency increases for /// each successive octave in the noise function. @@ -34,7 +34,7 @@ pub struct BasicMulti { /// /// A lacunarity of 2.0 results in the frequency doubling every octave. For /// almost all cases, 2.0 is a good value to use. - pub lacunarity: f64, + pub lacunarity: F, /// A multiplier that determines how quickly the amplitudes diminish for /// each successive octave in the noise function. @@ -42,69 +42,78 @@ pub struct BasicMulti { /// The amplitude of each successive octave is equal to the product of the /// previous octave's amplitude and the persistence value. Increasing the /// persistence produces "rougher" noise. - pub persistence: f64, + pub persistence: F, seed: u32, sources: Vec, } -impl BasicMulti { - pub const DEFAULT_SEED: u32 = 0; - pub const DEFAULT_OCTAVES: usize = 6; - pub const DEFAULT_FREQUENCY: f64 = 2.0; - pub const DEFAULT_LACUNARITY: f64 = core::f64::consts::PI * 2.0 / 3.0; - pub const DEFAULT_PERSISTENCE: f64 = 0.5; - pub const MAX_OCTAVES: usize = 32; - - pub fn new(seed: u32) -> Self { - Self { - seed, - octaves: Self::DEFAULT_OCTAVES, - frequency: Self::DEFAULT_FREQUENCY, - lacunarity: Self::DEFAULT_LACUNARITY, - persistence: Self::DEFAULT_PERSISTENCE, - sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVES), - } - } -} - -impl Default for BasicMulti { - fn default() -> Self { - Self::new(Self::DEFAULT_SEED) - } -} - -impl MultiFractal for BasicMulti { - fn set_octaves(self, mut octaves: usize) -> Self { - if self.octaves == octaves { - return self; +macro_rules! impl_basicmulti { + ($f:ty) => { + impl BasicMulti<$f> { + pub const DEFAULT_SEED: u32 = 0; + pub const DEFAULT_OCTAVES: usize = 6; + pub const DEFAULT_FREQUENCY: $f = 2.0; + pub const DEFAULT_LACUNARITY: $f = 2.0; + pub const DEFAULT_PERSISTENCE: $f = 0.5; + pub const MAX_OCTAVES: usize = 32; + + pub fn new(seed: u32) -> Self { + Self { + seed, + octaves: Self::DEFAULT_OCTAVES, + frequency: Self::DEFAULT_FREQUENCY, + lacunarity: Self::DEFAULT_LACUNARITY, + persistence: Self::DEFAULT_PERSISTENCE, + sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVES), + } + } } - octaves = octaves.clamp(1, Self::MAX_OCTAVES); - Self { - octaves, - sources: super::build_sources(self.seed, octaves), - ..self + impl Default for BasicMulti<$f> { + fn default() -> Self { + Self::new(Self::DEFAULT_SEED) + } } - } - fn set_frequency(self, frequency: f64) -> Self { - Self { frequency, ..self } - } - - fn set_lacunarity(self, lacunarity: f64) -> Self { - Self { lacunarity, ..self } - } - - fn set_persistence(self, persistence: f64) -> Self { - Self { - persistence, - ..self + impl MultiFractal for BasicMulti<$f> { + type F = $f; + + fn set_octaves(self, mut octaves: usize) -> Self { + if self.octaves == octaves { + return self; + } + + octaves = octaves.clamp(1, Self::MAX_OCTAVES); + Self { + octaves, + sources: super::build_sources(self.seed, octaves), + ..self + } + } + + fn set_frequency(self, frequency: Self::F) -> Self { + Self { frequency, ..self } + } + + fn set_lacunarity(self, lacunarity: Self::F) -> Self { + Self { lacunarity, ..self } + } + + fn set_persistence(self, persistence: Self::F) -> Self { + Self { + persistence, + ..self + } + } } - } + }; } -impl Seedable for BasicMulti { +impl_basicmulti!(f32); +impl_basicmulti!(f64); + +impl Seedable for BasicMulti { fn set_seed(self, seed: u32) -> Self { if self.seed == seed { return self; @@ -122,98 +131,146 @@ impl Seedable for BasicMulti { } } -/// 2-dimensional `BasicMulti` noise -impl NoiseFn for BasicMulti { - fn get(&self, point: [f64; 2]) -> f64 { - let mut point = Vector2::from(point); +macro_rules! basicmulti { + ($type:ty, $dim:expr, $vec:ident) => { + impl NoiseFn<$type, $dim> for BasicMulti<$type> { + fn get(&self, point: [$type; $dim]) -> $type { + let mut point = $vec::from(point); - // First unscaled octave of function; later octaves are scaled. - point *= self.frequency; - let mut result = self.sources[0].get(point.into_array()); + // First unscaled octave of function; later octaves are scaled. + point *= self.frequency; + let mut result = self.sources[0].get(point.into_array()); - // Spectral construction inner loop, where the fractal is built. - for x in 1..self.octaves { - // Raise the spatial frequency. - point *= self.lacunarity; + // Spectral construction inner loop, where the fractal is built. + for x in 1..self.octaves { + // Raise the spatial frequency. + point *= self.lacunarity; - // Get noise value. - let mut signal = self.sources[x].get(point.into_array()); + // Get noise value. + let mut signal = self.sources[x].get(point.into_array()); - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); + // Scale the amplitude appropriately for this frequency. + signal *= self.persistence.powi(x as i32); - // Scale the signal by the current 'altitude' of the function. - signal *= result; + // Scale the signal by the current 'altitude' of the function. + signal *= result; - // Add signal to result. - result += signal; - } - - // Scale the result to the [-1,1] range. - result * 0.5 - } -} - -/// 3-dimensional `BasicMulti` noise -impl NoiseFn for BasicMulti { - fn get(&self, point: [f64; 3]) -> f64 { - let mut point = Vector3::from(point); - - // First unscaled octave of function; later octaves are scaled. - point *= self.frequency; - let mut result = self.sources[0].get(point.into_array()); + // Add signal to result. + result += signal; + } - // Spectral construction inner loop, where the fractal is built. - for x in 1..self.octaves { - // Raise the spatial frequency. - point *= self.lacunarity; - - // Get noise value. - let mut signal = self.sources[x].get(point.into_array()); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Scale the signal by the current 'altitude' of the function. - signal *= result; - - // Add signal to result. - result += signal; + // Scale the result to the [-1,1] range. + result * 0.5 + } } - - // Scale the result to the [-1,1] range. - result * 0.5 - } + }; } -/// 4-dimensional `BasicMulti` noise -impl NoiseFn for BasicMulti { - fn get(&self, point: [f64; 4]) -> f64 { - let mut point = Vector4::from(point); - - // First unscaled octave of function; later octaves are scaled. - point *= self.frequency; - let mut result = self.sources[0].get(point.into_array()); - - // Spectral construction inner loop, where the fractal is built. - for x in 1..self.octaves { - // Raise the spatial frequency. - point *= self.lacunarity; - - // Get noise value. - let mut signal = self.sources[x].get(point.into_array()); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Scale the signal by the current 'altitude' of the function. - signal *= result; - - // Add signal to result. - result += signal; - } - - // Scale the result to the [-1,1] range. - result * 0.5 - } -} +basicmulti!(f32, 2, Vector2); +basicmulti!(f32, 3, Vector3); +basicmulti!(f32, 4, Vector4); +basicmulti!(f64, 2, Vector2); +basicmulti!(f64, 3, Vector3); +basicmulti!(f64, 4, Vector4); +// +// /// 2-dimensional `BasicMulti` noise +// impl NoiseFn for BasicMulti +// where +// F: Float, +// { +// fn get(&self, point: [F; 2]) -> F { +// let mut point = Vector2::from(point); +// +// // First unscaled octave of function; later octaves are scaled. +// point *= self.frequency; +// let mut result = self.sources[0].get(point.into_array()); +// +// // Spectral construction inner loop, where the fractal is built. +// for x in 1..self.octaves { +// // Raise the spatial frequency. +// point *= self.lacunarity; +// +// // Get noise value. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Scale the signal by the current 'altitude' of the function. +// signal *= result; +// +// // Add signal to result. +// result += signal; +// } +// +// // Scale the result to the [-1,1] range. +// result * F::from(0.5).unwrap() +// } +// } +// +// /// 3-dimensional `BasicMulti` noise +// impl NoiseFn for BasicMulti +// where +// F: Float, +// { +// fn get(&self, point: [F; 3]) -> F { +// let mut point = Vector3::from(point); +// +// // First unscaled octave of function; later octaves are scaled. +// point *= self.frequency; +// let mut result = self.sources[0].get(point.into_array()); +// +// // Spectral construction inner loop, where the fractal is built. +// for x in 1..self.octaves { +// // Raise the spatial frequency. +// point *= self.lacunarity; +// +// // Get noise value. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Scale the signal by the current 'altitude' of the function. +// signal *= result; +// +// // Add signal to result. +// result += signal; +// } +// +// // Scale the result to the [-1,1] range. +// result * 0.5 +// } +// } +// +// /// 4-dimensional `BasicMulti` noise +// impl NoiseFn for BasicMulti { +// fn get(&self, point: [f64; 4]) -> f64 { +// let mut point = Vector4::from(point); +// +// // First unscaled octave of function; later octaves are scaled. +// point *= self.frequency; +// let mut result = self.sources[0].get(point.into_array()); +// +// // Spectral construction inner loop, where the fractal is built. +// for x in 1..self.octaves { +// // Raise the spatial frequency. +// point *= self.lacunarity; +// +// // Get noise value. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Scale the signal by the current 'altitude' of the function. +// signal *= result; +// +// // Add signal to result. +// result += signal; +// } +// +// // Scale the result to the [-1,1] range. +// result * 0.5 +// } +// } diff --git a/src/noise_fns/generators/fractals/billow.rs b/src/noise_fns/generators/fractals/billow.rs index 7b684700..12ebc150 100644 --- a/src/noise_fns/generators/fractals/billow.rs +++ b/src/noise_fns/generators/fractals/billow.rs @@ -3,6 +3,7 @@ use crate::{ noise_fns::{MultiFractal, NoiseFn, Perlin, Seedable}, }; use alloc::vec::Vec; +use num_traits::Float; /// Noise function that outputs "billowy" noise. /// @@ -12,7 +13,7 @@ use alloc::vec::Vec; /// function modifies each octave with an absolute-value function. See the /// documentation for fBm for more information. #[derive(Clone, Debug)] -pub struct Billow { +pub struct Billow { /// Total number of frequency octaves to generate the noise with. /// /// The number of octaves control the _amount of detail_ in the noise @@ -21,7 +22,7 @@ pub struct Billow { pub octaves: usize, /// The number of cycles per unit length that the noise function outputs. - pub frequency: f64, + pub frequency: F, /// A multiplier that determines how quickly the frequency increases for /// each successive octave in the noise function. @@ -31,7 +32,7 @@ pub struct Billow { /// /// A lacunarity of 2.0 results in the frequency doubling every octave. For /// almost all cases, 2.0 is a good value to use. - pub lacunarity: f64, + pub lacunarity: F, /// A multiplier that determines how quickly the amplitudes diminish for /// each successive octave in the noise function. @@ -39,84 +40,96 @@ pub struct Billow { /// The amplitude of each successive octave is equal to the product of the /// previous octave's amplitude and the persistence value. Increasing the /// persistence produces "rougher" noise. - pub persistence: f64, + pub persistence: F, seed: u32, sources: Vec, - scale_factor: f64, + scale_factor: F, } -fn calc_scale_factor(persistence: f64, octaves: usize) -> f64 { - 1.0 - persistence.powi(octaves as i32) +fn calc_scale_factor(persistence: F, octaves: usize) -> F +where + F: Float, +{ + F::one() - persistence.powi(octaves as i32) } -impl Billow { - pub const DEFAULT_SEED: u32 = 0; - pub const DEFAULT_OCTAVE_COUNT: usize = 6; - pub const DEFAULT_FREQUENCY: f64 = 1.0; - pub const DEFAULT_LACUNARITY: f64 = core::f64::consts::PI * 2.0 / 3.0; - pub const DEFAULT_PERSISTENCE: f64 = 0.5; - pub const MAX_OCTAVES: usize = 32; - - pub fn new(seed: u32) -> Self { - Self { - seed, - octaves: Self::DEFAULT_OCTAVE_COUNT, - frequency: Self::DEFAULT_FREQUENCY, - lacunarity: Self::DEFAULT_LACUNARITY, - persistence: Self::DEFAULT_PERSISTENCE, - sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVE_COUNT), - scale_factor: Self::calc_scale_factor( - Self::DEFAULT_PERSISTENCE, - Self::DEFAULT_OCTAVE_COUNT, - ), - } - } - - fn calc_scale_factor(persistence: f64, octaves: usize) -> f64 { - 1.0 - persistence.powi(octaves as i32) - } -} - -impl Default for Billow { - fn default() -> Self { - Self::new(Self::DEFAULT_SEED) - } -} - -impl MultiFractal for Billow { - fn set_octaves(self, mut octaves: usize) -> Self { - if self.octaves == octaves { - return self; +macro_rules! impl_billow { + ($f:ty) => { + impl Billow<$f> { + pub const DEFAULT_SEED: u32 = 0; + pub const DEFAULT_OCTAVE_COUNT: usize = 6; + pub const DEFAULT_FREQUENCY: $f = 1.0; + pub const DEFAULT_LACUNARITY: $f = 2.0; + pub const DEFAULT_PERSISTENCE: $f = 0.5; + pub const MAX_OCTAVES: usize = 32; + + pub fn new(seed: u32) -> Self { + Self { + seed, + octaves: Self::DEFAULT_OCTAVE_COUNT, + frequency: Self::DEFAULT_FREQUENCY, + lacunarity: Self::DEFAULT_LACUNARITY, + persistence: Self::DEFAULT_PERSISTENCE, + sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVE_COUNT), + scale_factor: Self::calc_scale_factor( + Self::DEFAULT_PERSISTENCE, + Self::DEFAULT_OCTAVE_COUNT, + ), + } + } + + fn calc_scale_factor(persistence: $f, octaves: usize) -> $f { + 1.0 - persistence.powi(octaves as i32) + } } - octaves = octaves.clamp(1, Self::MAX_OCTAVES); - Self { - octaves, - sources: super::build_sources(self.seed, octaves), - scale_factor: calc_scale_factor(self.persistence, octaves), - ..self + impl Default for Billow<$f> { + fn default() -> Self { + Self::new(Self::DEFAULT_SEED) + } } - } - - fn set_frequency(self, frequency: f64) -> Self { - Self { frequency, ..self } - } - fn set_lacunarity(self, lacunarity: f64) -> Self { - Self { lacunarity, ..self } - } - - fn set_persistence(self, persistence: f64) -> Self { - Self { - persistence, - scale_factor: calc_scale_factor(persistence, self.octaves), - ..self + impl MultiFractal for Billow<$f> { + type F = $f; + + fn set_octaves(self, mut octaves: usize) -> Self { + if self.octaves == octaves { + return self; + } + + octaves = octaves.clamp(1, Self::MAX_OCTAVES); + Self { + octaves, + sources: super::build_sources(self.seed, octaves), + scale_factor: calc_scale_factor(self.persistence, octaves), + ..self + } + } + + fn set_frequency(self, frequency: Self::F) -> Self { + Self { frequency, ..self } + } + + fn set_lacunarity(self, lacunarity: Self::F) -> Self { + Self { lacunarity, ..self } + } + + fn set_persistence(self, persistence: Self::F) -> Self { + Self { + persistence, + scale_factor: calc_scale_factor(persistence, self.octaves), + ..self + } + } } - } + }; } -impl Seedable for Billow { +impl_billow!(f32); +impl_billow!(f64); + +impl Seedable for Billow { fn set_seed(self, seed: u32) -> Self { if self.seed == seed { return self; @@ -134,98 +147,146 @@ impl Seedable for Billow { } } -/// 2-dimensional Billow noise -impl NoiseFn for Billow { - fn get(&self, point: [f64; 2]) -> f64 { - let mut point = Vector2::from(point); +macro_rules! billow { + ($dim:expr, $vec:ident, $f:ty) => { + impl NoiseFn<$f, $dim> for Billow<$f> + where + $f: Float, + { + fn get(&self, point: [$f; $dim]) -> $f { + let mut point = $vec::from(point); - let mut result = 0.0; + let mut result = 0.0; - point *= self.frequency; + point *= self.frequency; - for x in 0..self.octaves { - // Get the signal. - let mut signal = self.sources[x].get(point.into_array()); + for x in 0..self.octaves { + // Get the signal. + let mut signal = self.sources[x].get(point.into_array()); - // Take the abs of the signal, then scale and shift back to - // the [-1,1] range. - signal = scale_shift(signal, 2.0); + // Take the abs of the signal, then scale and shift back to + // the [-1,1] range. + signal = scale_shift(signal, 2.0); - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); + // Scale the amplitude appropriately for this frequency. + signal *= self.persistence.powi(x as i32); - // Add the signal to the result. - result += signal; + // Add the signal to the result. + result += signal; - // Increase the frequency for the next octave. - point *= self.lacunarity; - } - - // Scale the result to the [-1,1] range. - result / self.scale_factor - } -} - -/// 3-dimensional Billow noise -impl NoiseFn for Billow { - fn get(&self, point: [f64; 3]) -> f64 { - let mut point = Vector3::from(point); - - let mut result = 0.0; + // Increase the frequency for the next octave. + point *= self.lacunarity; + } - point *= self.frequency; - - for x in 0..self.octaves { - // Get the signal. - let mut signal = self.sources[x].get(point.into_array()); - - // Take the abs of the signal, then scale and shift back to - // the [-1,1] range. - signal = scale_shift(signal, 2.0); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Add the signal to the result. - result += signal; - - // Increase the frequency for the next octave. - point *= self.lacunarity; + // Scale the result to the [-1,1] range. + result / self.scale_factor + } } - - // Scale the result to the [-1,1] range. - result / self.scale_factor - } + }; } -/// 4-dimensional Billow noise -impl NoiseFn for Billow { - fn get(&self, point: [f64; 4]) -> f64 { - let mut point = Vector4::from(point); - - let mut result = 0.0; - - point *= self.frequency; - - for x in 0..self.octaves { - // Get the signal. - let mut signal = self.sources[x].get(point.into_array()); - - // Take the abs of the signal, then scale and shift back to - // the [-1,1] range. - signal = scale_shift(signal, 2.0); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Add the signal to the result. - result += signal; - - // Increase the frequency for the next octave. - point *= self.lacunarity; - } - - // Scale the result to the [-1,1] range. - result / self.scale_factor - } -} +billow!(2, Vector2, f32); +billow!(3, Vector3, f32); +billow!(4, Vector4, f32); +billow!(2, Vector2, f64); +billow!(3, Vector3, f64); +billow!(4, Vector4, f64); + +// /// 2-dimensional Billow noise +// impl NoiseFn for Billow +// where +// F: Float, +// { +// fn get(&self, point: [f64; 2]) -> f64 { +// let mut point = Vector2::from(point); +// +// let mut result = 0.0; +// +// point *= self.frequency; +// +// for x in 0..self.octaves { +// // Get the signal. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Take the abs of the signal, then scale and shift back to +// // the [-1,1] range. +// signal = scale_shift(signal, 2.0); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add the signal to the result. +// result += signal; +// +// // Increase the frequency for the next octave. +// point *= self.lacunarity; +// } +// +// // Scale the result to the [-1,1] range. +// result / self.scale_factor +// } +// } +// +// /// 3-dimensional Billow noise +// impl NoiseFn for Billow { +// fn get(&self, point: [f64; 3]) -> f64 { +// let mut point = Vector3::from(point); +// +// let mut result = 0.0; +// +// point *= self.frequency; +// +// for x in 0..self.octaves { +// // Get the signal. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Take the abs of the signal, then scale and shift back to +// // the [-1,1] range. +// signal = scale_shift(signal, 2.0); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add the signal to the result. +// result += signal; +// +// // Increase the frequency for the next octave. +// point *= self.lacunarity; +// } +// +// // Scale the result to the [-1,1] range. +// result / self.scale_factor +// } +// } +// +// /// 4-dimensional Billow noise +// impl NoiseFn for Billow { +// fn get(&self, point: [f64; 4]) -> f64 { +// let mut point = Vector4::from(point); +// +// let mut result = 0.0; +// +// point *= self.frequency; +// +// for x in 0..self.octaves { +// // Get the signal. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Take the abs of the signal, then scale and shift back to +// // the [-1,1] range. +// signal = scale_shift(signal, 2.0); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add the signal to the result. +// result += signal; +// +// // Increase the frequency for the next octave. +// point *= self.lacunarity; +// } +// +// // Scale the result to the [-1,1] range. +// result / self.scale_factor +// } +// } diff --git a/src/noise_fns/generators/fractals/fbm.rs b/src/noise_fns/generators/fractals/fbm.rs index e279b576..d95b9c76 100644 --- a/src/noise_fns/generators/fractals/fbm.rs +++ b/src/noise_fns/generators/fractals/fbm.rs @@ -3,6 +3,7 @@ use crate::{ noise_fns::{MultiFractal, NoiseFn, Perlin, Seedable}, }; use alloc::vec::Vec; +use num_traits::Float; /// Noise function that outputs fBm (fractal Brownian motion) noise. /// @@ -21,7 +22,7 @@ use alloc::vec::Vec; /// /// fBm is commonly referred to as Perlin noise. #[derive(Clone, Debug)] -pub struct Fbm { +pub struct Fbm { /// Total number of frequency octaves to generate the noise with. /// /// The number of octaves control the _amount of detail_ in the noise @@ -30,7 +31,7 @@ pub struct Fbm { pub octaves: usize, /// The number of cycles per unit length that the noise function outputs. - pub frequency: f64, + pub frequency: F, /// A multiplier that determines how quickly the frequency increases for /// each successive octave in the noise function. @@ -40,7 +41,7 @@ pub struct Fbm { /// /// A lacunarity of 2.0 results in the frequency doubling every octave. For /// almost all cases, 2.0 is a good value to use. - pub lacunarity: f64, + pub lacunarity: F, /// A multiplier that determines how quickly the amplitudes diminish for /// each successive octave in the noise function. @@ -48,84 +49,96 @@ pub struct Fbm { /// The amplitude of each successive octave is equal to the product of the /// previous octave's amplitude and the persistence value. Increasing the /// persistence produces "rougher" noise. - pub persistence: f64, + pub persistence: F, seed: u32, sources: Vec, - scale_factor: f64, + scale_factor: F, } -fn calc_scale_factor(persistence: f64, octaves: usize) -> f64 { - 1.0 - persistence.powi(octaves as i32) +fn calc_scale_factor(persistence: F, octaves: usize) -> F +where + F: Float, +{ + F::one() - persistence.powi(octaves as i32) } -impl Fbm { - pub const DEFAULT_SEED: u32 = 0; - pub const DEFAULT_OCTAVE_COUNT: usize = 6; - pub const DEFAULT_FREQUENCY: f64 = 1.0; - pub const DEFAULT_LACUNARITY: f64 = core::f64::consts::PI * 2.0 / 3.0; - pub const DEFAULT_PERSISTENCE: f64 = 0.5; - pub const MAX_OCTAVES: usize = 32; - - pub fn new(seed: u32) -> Self { - Self { - seed, - octaves: Self::DEFAULT_OCTAVE_COUNT, - frequency: Self::DEFAULT_FREQUENCY, - lacunarity: Self::DEFAULT_LACUNARITY, - persistence: Self::DEFAULT_PERSISTENCE, - sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVE_COUNT), - scale_factor: Self::calc_scale_factor( - Self::DEFAULT_PERSISTENCE, - Self::DEFAULT_OCTAVE_COUNT, - ), +macro_rules! impl_fbm { + ($f:ty) => { + impl Fbm<$f> { + pub const DEFAULT_SEED: u32 = 0; + pub const DEFAULT_OCTAVE_COUNT: usize = 6; + pub const DEFAULT_FREQUENCY: $f = 1.0; + pub const DEFAULT_LACUNARITY: $f = 2.0; + pub const DEFAULT_PERSISTENCE: $f = 0.5; + pub const MAX_OCTAVES: usize = 32; + + pub fn new(seed: u32) -> Self { + Self { + seed, + octaves: Self::DEFAULT_OCTAVE_COUNT, + frequency: Self::DEFAULT_FREQUENCY, + lacunarity: Self::DEFAULT_LACUNARITY, + persistence: Self::DEFAULT_PERSISTENCE, + sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVE_COUNT), + scale_factor: Self::calc_scale_factor( + Self::DEFAULT_PERSISTENCE, + Self::DEFAULT_OCTAVE_COUNT, + ), + } + } + + fn calc_scale_factor(persistence: $f, octaves: usize) -> $f { + 1.0 - persistence.powi(octaves as i32) + } } - } - - fn calc_scale_factor(persistence: f64, octaves: usize) -> f64 { - 1.0 - persistence.powi(octaves as i32) - } -} - -impl Default for Fbm { - fn default() -> Self { - Self::new(Self::DEFAULT_SEED) - } -} -impl MultiFractal for Fbm { - fn set_octaves(self, mut octaves: usize) -> Self { - if self.octaves == octaves { - return self; + impl Default for Fbm<$f> { + fn default() -> Self { + Self::new(Self::DEFAULT_SEED) + } } - octaves = octaves.clamp(1, Self::MAX_OCTAVES); - Self { - octaves, - sources: super::build_sources(self.seed, octaves), - scale_factor: calc_scale_factor(self.persistence, octaves), - ..self + impl MultiFractal for Fbm<$f> { + type F = $f; + + fn set_octaves(self, mut octaves: usize) -> Self { + if self.octaves == octaves { + return self; + } + + octaves = octaves.clamp(1, Self::MAX_OCTAVES); + Self { + octaves, + sources: super::build_sources(self.seed, octaves), + scale_factor: calc_scale_factor(self.persistence, octaves), + ..self + } + } + + fn set_frequency(self, frequency: Self::F) -> Self { + Self { frequency, ..self } + } + + fn set_lacunarity(self, lacunarity: Self::F) -> Self { + Self { lacunarity, ..self } + } + + fn set_persistence(self, persistence: Self::F) -> Self { + Self { + persistence, + scale_factor: calc_scale_factor(persistence, self.octaves), + ..self + } + } } - } - - fn set_frequency(self, frequency: f64) -> Self { - Self { frequency, ..self } - } - - fn set_lacunarity(self, lacunarity: f64) -> Self { - Self { lacunarity, ..self } - } - - fn set_persistence(self, persistence: f64) -> Self { - Self { - persistence, - scale_factor: calc_scale_factor(persistence, self.octaves), - ..self - } - } + }; } -impl Seedable for Fbm { +impl_fbm!(f32); +impl_fbm!(f64); + +impl Seedable for Fbm { fn set_seed(self, seed: u32) -> Self { if self.seed == seed { return self; @@ -143,86 +156,125 @@ impl Seedable for Fbm { } } -/// 2-dimensional Fbm noise -impl NoiseFn for Fbm { - fn get(&self, point: [f64; 2]) -> f64 { - let mut point = Vector2::from(point); - - let mut result = 0.0; - - point *= self.frequency; - - for x in 0..self.octaves { - // Get the signal. - let mut signal = self.sources[x].get(point.into_array()); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Add the signal to the result. - result += signal; +macro_rules! fbm { + ($dim:expr, $vec:ident, $f:ty) => { + impl NoiseFn<$f, $dim> for Fbm<$f> { + fn get(&self, point: [$f; $dim]) -> $f { + let mut point = $vec::from(point); - // Increase the frequency for the next octave. - point *= self.lacunarity; - } - - // Scale the result into the [-1,1] range - result / self.scale_factor - } -} - -/// 3-dimensional Fbm noise -impl NoiseFn for Fbm { - fn get(&self, point: [f64; 3]) -> f64 { - let mut point = Vector3::from(point); + let mut result = 0.0; - let mut result = 0.0; + point *= self.frequency; - point *= self.frequency; + for x in 0..self.octaves { + // Get the signal. + let mut signal = self.sources[x].get(point.into_array()); - for x in 0..self.octaves { - // Get the signal. - let mut signal = self.sources[x].get(point.into_array()); + // Scale the amplitude appropriately for this frequency. + signal *= self.persistence.powi(x as i32); - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); + // Add the signal to the result. + result += signal; - // Add the signal to the result. - result += signal; + // Increase the frequency for the next octave. + point *= self.lacunarity; + } - // Increase the frequency for the next octave. - point *= self.lacunarity; + // Scale the result into the [-1,1] range + result / self.scale_factor + } } - - // Scale the result into the [-1,1] range - result / self.scale_factor - } + }; } -/// 4-dimensional Fbm noise -impl NoiseFn for Fbm { - fn get(&self, point: [f64; 4]) -> f64 { - let mut point = Vector4::from(point); - - let mut result = 0.0; - - point *= self.frequency; - - for x in 0..self.octaves { - // Get the signal. - let mut signal = self.sources[x].get(point.into_array()); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Add the signal to the result. - result += signal; - - // Increase the frequency for the next octave. - point *= self.lacunarity; - } - - // Scale the result into the [-1,1] range - result / self.scale_factor - } -} +fbm!(2, Vector2, f32); +fbm!(3, Vector3, f32); +fbm!(4, Vector4, f32); +fbm!(2, Vector2, f64); +fbm!(3, Vector3, f64); +fbm!(4, Vector4, f64); + +// +// /// 2-dimensional Fbm noise +// impl NoiseFn for Fbm { +// fn get(&self, point: [f64; 2]) -> f64 { +// let mut point = Vector2::from(point); +// +// let mut result = 0.0; +// +// point *= self.frequency; +// +// for x in 0..self.octaves { +// // Get the signal. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add the signal to the result. +// result += signal; +// +// // Increase the frequency for the next octave. +// point *= self.lacunarity; +// } +// +// // Scale the result into the [-1,1] range +// result / self.scale_factor +// } +// } +// +// /// 3-dimensional Fbm noise +// impl NoiseFn for Fbm { +// fn get(&self, point: [f64; 3]) -> f64 { +// let mut point = Vector3::from(point); +// +// let mut result = 0.0; +// +// point *= self.frequency; +// +// for x in 0..self.octaves { +// // Get the signal. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add the signal to the result. +// result += signal; +// +// // Increase the frequency for the next octave. +// point *= self.lacunarity; +// } +// +// // Scale the result into the [-1,1] range +// result / self.scale_factor +// } +// } +// +// /// 4-dimensional Fbm noise +// impl NoiseFn for Fbm { +// fn get(&self, point: [f64; 4]) -> f64 { +// let mut point = Vector4::from(point); +// +// let mut result = 0.0; +// +// point *= self.frequency; +// +// for x in 0..self.octaves { +// // Get the signal. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add the signal to the result. +// result += signal; +// +// // Increase the frequency for the next octave. +// point *= self.lacunarity; +// } +// +// // Scale the result into the [-1,1] range +// result / self.scale_factor +// } +// } diff --git a/src/noise_fns/generators/fractals/hybridmulti.rs b/src/noise_fns/generators/fractals/hybridmulti.rs index d65c3143..388231bd 100644 --- a/src/noise_fns/generators/fractals/hybridmulti.rs +++ b/src/noise_fns/generators/fractals/hybridmulti.rs @@ -9,7 +9,7 @@ use alloc::vec::Vec; /// The result of this multifractal noise is that valleys in the noise should /// have smooth bottoms at all altitudes. #[derive(Clone, Debug)] -pub struct HybridMulti { +pub struct HybridMulti { /// Total number of frequency octaves to generate the noise with. /// /// The number of octaves control the _amount of detail_ in the noise @@ -18,7 +18,7 @@ pub struct HybridMulti { pub octaves: usize, /// The number of cycles per unit length that the noise function outputs. - pub frequency: f64, + pub frequency: F, /// A multiplier that determines how quickly the frequency increases for /// each successive octave in the noise function. @@ -28,7 +28,7 @@ pub struct HybridMulti { /// /// A lacunarity of 2.0 results in the frequency doubling every octave. For /// almost all cases, 2.0 is a good value to use. - pub lacunarity: f64, + pub lacunarity: F, /// A multiplier that determines how quickly the amplitudes diminish for /// each successive octave in the noise function. @@ -36,69 +36,78 @@ pub struct HybridMulti { /// The amplitude of each successive octave is equal to the product of the /// previous octave's amplitude and the persistence value. Increasing the /// persistence produces "rougher" noise. - pub persistence: f64, + pub persistence: F, seed: u32, sources: Vec, } -impl HybridMulti { - pub const DEFAULT_SEED: u32 = 0; - pub const DEFAULT_OCTAVES: usize = 6; - pub const DEFAULT_FREQUENCY: f64 = 2.0; - pub const DEFAULT_LACUNARITY: f64 = core::f64::consts::PI * 2.0 / 3.0; - pub const DEFAULT_PERSISTENCE: f64 = 0.25; - pub const MAX_OCTAVES: usize = 32; - - pub fn new(seed: u32) -> Self { - Self { - seed, - octaves: Self::DEFAULT_OCTAVES, - frequency: Self::DEFAULT_FREQUENCY, - lacunarity: Self::DEFAULT_LACUNARITY, - persistence: Self::DEFAULT_PERSISTENCE, - sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVES), +macro_rules! impl_hybridmulti { + ($f:ty) => { + impl HybridMulti<$f> { + pub const DEFAULT_SEED: u32 = 0; + pub const DEFAULT_OCTAVES: usize = 6; + pub const DEFAULT_FREQUENCY: $f = 2.0; + pub const DEFAULT_LACUNARITY: $f = 2.0; + pub const DEFAULT_PERSISTENCE: $f = 0.25; + pub const MAX_OCTAVES: usize = 32; + + pub fn new(seed: u32) -> Self { + Self { + seed, + octaves: Self::DEFAULT_OCTAVES, + frequency: Self::DEFAULT_FREQUENCY, + lacunarity: Self::DEFAULT_LACUNARITY, + persistence: Self::DEFAULT_PERSISTENCE, + sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVES), + } + } } - } -} - -impl Default for HybridMulti { - fn default() -> Self { - Self::new(Self::DEFAULT_SEED) - } -} -impl MultiFractal for HybridMulti { - fn set_octaves(self, mut octaves: usize) -> Self { - if self.octaves == octaves { - return self; + impl Default for HybridMulti<$f> { + fn default() -> Self { + Self::new(Self::DEFAULT_SEED) + } } - octaves = octaves.clamp(1, Self::MAX_OCTAVES); - Self { - octaves, - sources: super::build_sources(self.seed, octaves), - ..self + impl MultiFractal for HybridMulti<$f> { + type F = $f; + + fn set_octaves(self, mut octaves: usize) -> Self { + if self.octaves == octaves { + return self; + } + + octaves = octaves.clamp(1, Self::MAX_OCTAVES); + Self { + octaves, + sources: super::build_sources(self.seed, octaves), + ..self + } + } + + fn set_frequency(self, frequency: Self::F) -> Self { + Self { frequency, ..self } + } + + fn set_lacunarity(self, lacunarity: Self::F) -> Self { + Self { lacunarity, ..self } + } + + fn set_persistence(self, persistence: Self::F) -> Self { + Self { + persistence, + ..self + } + } } - } - - fn set_frequency(self, frequency: f64) -> Self { - Self { frequency, ..self } - } - - fn set_lacunarity(self, lacunarity: f64) -> Self { - Self { lacunarity, ..self } - } - - fn set_persistence(self, persistence: f64) -> Self { - Self { - persistence, - ..self - } - } + }; } -impl Seedable for HybridMulti { +impl_hybridmulti!(f32); +impl_hybridmulti!(f64); + +impl Seedable for HybridMulti { fn set_seed(self, seed: u32) -> Self { if self.seed == seed { return self; @@ -116,110 +125,156 @@ impl Seedable for HybridMulti { } } -/// 2-dimensional `HybridMulti` noise -impl NoiseFn for HybridMulti { - fn get(&self, point: [f64; 2]) -> f64 { - let mut point = Vector2::from(point); - - // First unscaled octave of function; later octaves are scaled. - point *= self.frequency; - let mut result = self.sources[0].get(point.into_array()) * self.persistence; - let mut weight = result; +macro_rules! hybridmulti { + ($dim:expr, $vec:ident, $f:ty) => { + impl NoiseFn<$f, $dim> for HybridMulti<$f> { + fn get(&self, point: [$f; $dim]) -> $f { + let mut point = $vec::from(point); - // Spectral construction inner loop, where the fractal is built. - for x in 1..self.octaves { - // Prevent divergence. - weight = weight.max(1.0); + // First unscaled octave of function; later octaves are scaled. + point *= self.frequency; + let mut result = self.sources[0].get(point.into_array()) * self.persistence; + let mut weight = result; - // Raise the spatial frequency. - point *= self.lacunarity; + // Spectral construction inner loop, where the fractal is built. + for x in 1..self.octaves { + // Prevent divergence. + weight = weight.max(1.0); - // Get noise value. - let mut signal = self.sources[x].get(point.into_array()); + // Raise the spatial frequency. + point *= self.lacunarity; - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); + // Get noise value. + let mut signal = self.sources[x].get(point.into_array()); - // Add it in, weighted by previous octave's noise value. - result += weight * signal; + // Scale the amplitude appropriately for this frequency. + signal *= self.persistence.powi(x as i32); - // Update the weighting value. - weight *= signal; - } - - // Scale the result to the [-1,1] range - result * 3.0 - } -} - -/// 3-dimensional `HybridMulti` noise -impl NoiseFn for HybridMulti { - fn get(&self, point: [f64; 3]) -> f64 { - let mut point = Vector3::from(point); - - // First unscaled octave of function; later octaves are scaled. - point *= self.frequency; - let mut result = self.sources[0].get(point.into_array()) * self.persistence; - let mut weight = result; + // Add it in, weighted by previous octave's noise value. + result += weight * signal; - // Spectral construction inner loop, where the fractal is built. - for x in 1..self.octaves { - // Prevent divergence. - weight = weight.max(1.0); + // Update the weighting value. + weight *= signal; + } - // Raise the spatial frequency. - point *= self.lacunarity; - - // Get noise value. - let mut signal = self.sources[x].get(point.into_array()); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Add it in, weighted by previous octave's noise value. - result += weight * signal; - - // Update the weighting value. - weight *= signal; + // Scale the result to the [-1,1] range + result * 3.0 + } } - - // Scale the result to the [-1,1] range - result * 3.0 - } + }; } -/// 4-dimensional `HybridMulti` noise -impl NoiseFn for HybridMulti { - fn get(&self, point: [f64; 4]) -> f64 { - let mut point = Vector4::from(point); - - // First unscaled octave of function; later octaves are scaled. - point *= self.frequency; - let mut result = self.sources[0].get(point.into_array()) * self.persistence; - let mut weight = result; - - // Spectral construction inner loop, where the fractal is built. - for x in 1..self.octaves { - // Prevent divergence. - weight = weight.max(1.0); - - // Raise the spatial frequency. - point *= self.lacunarity; - - // Get noise value. - let mut signal = self.sources[x].get(point.into_array()); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Add it in, weighted by previous octave's noise value. - result += weight * signal; - - // Update the weighting value. - weight *= signal; - } - - // Scale the result to the [-1,1] range - result * 3.0 - } -} +hybridmulti!(2, Vector2, f32); +hybridmulti!(3, Vector3, f32); +hybridmulti!(4, Vector4, f32); +hybridmulti!(2, Vector2, f64); +hybridmulti!(3, Vector3, f64); +hybridmulti!(4, Vector4, f64); + +// /// 2-dimensional `HybridMulti` noise +// impl NoiseFn for HybridMulti { +// fn get(&self, point: [f64; 2]) -> f64 { +// let mut point = Vector2::from(point); +// +// // First unscaled octave of function; later octaves are scaled. +// point *= self.frequency; +// let mut result = self.sources[0].get(point.into_array()) * self.persistence; +// let mut weight = result; +// +// // Spectral construction inner loop, where the fractal is built. +// for x in 1..self.octaves { +// // Prevent divergence. +// weight = weight.max(1.0); +// +// // Raise the spatial frequency. +// point *= self.lacunarity; +// +// // Get noise value. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add it in, weighted by previous octave's noise value. +// result += weight * signal; +// +// // Update the weighting value. +// weight *= signal; +// } +// +// // Scale the result to the [-1,1] range +// result * 3.0 +// } +// } +// +// /// 3-dimensional `HybridMulti` noise +// impl NoiseFn for HybridMulti { +// fn get(&self, point: [f64; 3]) -> f64 { +// let mut point = Vector3::from(point); +// +// // First unscaled octave of function; later octaves are scaled. +// point *= self.frequency; +// let mut result = self.sources[0].get(point.into_array()) * self.persistence; +// let mut weight = result; +// +// // Spectral construction inner loop, where the fractal is built. +// for x in 1..self.octaves { +// // Prevent divergence. +// weight = weight.max(1.0); +// +// // Raise the spatial frequency. +// point *= self.lacunarity; +// +// // Get noise value. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add it in, weighted by previous octave's noise value. +// result += weight * signal; +// +// // Update the weighting value. +// weight *= signal; +// } +// +// // Scale the result to the [-1,1] range +// result * 3.0 +// } +// } +// +// /// 4-dimensional `HybridMulti` noise +// impl NoiseFn for HybridMulti { +// fn get(&self, point: [f64; 4]) -> f64 { +// let mut point = Vector4::from(point); +// +// // First unscaled octave of function; later octaves are scaled. +// point *= self.frequency; +// let mut result = self.sources[0].get(point.into_array()) * self.persistence; +// let mut weight = result; +// +// // Spectral construction inner loop, where the fractal is built. +// for x in 1..self.octaves { +// // Prevent divergence. +// weight = weight.max(1.0); +// +// // Raise the spatial frequency. +// point *= self.lacunarity; +// +// // Get noise value. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add it in, weighted by previous octave's noise value. +// result += weight * signal; +// +// // Update the weighting value. +// weight *= signal; +// } +// +// // Scale the result to the [-1,1] range +// result * 3.0 +// } +// } diff --git a/src/noise_fns/generators/fractals/ridgedmulti.rs b/src/noise_fns/generators/fractals/ridgedmulti.rs index 5dceadad..46d6d79f 100644 --- a/src/noise_fns/generators/fractals/ridgedmulti.rs +++ b/src/noise_fns/generators/fractals/ridgedmulti.rs @@ -3,6 +3,7 @@ use crate::{ noise_fns::{MultiFractal, NoiseFn, Perlin, Seedable}, }; use alloc::vec::Vec; +use num_traits::Float; /// Noise function that outputs ridged-multifractal noise. /// @@ -21,7 +22,7 @@ use alloc::vec::Vec; /// Ridged-multifractal noise is often used to generate craggy mountainous /// terrain or marble-like textures. #[derive(Clone, Debug)] -pub struct RidgedMulti { +pub struct RidgedMulti { /// Total number of frequency octaves to generate the noise with. /// /// The number of octaves control the _amount of detail_ in the noise @@ -30,7 +31,7 @@ pub struct RidgedMulti { pub octaves: usize, /// The number of cycles per unit length that the noise function outputs. - pub frequency: f64, + pub frequency: F, /// A multiplier that determines how quickly the frequency increases for /// each successive octave in the noise function. @@ -40,7 +41,7 @@ pub struct RidgedMulti { /// /// A lacunarity of 2.0 results in the frequency doubling every octave. For /// almost all cases, 2.0 is a good value to use. - pub lacunarity: f64, + pub lacunarity: F, /// A multiplier that determines how quickly the amplitudes diminish for /// each successive octave in the noise function. @@ -48,84 +49,93 @@ pub struct RidgedMulti { /// The amplitude of each successive octave is equal to the product of the /// previous octave's amplitude and the persistence value. Increasing the /// persistence produces "rougher" noise. - pub persistence: f64, + pub persistence: F, /// The attenuation to apply to the weight on each octave. This reduces /// the strength of each successive octave, making their respective /// ridges smaller. The default attenuation is 2.0, making each octave /// half the height of the previous. - pub attenuation: f64, + pub attenuation: F, seed: u32, sources: Vec, } -impl RidgedMulti { - pub const DEFAULT_SEED: u32 = 0; - pub const DEFAULT_OCTAVE_COUNT: usize = 6; - pub const DEFAULT_FREQUENCY: f64 = 1.0; - pub const DEFAULT_LACUNARITY: f64 = core::f64::consts::PI * 2.0 / 3.0; - pub const DEFAULT_PERSISTENCE: f64 = 1.0; - pub const DEFAULT_ATTENUATION: f64 = 2.0; - pub const MAX_OCTAVES: usize = 32; - - pub fn new(seed: u32) -> Self { - Self { - seed, - octaves: Self::DEFAULT_OCTAVE_COUNT, - frequency: Self::DEFAULT_FREQUENCY, - lacunarity: Self::DEFAULT_LACUNARITY, - persistence: Self::DEFAULT_PERSISTENCE, - attenuation: Self::DEFAULT_ATTENUATION, - sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVE_COUNT), - } - } - - pub fn set_attenuation(self, attenuation: f64) -> Self { - Self { - attenuation, - ..self - } - } -} - -impl Default for RidgedMulti { - fn default() -> Self { - Self::new(Self::DEFAULT_SEED) - } -} - -impl MultiFractal for RidgedMulti { - fn set_octaves(self, mut octaves: usize) -> Self { - if self.octaves == octaves { - return self; +macro_rules! impl_ridgedmulti { + ($f:ty) => { + impl RidgedMulti<$f> { + pub const DEFAULT_SEED: u32 = 0; + pub const DEFAULT_OCTAVE_COUNT: usize = 6; + pub const DEFAULT_FREQUENCY: $f = 1.0; + pub const DEFAULT_LACUNARITY: $f = 2.0; + pub const DEFAULT_PERSISTENCE: $f = 1.0; + pub const DEFAULT_ATTENUATION: $f = 2.0; + pub const MAX_OCTAVES: usize = 32; + + pub fn new(seed: u32) -> Self { + Self { + seed, + octaves: Self::DEFAULT_OCTAVE_COUNT, + frequency: Self::DEFAULT_FREQUENCY, + lacunarity: Self::DEFAULT_LACUNARITY, + persistence: Self::DEFAULT_PERSISTENCE, + attenuation: Self::DEFAULT_ATTENUATION, + sources: super::build_sources(Self::DEFAULT_SEED, Self::DEFAULT_OCTAVE_COUNT), + } + } + + pub fn set_attenuation(self, attenuation: $f) -> Self { + Self { + attenuation, + ..self + } + } } - octaves = octaves.clamp(1, Self::MAX_OCTAVES); - Self { - octaves, - sources: super::build_sources(self.seed, octaves), - ..self + impl Default for RidgedMulti<$f> { + fn default() -> Self { + Self::new(Self::DEFAULT_SEED) + } } - } - - fn set_frequency(self, frequency: f64) -> Self { - Self { frequency, ..self } - } - - fn set_lacunarity(self, lacunarity: f64) -> Self { - Self { lacunarity, ..self } - } - fn set_persistence(self, persistence: f64) -> Self { - Self { - persistence, - ..self + impl MultiFractal for RidgedMulti<$f> { + type F = $f; + + fn set_octaves(self, mut octaves: usize) -> Self { + if self.octaves == octaves { + return self; + } + + octaves = octaves.clamp(1, Self::MAX_OCTAVES); + Self { + octaves, + sources: super::build_sources(self.seed, octaves), + ..self + } + } + + fn set_frequency(self, frequency: Self::F) -> Self { + Self { frequency, ..self } + } + + fn set_lacunarity(self, lacunarity: Self::F) -> Self { + Self { lacunarity, ..self } + } + + fn set_persistence(self, persistence: Self::F) -> Self { + Self { + persistence, + ..self + } + } } - } + }; } -impl Seedable for RidgedMulti { +impl_ridgedmulti!(f32); +impl_ridgedmulti!(f64); + +impl Seedable for RidgedMulti { fn set_seed(self, seed: u32) -> Self { if self.seed == seed { return self; @@ -143,146 +153,205 @@ impl Seedable for RidgedMulti { } } -/// 2-dimensional `RidgedMulti` noise -impl NoiseFn for RidgedMulti { - fn get(&self, point: [f64; 2]) -> f64 { - let mut point = Vector2::from(point); +macro_rules! ridgedmulti { + ($dim:expr, $vec:ident, $f:ty) => { + impl NoiseFn<$f, $dim> for RidgedMulti<$f> { + fn get(&self, point: [$f; $dim]) -> $f { + let mut point = $vec::from(point); - let mut result = 0.0; - let mut weight = 1.0; + let mut result = 0.0; + let mut weight = 1.0; - point *= self.frequency; + point *= self.frequency; - for x in 0..self.octaves { - // Get the value. - let mut signal = self.sources[x].get(point.into_array()); + for x in 0..self.octaves { + // Get the value. + let mut signal = self.sources[x].get(point.into_array()); - // Make the ridges. - signal = signal.abs(); - signal = 1.0 - signal; + // Make the ridges. + signal = signal.abs(); + signal = 1.0 - signal; - // Square the signal to increase the sharpness of the ridges. - signal *= signal; + // Square the signal to increase the sharpness of the ridges. + signal *= signal; - // Apply the weighting from the previous octave to the signal. - // Larger values have higher weights, producing sharp points along - // the ridges. - signal *= weight; + // Apply the weighting from the previous octave to the signal. + // Larger values have higher weights, producing sharp points along + // the ridges. + signal *= weight; - // Weight successive contributions by the previous signal. - weight = signal / self.attenuation; + // Weight successive contributions by the previous signal. + weight = signal / self.attenuation; - // Clamp the weight to [0,1] to prevent the result from diverging. - weight = weight.clamp(0.0, 1.0); + // Clamp the weight to [0,1] to prevent the result from diverging. + weight = weight.clamp(0.0, 1.0); - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); + // Scale the amplitude appropriately for this frequency. + signal *= self.persistence.powi(x as i32); - // Add the signal to the result. - result += signal; + // Add the signal to the result. + result += signal; - // Increase the frequency. - point *= self.lacunarity; - } + // Increase the frequency. + point *= self.lacunarity; + } - // Scale and shift the result into the [-1,1] range - let scale = 2.0 - 0.5_f64.powi(self.octaves as i32 - 1); - scale_shift(result, 2.0 / scale) - } -} - -/// 3-dimensional `RidgedMulti` noise -impl NoiseFn for RidgedMulti { - fn get(&self, point: [f64; 3]) -> f64 { - let mut point = Vector3::from(point); - - let mut result = 0.0; - let mut weight = 1.0; - - point *= self.frequency; - - for x in 0..self.octaves { - // Get the value. - let mut signal = self.sources[x].get(point.into_array()); - - // Make the ridges. - signal = signal.abs(); - signal = 1.0 - signal; - - // Square the signal to increase the sharpness of the ridges. - signal *= signal; - - // Apply the weighting from the previous octave to the signal. - // Larger values have higher weights, producing sharp points along - // the ridges. - signal *= weight; - - // Weight successive contributions by the previous signal. - weight = signal / self.attenuation; - - // Clamp the weight to [0,1] to prevent the result from diverging. - weight = weight.clamp(0.0, 1.0); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Add the signal to the result. - result += signal; - - // Increase the frequency. - point *= self.lacunarity; + // Scale and shift the result into the [-1,1] range + let scale = 2.0 - 0.5.powi(self.octaves as i32 - 1); + scale_shift(result, 2.0 / scale) + } } - - // Scale and shift the result into the [-1,1] range - let scale = 2.0 - 0.5_f64.powi(self.octaves as i32 - 1); - scale_shift(result, 2.0 / scale) - } + }; } -/// 4-dimensional `RidgedMulti` noise -impl NoiseFn for RidgedMulti { - fn get(&self, point: [f64; 4]) -> f64 { - let mut point = Vector4::from(point); - - let mut result = 0.0; - let mut weight = 1.0; - - point *= self.frequency; - - for x in 0..self.octaves { - // Get the value. - let mut signal = self.sources[x].get(point.into_array()); - - // Make the ridges. - signal = signal.abs(); - signal = 1.0 - signal; - - // Square the signal to increase the sharpness of the ridges. - signal *= signal; - - // Apply the weighting from the previous octave to the signal. - // Larger values have higher weights, producing sharp points along - // the ridges. - signal *= weight; - - // Weight successive contributions by the previous signal. - weight = signal / self.attenuation; - - // Clamp the weight to [0,1] to prevent the result from diverging. - weight = weight.clamp(0.0, 1.0); - - // Scale the amplitude appropriately for this frequency. - signal *= self.persistence.powi(x as i32); - - // Add the signal to the result. - result += signal; - - // Increase the frequency. - point *= self.lacunarity; - } - - // Scale and shift the result into the [-1,1] range - let scale = 2.0 - 0.5_f64.powi(self.octaves as i32 - 1); - scale_shift(result, 2.0 / scale) - } -} +ridgedmulti!(2, Vector2, f32); +ridgedmulti!(3, Vector3, f32); +ridgedmulti!(4, Vector4, f32); +ridgedmulti!(2, Vector2, f64); +ridgedmulti!(3, Vector3, f64); +ridgedmulti!(4, Vector4, f64); + +// +// /// 2-dimensional `RidgedMulti` noise +// impl NoiseFn for RidgedMulti { +// fn get(&self, point: [f64; 2]) -> f64 { +// let mut point = Vector2::from(point); +// +// let mut result = 0.0; +// let mut weight = 1.0; +// +// point *= self.frequency; +// +// for x in 0..self.octaves { +// // Get the value. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Make the ridges. +// signal = signal.abs(); +// signal = 1.0 - signal; +// +// // Square the signal to increase the sharpness of the ridges. +// signal *= signal; +// +// // Apply the weighting from the previous octave to the signal. +// // Larger values have higher weights, producing sharp points along +// // the ridges. +// signal *= weight; +// +// // Weight successive contributions by the previous signal. +// weight = signal / self.attenuation; +// +// // Clamp the weight to [0,1] to prevent the result from diverging. +// weight = weight.clamp(0.0, 1.0); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add the signal to the result. +// result += signal; +// +// // Increase the frequency. +// point *= self.lacunarity; +// } +// +// // Scale and shift the result into the [-1,1] range +// let scale = 2.0 - 0.5_f64.powi(self.octaves as i32 - 1); +// scale_shift(result, 2.0 / scale) +// } +// } +// +// /// 3-dimensional `RidgedMulti` noise +// impl NoiseFn for RidgedMulti { +// fn get(&self, point: [f64; 3]) -> f64 { +// let mut point = Vector3::from(point); +// +// let mut result = 0.0; +// let mut weight = 1.0; +// +// point *= self.frequency; +// +// for x in 0..self.octaves { +// // Get the value. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Make the ridges. +// signal = signal.abs(); +// signal = 1.0 - signal; +// +// // Square the signal to increase the sharpness of the ridges. +// signal *= signal; +// +// // Apply the weighting from the previous octave to the signal. +// // Larger values have higher weights, producing sharp points along +// // the ridges. +// signal *= weight; +// +// // Weight successive contributions by the previous signal. +// weight = signal / self.attenuation; +// +// // Clamp the weight to [0,1] to prevent the result from diverging. +// weight = weight.clamp(0.0, 1.0); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add the signal to the result. +// result += signal; +// +// // Increase the frequency. +// point *= self.lacunarity; +// } +// +// // Scale and shift the result into the [-1,1] range +// let scale = 2.0 - 0.5_f64.powi(self.octaves as i32 - 1); +// scale_shift(result, 2.0 / scale) +// } +// } +// +// /// 4-dimensional `RidgedMulti` noise +// impl NoiseFn for RidgedMulti { +// fn get(&self, point: [f64; 4]) -> f64 { +// let mut point = Vector4::from(point); +// +// let mut result = 0.0; +// let mut weight = 1.0; +// +// point *= self.frequency; +// +// for x in 0..self.octaves { +// // Get the value. +// let mut signal = self.sources[x].get(point.into_array()); +// +// // Make the ridges. +// signal = signal.abs(); +// signal = 1.0 - signal; +// +// // Square the signal to increase the sharpness of the ridges. +// signal *= signal; +// +// // Apply the weighting from the previous octave to the signal. +// // Larger values have higher weights, producing sharp points along +// // the ridges. +// signal *= weight; +// +// // Weight successive contributions by the previous signal. +// weight = signal / self.attenuation; +// +// // Clamp the weight to [0,1] to prevent the result from diverging. +// weight = weight.clamp(0.0, 1.0); +// +// // Scale the amplitude appropriately for this frequency. +// signal *= self.persistence.powi(x as i32); +// +// // Add the signal to the result. +// result += signal; +// +// // Increase the frequency. +// point *= self.lacunarity; +// } +// +// // Scale and shift the result into the [-1,1] range +// let scale = 2.0 - 0.5_f64.powi(self.octaves as i32 - 1); +// scale_shift(result, 2.0 / scale) +// } +// } diff --git a/src/noise_fns/generators/perlin.rs b/src/noise_fns/generators/perlin.rs index 19dda591..60402f95 100644 --- a/src/noise_fns/generators/perlin.rs +++ b/src/noise_fns/generators/perlin.rs @@ -48,23 +48,44 @@ impl Seedable for Perlin { } } +/// 2-dimensional perlin noise +impl NoiseFn for Perlin { + fn get(&self, point: [f32; 2]) -> f32 { + perlin_2d_f32(point, &self.perm_table) + } +} + +/// 3-dimensional perlin noise +impl NoiseFn for Perlin { + fn get(&self, point: [f32; 3]) -> f32 { + perlin_3d_f32(point, &self.perm_table) + } +} + +/// 4-dimensional perlin noise +impl NoiseFn for Perlin { + fn get(&self, point: [f32; 4]) -> f32 { + perlin_4d_f32(point, &self.perm_table) + } +} + /// 2-dimensional perlin noise impl NoiseFn for Perlin { fn get(&self, point: [f64; 2]) -> f64 { - perlin_2d(point, &self.perm_table) + perlin_2d_f64(point, &self.perm_table) } } /// 3-dimensional perlin noise impl NoiseFn for Perlin { fn get(&self, point: [f64; 3]) -> f64 { - perlin_3d(point, &self.perm_table) + perlin_3d_f64(point, &self.perm_table) } } /// 4-dimensional perlin noise impl NoiseFn for Perlin { fn get(&self, point: [f64; 4]) -> f64 { - perlin_4d(point, &self.perm_table) + perlin_4d_f64(point, &self.perm_table) } } diff --git a/src/noise_fns/generators/simplex.rs b/src/noise_fns/generators/simplex.rs index 587ac4e1..63fcafa2 100644 --- a/src/noise_fns/generators/simplex.rs +++ b/src/noise_fns/generators/simplex.rs @@ -49,10 +49,28 @@ impl Seedable for Simplex { } } +/// 2-dimensional Simplex noise +impl NoiseFn for Simplex { + fn get(&self, point: [f32; 2]) -> f32 { + let (result, _) = simplex_2d_f32(point, &self.hasher); + + result + } +} + /// 2-dimensional Simplex noise impl NoiseFn for Simplex { fn get(&self, point: [f64; 2]) -> f64 { - let (result, _) = simplex_2d(point, &self.hasher); + let (result, _) = simplex_2d_f64(point, &self.hasher); + + result + } +} + +/// 3-dimensional Simplex noise +impl NoiseFn for Simplex { + fn get(&self, point: [f32; 3]) -> f32 { + let (result, _) = simplex_3d_f32(point, &self.hasher); result } @@ -61,7 +79,16 @@ impl NoiseFn for Simplex { /// 3-dimensional Simplex noise impl NoiseFn for Simplex { fn get(&self, point: [f64; 3]) -> f64 { - let (result, _) = simplex_3d(point, &self.hasher); + let (result, _) = simplex_3d_f64(point, &self.hasher); + + result + } +} + +/// 4-dimensional Simplex noise +impl NoiseFn for Simplex { + fn get(&self, point: [f32; 4]) -> f32 { + let (result, _) = simplex_4d_f32(point, &self.hasher); result } @@ -70,7 +97,7 @@ impl NoiseFn for Simplex { /// 4-dimensional Simplex noise impl NoiseFn for Simplex { fn get(&self, point: [f64; 4]) -> f64 { - let (result, _) = simplex_4d(point, &self.hasher); + let (result, _) = simplex_4d_f64(point, &self.hasher); result } diff --git a/src/noise_fns/modifiers/abs.rs b/src/noise_fns/modifiers/abs.rs index 0a4472d3..eb48a28a 100644 --- a/src/noise_fns/modifiers/abs.rs +++ b/src/noise_fns/modifiers/abs.rs @@ -1,21 +1,22 @@ use crate::noise_fns::NoiseFn; use core::marker::PhantomData; +use num_traits::Float; /// Noise function that outputs the absolute value of the output value from the /// source function. -pub struct Abs +pub struct Abs where - Source: NoiseFn, + Source: NoiseFn, { /// Outputs a value. pub source: Source, - phantom: PhantomData, + phantom: PhantomData, } -impl Abs +impl Abs where - Source: NoiseFn, + Source: NoiseFn, { pub fn new(source: Source) -> Self { Self { @@ -25,11 +26,12 @@ where } } -impl NoiseFn for Abs +impl NoiseFn for Abs where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { (self.source.get(point)).abs() } } diff --git a/src/noise_fns/modifiers/clamp.rs b/src/noise_fns/modifiers/clamp.rs index 5cb9249f..a5ec1dc5 100644 --- a/src/noise_fns/modifiers/clamp.rs +++ b/src/noise_fns/modifiers/clamp.rs @@ -1,48 +1,52 @@ use crate::noise_fns::NoiseFn; -use core::marker::PhantomData; +use num_traits::Float; /// Noise function that clamps the output value from the source function to a /// range of values. -pub struct Clamp +pub struct Clamp where - Source: NoiseFn, + Source: NoiseFn, { /// Outputs a value. pub source: Source, /// Bound of the clamping range. Default is -1.0 to 1.0. - pub bounds: (f64, f64), - - phantom: PhantomData, + pub bounds: (F, F), } -impl Clamp +impl Clamp where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { pub fn new(source: Source) -> Self { Self { source, - bounds: (-1.0, 1.0), - phantom: PhantomData, + bounds: (-F::one(), F::one()), } } - pub fn set_lower_bound(self, lower_bound: f64) -> Self { + pub fn set_lower_bound(self, lower_bound: F) -> Self { + assert!(lower_bound <= self.bounds.1); + Self { bounds: (lower_bound, self.bounds.1), ..self } } - pub fn set_upper_bound(self, upper_bound: f64) -> Self { + pub fn set_upper_bound(self, upper_bound: F) -> Self { + assert!(self.bounds.0 <= upper_bound); + Self { bounds: (self.bounds.0, upper_bound), ..self } } - pub fn set_bounds(self, lower_bound: f64, upper_bound: f64) -> Self { + pub fn set_bounds(self, lower_bound: F, upper_bound: F) -> Self { + assert!(lower_bound <= upper_bound); + Self { bounds: (lower_bound, upper_bound), ..self @@ -50,13 +54,20 @@ where } } -impl NoiseFn for Clamp +impl NoiseFn for Clamp where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { let value = self.source.get(point); - value.clamp(self.bounds.0, self.bounds.1) + if value < self.bounds.0 { + self.bounds.0 + } else if value > self.bounds.1 { + self.bounds.1 + } else { + value + } } } diff --git a/src/noise_fns/modifiers/curve.rs b/src/noise_fns/modifiers/curve.rs index 5656888b..ea680d06 100644 --- a/src/noise_fns/modifiers/curve.rs +++ b/src/noise_fns/modifiers/curve.rs @@ -1,6 +1,6 @@ use crate::{math::interpolate, noise_fns::NoiseFn}; use alloc::vec::Vec; -use core::marker::PhantomData; +use num_traits::Float; /// Noise function that maps the output value from the source function onto an /// arbitrary function curve. @@ -16,42 +16,40 @@ use core::marker::PhantomData; /// four control points to the curve. If there is less than four control /// points, the get() method panics. Each control point can have any input /// and output value, although no two control points can have the same input. -pub struct Curve +pub struct Curve where - Source: NoiseFn, + Source: NoiseFn, { /// Outputs a value. pub source: Source, /// Vec that stores the control points. - control_points: Vec>, - - phantom: PhantomData, + control_points: Vec>, } -struct ControlPoint { - input: T, - output: T, +struct ControlPoint { + input: F, + output: F, } -impl Curve +impl Curve where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { pub fn new(source: Source) -> Self { Self { source, control_points: Vec::with_capacity(4), - phantom: PhantomData, } } - pub fn add_control_point(mut self, input_value: f64, output_value: f64) -> Self { + pub fn add_control_point(mut self, input_value: F, output_value: F) -> Self { // check to see if the vector already contains the input point. if !self .control_points .iter() - .any(|x| (x.input - input_value).abs() < f64::EPSILON) + .any(|x| (x.input - input_value).abs() < F::from(f64::EPSILON).unwrap()) { // it doesn't, so find the correct position to insert the new // control point. @@ -75,11 +73,12 @@ where } } -impl NoiseFn for Curve +impl NoiseFn for Curve where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { // confirm that there's at least 4 control points in the vector. assert!(self.control_points.len() >= 4); diff --git a/src/noise_fns/modifiers/exponent.rs b/src/noise_fns/modifiers/exponent.rs index 0599ce7f..6c66b4ed 100644 --- a/src/noise_fns/modifiers/exponent.rs +++ b/src/noise_fns/modifiers/exponent.rs @@ -1,5 +1,5 @@ use crate::{math::scale_shift, noise_fns::NoiseFn}; -use core::marker::PhantomData; +use num_traits::Float; /// Noise function that maps the output value from the source function onto an /// exponential curve. @@ -8,46 +8,45 @@ use core::marker::PhantomData; /// this noise function first normalizes the output value (the range becomes 0.0 /// to 1.0), maps that value onto an exponential curve, then rescales that /// value back to the original range. -pub struct Exponent +pub struct Exponent where - Source: NoiseFn, + Source: NoiseFn, { /// Outputs a value. pub source: Source, /// Exponent to apply to the output value from the source function. Default /// is 1.0. - pub exponent: f64, - - phantom: PhantomData, + pub exponent: F, } -impl Exponent +impl Exponent where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { pub fn new(source: Source) -> Self { Self { source, - exponent: 1.0, - phantom: PhantomData, + exponent: F::one(), } } - pub fn set_exponent(self, exponent: f64) -> Self { + pub fn set_exponent(self, exponent: F) -> Self { Self { exponent, ..self } } } -impl NoiseFn for Exponent +impl NoiseFn for Exponent where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { let mut value = self.source.get(point); - value = (value + 1.0) / 2.0; + value = (value + F::one()) / (F::one() + F::one()); value = value.abs(); value = value.powf(self.exponent); - scale_shift(value, 2.0) + scale_shift(value, F::one() + F::one()) } } diff --git a/src/noise_fns/modifiers/negate.rs b/src/noise_fns/modifiers/negate.rs index ba190308..a8f63968 100644 --- a/src/noise_fns/modifiers/negate.rs +++ b/src/noise_fns/modifiers/negate.rs @@ -1,20 +1,21 @@ use crate::noise_fns::NoiseFn; use core::marker::PhantomData; +use num_traits::Float; /// Noise function that negates the output value from the source function. -pub struct Negate +pub struct Negate where - Source: NoiseFn, + Source: NoiseFn, { /// Outputs a value. pub source: Source, - phantom: PhantomData, + phantom: PhantomData, } -impl Negate +impl Negate where - Source: NoiseFn, + Source: NoiseFn, { pub fn new(source: Source) -> Self { Negate { @@ -24,11 +25,12 @@ where } } -impl NoiseFn for Negate +impl NoiseFn for Negate where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { -self.source.get(point) } } diff --git a/src/noise_fns/modifiers/scale_bias.rs b/src/noise_fns/modifiers/scale_bias.rs index 6f7d4ea0..73a95444 100644 --- a/src/noise_fns/modifiers/scale_bias.rs +++ b/src/noise_fns/modifiers/scale_bias.rs @@ -1,59 +1,58 @@ use crate::noise_fns::NoiseFn; -use core::marker::PhantomData; +use num_traits::{Float, MulAdd}; /// Noise function that applies a scaling factor and a bias to the output value /// from the source function. /// /// The function retrieves the output value from the source function, multiplies /// it with the scaling factor, adds the bias to it, then outputs the value. -pub struct ScaleBias { +pub struct ScaleBias { /// Outputs a value. pub source: Source, /// Scaling factor to apply to the output value from the source function. /// The default value is 1.0. - pub scale: f64, + pub scale: F, /// Bias to apply to the scaled output value from the source function. /// The default value is 0.0. - pub bias: f64, - - phantom: PhantomData, + pub bias: F, } -impl ScaleBias +impl ScaleBias where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { pub fn new(source: Source) -> Self { Self { source, - scale: 1.0, - bias: 0.0, - phantom: PhantomData, + scale: F::one(), + bias: F::zero(), } } - pub fn set_scale(self, scale: f64) -> Self { + pub fn set_scale(self, scale: F) -> Self { Self { scale, ..self } } - pub fn set_bias(self, bias: f64) -> Self { + pub fn set_bias(self, bias: F) -> Self { Self { bias, ..self } } } -impl NoiseFn for ScaleBias +impl NoiseFn for ScaleBias where - Source: NoiseFn, + F: Float + MulAdd, + Source: NoiseFn, { #[cfg(not(target_os = "emscripten"))] - fn get(&self, point: [T; DIM]) -> f64 { - (self.source.get(point)).mul_add(self.scale, self.bias) + fn get(&self, point: [F; DIM]) -> F { + MulAdd::mul_add(self.source.get(point), self.scale, self.bias) } #[cfg(target_os = "emscripten")] - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> f64 { (self.source.get(point) * self.scale) + self.bias } } diff --git a/src/noise_fns/modifiers/terrace.rs b/src/noise_fns/modifiers/terrace.rs index 7bada27f..12496efc 100644 --- a/src/noise_fns/modifiers/terrace.rs +++ b/src/noise_fns/modifiers/terrace.rs @@ -1,6 +1,7 @@ use crate::{math::interpolate, noise_fns::NoiseFn}; use alloc::vec::Vec; -use core::marker::PhantomData; +use core::{marker::PhantomData, ops::MulAssign}; +use num_traits::{Float, MulAdd}; /// Noise function that maps the output value from the source function onto a /// terrace-forming curve. @@ -25,9 +26,10 @@ use core::marker::PhantomData; /// /// This noise function is often used to generate terrain features such as the /// stereotypical desert canyon. -pub struct Terrace +pub struct Terrace where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { /// Outputs a value. pub source: Source, @@ -37,14 +39,15 @@ where pub invert_terraces: bool, /// Vec that stores the control points. - control_points: Vec, + control_points: Vec, - phantom: PhantomData, + phantom: PhantomData, } -impl Terrace +impl Terrace where - Source: NoiseFn, + F: Float, + Source: NoiseFn, { pub fn new(source: Source) -> Self { Terrace { @@ -62,12 +65,12 @@ where /// At the control points, its slope resets to zero. /// /// It does not matter which order these points are added in. - pub fn add_control_point(mut self, control_point: f64) -> Self { + pub fn add_control_point(mut self, control_point: F) -> Self { // check to see if the vector already contains the input point. if !self .control_points .iter() - .any(|&x| (x - control_point).abs() < f64::EPSILON) + .any(|&x| (x - control_point).abs() < F::from(f64::EPSILON).unwrap()) { // it doesn't, so find the correct position to insert the new // control point. @@ -95,11 +98,12 @@ where } } -impl NoiseFn for Terrace +impl NoiseFn for Terrace where - Source: NoiseFn, + F: Float + MulAdd + MulAssign, + Source: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { // confirm that there's at least 2 control points in the vector. assert!(self.control_points.len() >= 2); @@ -133,7 +137,7 @@ where let mut alpha = (source_value - input0) / (input1 - input0); if self.invert_terraces { - alpha = 1.0 - alpha; + alpha = F::one() - alpha; core::mem::swap(&mut input0, &mut input1); } diff --git a/src/noise_fns/selectors/blend.rs b/src/noise_fns/selectors/blend.rs index e63fb789..123f3161 100644 --- a/src/noise_fns/selectors/blend.rs +++ b/src/noise_fns/selectors/blend.rs @@ -1,16 +1,17 @@ use crate::{math::interpolate, noise_fns::NoiseFn}; use core::marker::PhantomData; +use num_traits::{Float, MulAdd}; /// Noise function that outputs a weighted blend of the output values from two /// source functions given the output value supplied by a control function. /// /// This noise function uses linear interpolation to perform the blending /// operation. -pub struct Blend +pub struct Blend where - Source1: NoiseFn, - Source2: NoiseFn, - Control: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, + Control: NoiseFn, { /// Outputs one of the values to blend. pub source1: Source1, @@ -24,14 +25,14 @@ where /// function. pub control: Control, - phantom: PhantomData, + phantom: PhantomData, } -impl Blend +impl Blend where - Source1: NoiseFn, - Source2: NoiseFn, - Control: NoiseFn, + Source1: NoiseFn, + Source2: NoiseFn, + Control: NoiseFn, { pub fn new(source1: Source1, source2: Source2, control: Control) -> Self { Blend { @@ -43,15 +44,15 @@ where } } -impl NoiseFn - for Blend +impl NoiseFn + for Blend where - T: Copy, - Source1: NoiseFn, - Source2: NoiseFn, - Control: NoiseFn, + F: Float + MulAdd, + Source1: NoiseFn, + Source2: NoiseFn, + Control: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { let lower = self.source1.get(point); let upper = self.source2.get(point); let control = self.control.get(point); diff --git a/src/noise_fns/selectors/select.rs b/src/noise_fns/selectors/select.rs index 05ed18c8..f42d74c0 100644 --- a/src/noise_fns/selectors/select.rs +++ b/src/noise_fns/selectors/select.rs @@ -3,14 +3,16 @@ use crate::{ noise_fns::NoiseFn, }; use core::marker::PhantomData; +use num_traits::{Float, MulAdd}; /// Noise function that outputs the value selected from one of two source /// functions chosen by the output value from a control function. -pub struct Select +pub struct Select where - Source1: NoiseFn, - Source2: NoiseFn, - Control: NoiseFn, + F: Float, + Source1: NoiseFn, + Source2: NoiseFn, + Control: NoiseFn, { /// Outputs a value. pub source1: Source1, @@ -25,56 +27,57 @@ where pub control: Control, /// Bounds of the selection range. Default is 0.0 to 1.0. - pub bounds: (f64, f64), + pub bounds: (F, F), /// Edge falloff value. Default is 0.0. - pub falloff: f64, + pub falloff: F, - phantom: PhantomData, + phantom: PhantomData, } -impl Select +impl Select where - Source1: NoiseFn, - Source2: NoiseFn, - Control: NoiseFn, + F: Float, + Source1: NoiseFn, + Source2: NoiseFn, + Control: NoiseFn, { pub fn new(source1: Source1, source2: Source2, control: Control) -> Self { Select { source1, source2, control, - bounds: (0.0, 1.0), - falloff: 0.0, + bounds: (F::zero(), F::one()), + falloff: F::zero(), phantom: PhantomData, } } - pub fn set_bounds(self, lower_bound: f64, upper_bound: f64) -> Self { + pub fn set_bounds(self, lower_bound: F, upper_bound: F) -> Self { Select { bounds: (lower_bound, upper_bound), ..self } } - pub fn set_falloff(self, falloff: f64) -> Self { + pub fn set_falloff(self, falloff: F) -> Self { Select { falloff, ..self } } } -impl NoiseFn - for Select +impl NoiseFn + for Select where - T: Copy, - Source1: NoiseFn, - Source2: NoiseFn, - Control: NoiseFn, + F: Float + MulAdd + Cubic, + Source1: NoiseFn, + Source2: NoiseFn, + Control: NoiseFn, { - fn get(&self, point: [T; DIM]) -> f64 { + fn get(&self, point: [F; DIM]) -> F { let control_value = self.control.get(point); let (lower, upper) = self.bounds; - if self.falloff > 0.0 { + if self.falloff > F::zero() { match () { _ if control_value < (lower - self.falloff) => self.source1.get(point), _ if control_value < (lower + self.falloff) => { diff --git a/src/noise_fns/transformers/turbulence.rs b/src/noise_fns/transformers/turbulence.rs index 885ea6e6..8ebac61d 100644 --- a/src/noise_fns/transformers/turbulence.rs +++ b/src/noise_fns/transformers/turbulence.rs @@ -9,87 +9,94 @@ use crate::noise_fns::{Fbm, MultiFractal, NoiseFn, Seedable}; /// turbulence, an application can modify its frequency, its power, and its /// roughness. #[derive(Clone, Debug)] -pub struct Turbulence { +pub struct Turbulence { /// Source function that outputs a value. pub source: Source, /// Frequency value for the Turbulence function. - pub frequency: f64, + pub frequency: F, /// Controls the strength of the turbulence by affecting how much each /// point is moved. - pub power: f64, + pub power: F, /// Affects the roughness of the turbulence. Higher values are rougher. pub roughness: usize, seed: u32, - x_distort_function: Fbm, - y_distort_function: Fbm, - z_distort_function: Fbm, - u_distort_function: Fbm, + x_distort_function: Fbm, + y_distort_function: Fbm, + z_distort_function: Fbm, + u_distort_function: Fbm, } -impl Turbulence { - pub const DEFAULT_SEED: u32 = 0; - pub const DEFAULT_FREQUENCY: f64 = 1.0; - pub const DEFAULT_POWER: f64 = 1.0; - pub const DEFAULT_ROUGHNESS: usize = 3; - - pub fn new(source: Source) -> Self { - Self { - source, - seed: Self::DEFAULT_SEED, - frequency: Self::DEFAULT_FREQUENCY, - power: Self::DEFAULT_POWER, - roughness: Self::DEFAULT_ROUGHNESS, - x_distort_function: Fbm::default() - .set_seed(Self::DEFAULT_SEED) - .set_octaves(Self::DEFAULT_ROUGHNESS) - .set_frequency(Self::DEFAULT_FREQUENCY), - y_distort_function: Fbm::default() - .set_seed(Self::DEFAULT_SEED + 1) - .set_octaves(Self::DEFAULT_ROUGHNESS) - .set_frequency(Self::DEFAULT_FREQUENCY), - z_distort_function: Fbm::default() - .set_seed(Self::DEFAULT_SEED + 2) - .set_octaves(Self::DEFAULT_ROUGHNESS) - .set_frequency(Self::DEFAULT_FREQUENCY), - u_distort_function: Fbm::default() - .set_seed(Self::DEFAULT_SEED + 3) - .set_octaves(Self::DEFAULT_ROUGHNESS) - .set_frequency(Self::DEFAULT_FREQUENCY), - } - } - - pub fn set_frequency(self, frequency: f64) -> Self { - Self { - frequency, - x_distort_function: self.x_distort_function.set_frequency(frequency), - y_distort_function: self.y_distort_function.set_frequency(frequency), - z_distort_function: self.z_distort_function.set_frequency(frequency), - u_distort_function: self.u_distort_function.set_frequency(frequency), - ..self - } - } - - pub fn set_power(self, power: f64) -> Self { - Self { power, ..self } - } - - pub fn set_roughness(self, roughness: usize) -> Self { - Self { - roughness, - x_distort_function: self.x_distort_function.set_octaves(roughness), - y_distort_function: self.y_distort_function.set_octaves(roughness), - z_distort_function: self.z_distort_function.set_octaves(roughness), - u_distort_function: self.u_distort_function.set_octaves(roughness), - ..self +macro_rules! impl_turbulence { + ($f:ty) => { + impl Turbulence<$f, Source> { + pub const DEFAULT_SEED: u32 = 0; + pub const DEFAULT_FREQUENCY: $f = 1.0; + pub const DEFAULT_POWER: $f = 1.0; + pub const DEFAULT_ROUGHNESS: usize = 3; + + pub fn new(source: Source) -> Self { + Self { + source, + seed: Self::DEFAULT_SEED, + frequency: Self::DEFAULT_FREQUENCY, + power: Self::DEFAULT_POWER, + roughness: Self::DEFAULT_ROUGHNESS, + x_distort_function: Fbm::default() + .set_seed(Self::DEFAULT_SEED) + .set_octaves(Self::DEFAULT_ROUGHNESS) + .set_frequency(Self::DEFAULT_FREQUENCY), + y_distort_function: Fbm::default() + .set_seed(Self::DEFAULT_SEED + 1) + .set_octaves(Self::DEFAULT_ROUGHNESS) + .set_frequency(Self::DEFAULT_FREQUENCY), + z_distort_function: Fbm::default() + .set_seed(Self::DEFAULT_SEED + 2) + .set_octaves(Self::DEFAULT_ROUGHNESS) + .set_frequency(Self::DEFAULT_FREQUENCY), + u_distort_function: Fbm::default() + .set_seed(Self::DEFAULT_SEED + 3) + .set_octaves(Self::DEFAULT_ROUGHNESS) + .set_frequency(Self::DEFAULT_FREQUENCY), + } + } + + pub fn set_frequency(self, frequency: $f) -> Self { + Self { + frequency, + x_distort_function: self.x_distort_function.set_frequency(frequency), + y_distort_function: self.y_distort_function.set_frequency(frequency), + z_distort_function: self.z_distort_function.set_frequency(frequency), + u_distort_function: self.u_distort_function.set_frequency(frequency), + ..self + } + } + + pub fn set_power(self, power: $f) -> Self { + Self { power, ..self } + } + + pub fn set_roughness(self, roughness: usize) -> Self { + Self { + roughness, + x_distort_function: self.x_distort_function.set_octaves(roughness), + y_distort_function: self.y_distort_function.set_octaves(roughness), + z_distort_function: self.z_distort_function.set_octaves(roughness), + u_distort_function: self.u_distort_function.set_octaves(roughness), + ..self + } + } } - } + }; } -impl Seedable for Turbulence { +impl_turbulence!(f32); +impl_turbulence!(f64); + +impl Seedable for Turbulence { fn set_seed(self, seed: u32) -> Self { Self { seed, @@ -106,89 +113,114 @@ impl Seedable for Turbulence { } } -impl NoiseFn for Turbulence -where - Source: NoiseFn, -{ - fn get(&self, point: [f64; 2]) -> f64 { - // First, create offsets based on the input values to keep the sampled - // points from being near a integer boundary. This is a result of - // using perlin noise, which returns zero at integer boundaries. - let x0 = point[0] + 12414.0 / 65536.0; - let y0 = point[1] + 65124.0 / 65536.0; - - let x1 = point[0] + 26519.0 / 65536.0; - let y1 = point[1] + 18128.0 / 65536.0; - - let x_distort = point[0] + (self.x_distort_function.get([x0, y0]) * self.power); - let y_distort = point[1] + (self.y_distort_function.get([x1, y1]) * self.power); - - self.source.get([x_distort, y_distort]) - } +macro_rules! turbulence_2d { + ($f:ty) => { + impl NoiseFn<$f, 2> for Turbulence<$f, Source> + where + Source: NoiseFn<$f, 2>, + { + fn get(&self, point: [$f; 2]) -> $f { + // First, create offsets based on the input values to keep the sampled + // points from being near a integer boundary. This is a result of + // using perlin noise, which returns zero at integer boundaries. + let x0 = point[0] + 12414.0 / 65536.0; + let y0 = point[1] + 65124.0 / 65536.0; + + let x1 = point[0] + 26519.0 / 65536.0; + let y1 = point[1] + 18128.0 / 65536.0; + + let x_distort = point[0] + (self.x_distort_function.get([x0, y0]) * self.power); + let y_distort = point[1] + (self.y_distort_function.get([x1, y1]) * self.power); + + self.source.get([x_distort, y_distort]) + } + } + }; } -impl NoiseFn for Turbulence -where - Source: NoiseFn, -{ - fn get(&self, point: [f64; 3]) -> f64 { - // First, create offsets based on the input values to keep the sampled - // points from being near a integer boundary. This is a result of - // using perlin noise, which returns zero at integer boundaries. - let x0 = point[0] + 12414.0 / 65536.0; - let y0 = point[1] + 65124.0 / 65536.0; - let z0 = point[2] + 31337.0 / 65536.0; - - let x1 = point[0] + 26519.0 / 65536.0; - let y1 = point[1] + 18128.0 / 65536.0; - let z1 = point[2] + 60943.0 / 65536.0; - - let x2 = point[0] + 53820.0 / 65536.0; - let y2 = point[1] + 11213.0 / 65536.0; - let z2 = point[2] + 44845.0 / 65536.0; - - let x_distort = point[0] + (self.x_distort_function.get([x0, y0, z0]) * self.power); - let y_distort = point[1] + (self.y_distort_function.get([x1, y1, z1]) * self.power); - let z_distort = point[2] + (self.z_distort_function.get([x2, y2, z2]) * self.power); - - self.source.get([x_distort, y_distort, z_distort]) - } +turbulence_2d!(f32); +turbulence_2d!(f64); + +macro_rules! turbulence_3d { + ($f:ty) => { + impl NoiseFn<$f, 3> for Turbulence<$f, Source> + where + Source: NoiseFn<$f, 3>, + { + fn get(&self, point: [$f; 3]) -> $f { + // First, create offsets based on the input values to keep the sampled + // points from being near a integer boundary. This is a result of + // using perlin noise, which returns zero at integer boundaries. + let x0 = point[0] + 12414.0 / 65536.0; + let y0 = point[1] + 65124.0 / 65536.0; + let z0 = point[2] + 31337.0 / 65536.0; + + let x1 = point[0] + 26519.0 / 65536.0; + let y1 = point[1] + 18128.0 / 65536.0; + let z1 = point[2] + 60943.0 / 65536.0; + + let x2 = point[0] + 53820.0 / 65536.0; + let y2 = point[1] + 11213.0 / 65536.0; + let z2 = point[2] + 44845.0 / 65536.0; + + let x_distort = point[0] + (self.x_distort_function.get([x0, y0, z0]) * self.power); + let y_distort = point[1] + (self.y_distort_function.get([x1, y1, z1]) * self.power); + let z_distort = point[2] + (self.z_distort_function.get([x2, y2, z2]) * self.power); + + self.source.get([x_distort, y_distort, z_distort]) + } + } + }; } -impl NoiseFn for Turbulence -where - Source: NoiseFn, -{ - fn get(&self, point: [f64; 4]) -> f64 { - // First, create offsets based on the input values to keep the sampled - // points from being near a integer boundary. This is a result of - // using perlin noise, which returns zero at integer boundaries. - let x0 = point[0] + 12414.0 / 65536.0; - let y0 = point[1] + 65124.0 / 65536.0; - let z0 = point[2] + 31337.0 / 65536.0; - let u0 = point[3] + 57948.0 / 65536.0; - - let x1 = point[0] + 26519.0 / 65536.0; - let y1 = point[1] + 18128.0 / 65536.0; - let z1 = point[2] + 60943.0 / 65536.0; - let u1 = point[3] + 48513.0 / 65536.0; - - let x2 = point[0] + 53820.0 / 65536.0; - let y2 = point[1] + 11213.0 / 65536.0; - let z2 = point[2] + 44845.0 / 65536.0; - let u2 = point[3] + 39357.0 / 65536.0; - - let x3 = point[0] + 18128.0 / 65536.0; - let y3 = point[1] + 44845.0 / 65536.0; - let z3 = point[2] + 12414.0 / 65536.0; - let u3 = point[3] + 60943.0 / 65536.0; - - let x_distort = point[0] + (self.x_distort_function.get([x0, y0, z0, u0]) * self.power); - let y_distort = point[1] + (self.y_distort_function.get([x1, y1, z1, u1]) * self.power); - let z_distort = point[2] + (self.z_distort_function.get([x2, y2, z2, u2]) * self.power); - let u_distort = point[3] + (self.u_distort_function.get([x3, y3, z3, u3]) * self.power); - - self.source - .get([x_distort, y_distort, z_distort, u_distort]) - } +turbulence_3d!(f32); +turbulence_3d!(f64); + +macro_rules! turbulence_4d { + ($f:ty) => { + impl NoiseFn<$f, 4> for Turbulence<$f, Source> + where + Source: NoiseFn<$f, 4>, + { + fn get(&self, point: [$f; 4]) -> $f { + // First, create offsets based on the input values to keep the sampled + // points from being near a integer boundary. This is a result of + // using perlin noise, which returns zero at integer boundaries. + let x0 = point[0] + 12414.0 / 65536.0; + let y0 = point[1] + 65124.0 / 65536.0; + let z0 = point[2] + 31337.0 / 65536.0; + let u0 = point[3] + 57948.0 / 65536.0; + + let x1 = point[0] + 26519.0 / 65536.0; + let y1 = point[1] + 18128.0 / 65536.0; + let z1 = point[2] + 60943.0 / 65536.0; + let u1 = point[3] + 48513.0 / 65536.0; + + let x2 = point[0] + 53820.0 / 65536.0; + let y2 = point[1] + 11213.0 / 65536.0; + let z2 = point[2] + 44845.0 / 65536.0; + let u2 = point[3] + 39357.0 / 65536.0; + + let x3 = point[0] + 18128.0 / 65536.0; + let y3 = point[1] + 44845.0 / 65536.0; + let z3 = point[2] + 12414.0 / 65536.0; + let u3 = point[3] + 60943.0 / 65536.0; + + let x_distort = + point[0] + (self.x_distort_function.get([x0, y0, z0, u0]) * self.power); + let y_distort = + point[1] + (self.y_distort_function.get([x1, y1, z1, u1]) * self.power); + let z_distort = + point[2] + (self.z_distort_function.get([x2, y2, z2, u2]) * self.power); + let u_distort = + point[3] + (self.u_distort_function.get([x3, y3, z3, u3]) * self.power); + + self.source + .get([x_distort, y_distort, z_distort, u_distort]) + } + } + }; } + +turbulence_4d!(f32); +turbulence_4d!(f64); diff --git a/src/utils/color_gradient.rs b/src/utils/color_gradient.rs index 4df0be3b..1f9ee985 100644 --- a/src/utils/color_gradient.rs +++ b/src/utils/color_gradient.rs @@ -4,26 +4,26 @@ pub type Color = [u8; 4]; #[derive(Clone, Copy, Debug, Default)] struct GradientPoint { - pos: f64, + pos: f32, color: Color, } #[derive(Clone, Copy, Debug, Default)] struct GradientDomain { - min: f64, - max: f64, + min: f32, + max: f32, } impl GradientDomain { - pub fn new(min: f64, max: f64) -> Self { + pub fn new(min: f32, max: f32) -> Self { Self { min, max } } - pub fn set_min(&mut self, min: f64) { + pub fn set_min(&mut self, min: f32) { self.min = min; } - pub fn set_max(&mut self, max: f64) { + pub fn set_max(&mut self, max: f32) { self.max = max; } } @@ -44,7 +44,7 @@ impl ColorGradient { gradient.build_grayscale_gradient() } - pub fn add_gradient_point(mut self, pos: f64, color: Color) -> Self { + pub fn add_gradient_point(mut self, pos: f32, color: Color) -> Self { let new_point = GradientPoint { pos, color }; // first check to see if the position is within the domain of the gradient. if the position @@ -63,7 +63,7 @@ impl ColorGradient { // it doesn't exist already .gradient_points .iter() - .any(|&x| (x.pos - pos).abs() < f64::EPSILON) + .any(|&x| (x.pos - pos).abs() < f32::EPSILON) { // it doesn't, so find the correct position to insert the new // control point. @@ -76,7 +76,7 @@ impl ColorGradient { self } - fn find_insertion_point(&self, pos: f64) -> usize { + fn find_insertion_point(&self, pos: f32) -> usize { self.gradient_points .iter() .position(|x| x.pos >= pos) @@ -124,7 +124,7 @@ impl ColorGradient { .add_gradient_point( 1.0, [255, 0, 0, 255]) } - pub fn get_color(&self, pos: f64) -> Color { + pub fn get_color(&self, pos: f32) -> Color { let mut color = Color::default(); // If there are no colors in the gradient, return black @@ -150,10 +150,10 @@ impl ColorGradient { } } -fn interpolate_color(color0: Color, color1: Color, alpha: f64) -> Color { - fn blend_channel(channel0: u8, channel1: u8, alpha: f64) -> u8 { - let c0 = (f64::from(channel0)) / 255.0; - let c1 = (f64::from(channel1)) / 255.0; +fn interpolate_color(color0: Color, color1: Color, alpha: f32) -> Color { + fn blend_channel(channel0: u8, channel1: u8, alpha: f32) -> u8 { + let c0 = (f32::from(channel0)) / 255.0; + let c1 = (f32::from(channel1)) / 255.0; ((c1 - c0).mul_add(alpha, c0) * 255.0) as u8 } diff --git a/src/utils/image_renderer.rs b/src/utils/image_renderer.rs index 28318802..a12666d2 100644 --- a/src/utils/image_renderer.rs +++ b/src/utils/image_renderer.rs @@ -1,5 +1,5 @@ use crate::math::interpolate; -use core::{self, f64::consts::SQRT_2}; +use core::{self, f32::consts::SQRT_2}; use super::{color_gradient::*, noise_image::*, noise_map::*}; @@ -46,23 +46,23 @@ impl ImageRenderer { self.light_enabled } - pub fn set_light_azimuth(mut self, azimuth: f64) -> Self { + pub fn set_light_azimuth(mut self, azimuth: f32) -> Self { self.light_source.set_azimuth(azimuth); self } - pub fn light_azimuth(&self) -> f64 { + pub fn light_azimuth(&self) -> f32 { self.light_source.azimuth } - pub fn set_light_brightness(mut self, brightness: f64) -> Self { + pub fn set_light_brightness(mut self, brightness: f32) -> Self { self.light_source.set_brightness(brightness); self } - pub fn light_brightness(&self) -> f64 { + pub fn light_brightness(&self) -> f32 { self.light_source.brightness } @@ -76,33 +76,33 @@ impl ImageRenderer { self.light_source.color } - pub fn set_light_contrast(mut self, contrast: f64) -> Self { + pub fn set_light_contrast(mut self, contrast: f32) -> Self { self.light_source.set_contrast(contrast); self } - pub fn light_contrast(&self) -> f64 { + pub fn light_contrast(&self) -> f32 { self.light_source.contrast } - pub fn set_light_elevation(mut self, elevation: f64) -> Self { + pub fn set_light_elevation(mut self, elevation: f32) -> Self { self.light_source.set_elevation(elevation); self } - pub fn light_elevation(&self) -> f64 { + pub fn light_elevation(&self) -> f32 { self.light_source.elevation } - pub fn set_light_intensity(mut self, intensity: f64) -> Self { + pub fn set_light_intensity(mut self, intensity: f32) -> Self { self.light_source.set_intensity(intensity); self } - pub fn light_intensity(&self) -> f64 { + pub fn light_intensity(&self) -> f32 { self.light_source.intensity } @@ -192,8 +192,8 @@ impl ImageRenderer { destination_image } - fn calc_destination_color(&self, source_color: Color, light_value: f64) -> Color { - let source = u8_array_to_f64_array(source_color); + fn calc_destination_color(&self, source_color: Color, light_value: f32) -> Color { + let source = u8_array_to_f32_array(source_color); let mut red = source[0]; let mut green = source[1]; @@ -201,9 +201,9 @@ impl ImageRenderer { if self.light_enabled { // Calculate light color - let light_red = light_value * f64::from(self.light_source.color[0]) / 255.0; - let light_green = light_value * f64::from(self.light_source.color[1]) / 255.0; - let light_blue = light_value * f64::from(self.light_source.color[2]) / 255.0; + let light_red = light_value * f32::from(self.light_source.color[0]) / 255.0; + let light_green = light_value * f32::from(self.light_source.color[1]) / 255.0; + let light_blue = light_value * f32::from(self.light_source.color[2]) / 255.0; // Apply the light color red *= light_red; @@ -313,10 +313,10 @@ impl ImageRenderer { &self, source_color: Color, background_color: Color, - light_value: f64, + light_value: f32, ) -> Color { - let source = u8_array_to_f64_array(source_color); - let background = u8_array_to_f64_array(background_color); + let source = u8_array_to_f32_array(source_color); + let background = u8_array_to_f32_array(background_color); // Blend source color and background color together using source's alpha. let mut red = interpolate::linear(source[0], background[0], source[3]); @@ -325,9 +325,9 @@ impl ImageRenderer { if self.light_enabled { // Calculate light color - let light_red = light_value * f64::from(self.light_source.color[0]) / 255.0; - let light_green = light_value * f64::from(self.light_source.color[1]) / 255.0; - let light_blue = light_value * f64::from(self.light_source.color[2]) / 255.0; + let light_red = light_value * f32::from(self.light_source.color[0]) / 255.0; + let light_green = light_value * f32::from(self.light_source.color[1]) / 255.0; + let light_blue = light_value * f32::from(self.light_source.color[2]) / 255.0; // Apply the light color red *= light_red; @@ -359,34 +359,34 @@ impl Default for ImageRenderer { #[derive(Copy, Clone, Default)] pub struct LightSource { // Azimuth of the light source, in degrees. - azimuth: f64, + azimuth: f32, // Brightness of the light source. - brightness: f64, + brightness: f32, // The color of the light source. color: Color, // The contrast between areas in light and areas in shadow. - contrast: f64, + contrast: f32, // Elevation of the light source, in degrees. - elevation: f64, + elevation: f32, // The intensity of the light source. - intensity: f64, + intensity: f32, // The cosine of the azimuth of the light source. - azimuth_cosine: f64, + azimuth_cosine: f32, // The sine of the azimuth of the light source. - azimuth_sine: f64, + azimuth_sine: f32, // The cosine of the elevation of the light source. - elevation_cosine: f64, + elevation_cosine: f32, // The sine of the elevation of the light source. - elevation_sine: f64, + elevation_sine: f32, // Used by the calc_light_intensity method to recalculate the light values // only if the light parameters change. @@ -405,20 +405,20 @@ impl LightSource { contrast: 1.0, elevation: 45.0, intensity: 1.0, - azimuth_cosine: 45.0_f64.to_radians().cos(), - azimuth_sine: 45.0_f64.to_radians().sin(), - elevation_cosine: 45.0_f64.to_radians().cos(), - elevation_sine: 45.0_f64.to_radians().sin(), + azimuth_cosine: 45.0_f32.to_radians().cos(), + azimuth_sine: 45.0_f32.to_radians().sin(), + elevation_cosine: 45.0_f32.to_radians().cos(), + elevation_sine: 45.0_f32.to_radians().sin(), recalculate_light_values: false, } } - pub fn set_azimuth(&mut self, azimuth: f64) { + pub fn set_azimuth(&mut self, azimuth: f32) { self.azimuth = azimuth; self.recalculate_light_values = true; } - pub fn set_brightness(&mut self, brightness: f64) { + pub fn set_brightness(&mut self, brightness: f32) { self.brightness = brightness; self.recalculate_light_values = true; } @@ -427,7 +427,7 @@ impl LightSource { self.color = color; } - pub fn set_contrast(&mut self, contrast: f64) { + pub fn set_contrast(&mut self, contrast: f32) { if contrast >= 0.0 { self.contrast = contrast; self.recalculate_light_values = true; @@ -436,24 +436,24 @@ impl LightSource { } } - pub fn set_elevation(&mut self, elevation: f64) { + pub fn set_elevation(&mut self, elevation: f32) { self.elevation = elevation; self.recalculate_light_values = true; } - pub fn set_intensity(&mut self, intensity: f64) { + pub fn set_intensity(&mut self, intensity: f32) { self.intensity = intensity; self.recalculate_light_values = true; } fn calc_light_intensity( &mut self, - _center: f64, - left: f64, - right: f64, - down: f64, - up: f64, - ) -> f64 { + _center: f32, + left: f32, + right: f32, + down: f32, + up: f32, + ) -> f32 { // Recalculate the sine and cosine of the various light values if necessary so it does not // have to be calculated each time this method is called. if self.recalculate_light_values { @@ -483,11 +483,11 @@ impl LightSource { } #[inline] -fn u8_array_to_f64_array(input: [u8; 4]) -> [f64; 4] { +fn u8_array_to_f32_array(input: [u8; 4]) -> [f32; 4] { let mut result = [0.0; 4]; for x in 0..4 { - result[x] = f64::from(input[x]) / 255.0; + result[x] = f32::from(input[x]) / 255.0; } result @@ -499,7 +499,7 @@ mod tests { #[test] fn array_conversion() { - assert_eq!([0.0; 4], u8_array_to_f64_array([0; 4])); - assert_eq!([1.0; 4], u8_array_to_f64_array([255; 4])); + assert_eq!([0.0; 4], u8_array_to_f32_array([0; 4])); + assert_eq!([1.0; 4], u8_array_to_f32_array([255; 4])); } } diff --git a/src/utils/noise_map.rs b/src/utils/noise_map.rs index 7f7e9764..ca55e429 100644 --- a/src/utils/noise_map.rs +++ b/src/utils/noise_map.rs @@ -9,8 +9,8 @@ const RASTER_MAX_HEIGHT: u16 = 32_767; pub struct NoiseMap { size: (usize, usize), - border_value: f64, - map: Vec, + border_value: f32, + map: Vec, } impl NoiseMap { @@ -18,11 +18,11 @@ impl NoiseMap { Self::initialize().set_size(width, height) } - pub fn iter(&self) -> Iter<'_, f64> { + pub fn iter(&self) -> Iter<'_, f32> { self.map.iter() } - pub fn iter_mut(&mut self) -> IterMut<'_, f64> { + pub fn iter_mut(&mut self) -> IterMut<'_, f32> { self.map.iter_mut() } @@ -60,18 +60,18 @@ impl NoiseMap { self.size } - pub fn set_border_value(self, border_value: f64) -> Self { + pub fn set_border_value(self, border_value: f32) -> Self { Self { border_value, ..self } } - pub fn border_value(&self) -> f64 { + pub fn border_value(&self) -> f32 { self.border_value } - pub fn set_value(&mut self, x: usize, y: usize, value: f64) { + pub fn set_value(&mut self, x: usize, y: usize, value: f32) { let (width, height) = self.size; if x < width && y < height { @@ -81,7 +81,7 @@ impl NoiseMap { } } - pub fn get_value(&self, x: usize, y: usize) -> f64 { + pub fn get_value(&self, x: usize, y: usize) -> f32 { let (width, height) = self.size; if x < width && y < height { @@ -106,7 +106,7 @@ impl NoiseMap { let directory: String = "example_images/".to_owned(); let file_path = directory + filename; - // collect the values from f64 into u8 in a separate vec + // collect the values from f32 into u8 in a separate vec let (width, height) = self.size; let mut pixels: Vec = Vec::with_capacity(width * height); @@ -141,7 +141,7 @@ impl Default for NoiseMap { } impl Index<(usize, usize)> for NoiseMap { - type Output = f64; + type Output = f32; fn index(&self, (x, y): (usize, usize)) -> &Self::Output { let (width, height) = self.size; @@ -168,9 +168,9 @@ impl IndexMut<(usize, usize)> for NoiseMap { } impl IntoIterator for NoiseMap { - type Item = f64; + type Item = f32; - type IntoIter = IntoIter; + type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { self.map.into_iter() @@ -178,9 +178,9 @@ impl IntoIterator for NoiseMap { } impl<'a> IntoIterator for &'a NoiseMap { - type Item = &'a f64; + type Item = &'a f32; - type IntoIter = Iter<'a, f64>; + type IntoIter = Iter<'a, f32>; fn into_iter(self) -> Self::IntoIter { self.iter() @@ -188,9 +188,9 @@ impl<'a> IntoIterator for &'a NoiseMap { } impl<'a> IntoIterator for &'a mut NoiseMap { - type Item = &'a mut f64; + type Item = &'a mut f32; - type IntoIter = IterMut<'a, f64>; + type IntoIter = IterMut<'a, f32>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() diff --git a/src/utils/noise_map_builder.rs b/src/utils/noise_map_builder.rs index 616a63bb..d6a1affb 100644 --- a/src/utils/noise_map_builder.rs +++ b/src/utils/noise_map_builder.rs @@ -12,17 +12,17 @@ pub trait NoiseMapBuilder { pub struct CylinderMapBuilder where - SourceModule: NoiseFn, + SourceModule: NoiseFn, { - angle_bounds: (f64, f64), - height_bounds: (f64, f64), + angle_bounds: (f32, f32), + height_bounds: (f32, f32), size: (usize, usize), source_module: SourceModule, } impl CylinderMapBuilder where - SourceModule: NoiseFn, + SourceModule: NoiseFn, { pub fn new(source_module: SourceModule) -> Self { CylinderMapBuilder { @@ -33,7 +33,7 @@ where } } - pub fn set_angle_bounds(self, lower_bound: f64, upper_bound: f64) -> Self { + pub fn set_angle_bounds(self, lower_bound: f32, upper_bound: f32) -> Self { let angle_bounds = if lower_bound >= upper_bound { // eprintln!( // "lower bound {:?} is larger than upper bound {:?}, switching order", @@ -50,7 +50,7 @@ where } } - pub fn set_height_bounds(self, lower_bound: f64, upper_bound: f64) -> Self { + pub fn set_height_bounds(self, lower_bound: f32, upper_bound: f32) -> Self { let height_bounds = if lower_bound >= upper_bound { // eprintln!( // "lower bound {:?} is larger than upper bound {:?}, switching order", @@ -67,18 +67,18 @@ where } } - pub fn angle_bounds(&self) -> (f64, f64) { + pub fn angle_bounds(&self) -> (f32, f32) { self.angle_bounds } - pub fn height_bounds(&self) -> (f64, f64) { + pub fn height_bounds(&self) -> (f32, f32) { self.height_bounds } } impl NoiseMapBuilder for CylinderMapBuilder where - SourceModule: NoiseFn, + SourceModule: NoiseFn, { fn set_size(self, width: usize, height: usize) -> Self { CylinderMapBuilder { @@ -106,14 +106,14 @@ where let angle_extent = self.angle_bounds.1 - self.angle_bounds.0; let height_extent = self.height_bounds.1 - self.height_bounds.0; - let x_step = angle_extent / width as f64; - let y_step = height_extent / height as f64; + let x_step = angle_extent / width as f32; + let y_step = height_extent / height as f32; for y in 0..height { - let current_height = self.height_bounds.0 + y_step * y as f64; + let current_height = self.height_bounds.0 + y_step * y as f32; for x in 0..width { - let current_angle = self.angle_bounds.0 + x_step * x as f64; + let current_angle = self.angle_bounds.0 + x_step * x as f32; let point_x = current_angle.to_radians().cos(); let point_z = current_angle.to_radians().sin(); @@ -135,18 +135,18 @@ where pub struct PlaneMapBuilder where - SourceModule: NoiseFn, + SourceModule: NoiseFn, { is_seamless: bool, - x_bounds: (f64, f64), - y_bounds: (f64, f64), + x_bounds: (f32, f32), + y_bounds: (f32, f32), size: (usize, usize), source_module: SourceModule, } impl PlaneMapBuilder where - SourceModule: NoiseFn, + SourceModule: NoiseFn, { pub fn new(source_module: SourceModule) -> Self { PlaneMapBuilder { @@ -165,32 +165,32 @@ where } } - pub fn set_x_bounds(self, lower_x_bound: f64, upper_x_bound: f64) -> Self { + pub fn set_x_bounds(self, lower_x_bound: f32, upper_x_bound: f32) -> Self { PlaneMapBuilder { x_bounds: (lower_x_bound, upper_x_bound), ..self } } - pub fn set_y_bounds(self, lower_y_bound: f64, upper_y_bound: f64) -> Self { + pub fn set_y_bounds(self, lower_y_bound: f32, upper_y_bound: f32) -> Self { PlaneMapBuilder { y_bounds: (lower_y_bound, upper_y_bound), ..self } } - pub fn x_bounds(&self) -> (f64, f64) { + pub fn x_bounds(&self) -> (f32, f32) { self.x_bounds } - pub fn y_bounds(&self) -> (f64, f64) { + pub fn y_bounds(&self) -> (f32, f32) { self.y_bounds } } impl NoiseMapBuilder for PlaneMapBuilder where - SourceModule: NoiseFn, + SourceModule: NoiseFn, { fn set_size(self, width: usize, height: usize) -> Self { PlaneMapBuilder { @@ -218,14 +218,14 @@ where let x_extent = self.x_bounds.1 - self.x_bounds.0; let y_extent = self.y_bounds.1 - self.y_bounds.0; - let x_step = x_extent / width as f64; - let y_step = y_extent / height as f64; + let x_step = x_extent / width as f32; + let y_step = y_extent / height as f32; for y in 0..height { - let current_y = self.y_bounds.0 + y_step * y as f64; + let current_y = self.y_bounds.0 + y_step * y as f32; for x in 0..width { - let current_x = self.x_bounds.0 + x_step * x as f64; + let current_x = self.x_bounds.0 + x_step * x as f32; let final_value = if self.is_seamless { let sw_value = self.source_module.get([current_x, current_y, 0.0]); @@ -260,17 +260,17 @@ where pub struct SphereMapBuilder where - SourceModule: NoiseFn, + SourceModule: NoiseFn, { - latitude_bounds: (f64, f64), - longitude_bounds: (f64, f64), + latitude_bounds: (f32, f32), + longitude_bounds: (f32, f32), size: (usize, usize), source_module: SourceModule, } impl SphereMapBuilder where - SourceModule: NoiseFn, + SourceModule: NoiseFn, { pub fn new(source_module: SourceModule) -> Self { SphereMapBuilder { @@ -281,14 +281,14 @@ where } } - pub fn set_latitude_bounds(self, min_lat_bound: f64, max_lat_bound: f64) -> Self { + pub fn set_latitude_bounds(self, min_lat_bound: f32, max_lat_bound: f32) -> Self { SphereMapBuilder { latitude_bounds: (min_lat_bound, max_lat_bound), ..self } } - pub fn set_longitude_bounds(self, min_lon_bound: f64, max_lon_bound: f64) -> Self { + pub fn set_longitude_bounds(self, min_lon_bound: f32, max_lon_bound: f32) -> Self { SphereMapBuilder { longitude_bounds: (min_lon_bound, max_lon_bound), ..self @@ -297,10 +297,10 @@ where pub fn set_bounds( self, - min_lat_bound: f64, - max_lat_bound: f64, - min_lon_bound: f64, - max_lon_bound: f64, + min_lat_bound: f32, + max_lat_bound: f32, + min_lon_bound: f32, + max_lon_bound: f32, ) -> Self { SphereMapBuilder { latitude_bounds: (min_lat_bound, max_lat_bound), @@ -309,18 +309,18 @@ where } } - pub fn latitude_bounds(&self) -> (f64, f64) { + pub fn latitude_bounds(&self) -> (f32, f32) { self.latitude_bounds } - pub fn longitude_bounds(&self) -> (f64, f64) { + pub fn longitude_bounds(&self) -> (f32, f32) { self.longitude_bounds } } impl NoiseMapBuilder for SphereMapBuilder where - SourceModule: NoiseFn, + SourceModule: NoiseFn, { fn set_size(self, width: usize, height: usize) -> Self { SphereMapBuilder { @@ -348,14 +348,14 @@ where let lon_extent = self.longitude_bounds.1 - self.longitude_bounds.0; let lat_extent = self.latitude_bounds.1 - self.latitude_bounds.0; - let x_step = lon_extent / width as f64; - let y_step = lat_extent / height as f64; + let x_step = lon_extent / width as f32; + let y_step = lat_extent / height as f32; for y in 0..height { - let current_lat = self.latitude_bounds.0 + y_step * y as f64; + let current_lat = self.latitude_bounds.0 + y_step * y as f32; for x in 0..width { - let current_lon = self.longitude_bounds.0 + x_step * x as f64; + let current_lon = self.longitude_bounds.0 + x_step * x as f32; let point = lat_lon_to_xyz(current_lat, current_lon); @@ -367,7 +367,7 @@ where } } -fn lat_lon_to_xyz(lat: f64, lon: f64) -> [f64; 3] { +fn lat_lon_to_xyz(lat: f32, lon: f32) -> [f32; 3] { let r = lat.to_radians().cos(); let x = r * lon.to_radians().cos(); let y = lat.to_radians().sin();