use core::{cell::Cell, f32};
use std::{boxed::Box, thread_local};
use crate::{Constant, Noise, NoiseFn, OpenSimplex2, Perlin, Sample};
#[test]
fn translate() {
const OFFSET: f32 = 10.0;
thread_local! {
static X: Cell<f32> = const { Cell::new(f32::NAN) };
static Y: Cell<f32> = const { Cell::new(f32::NAN) };
static Z: Cell<f32> = const { Cell::new(f32::NAN) };
static W: Cell<f32> = const { Cell::new(f32::NAN) };
}
fn reset() {
X.set(f32::NAN);
Y.set(f32::NAN);
Z.set(f32::NAN);
W.set(f32::NAN);
}
fn assert(dim: usize) {
for (i, value) in [&X, &Y, &Z, &W].into_iter().enumerate().take(dim) {
assert_eq!(value.get(), OFFSET + (i + 1) as f32);
}
}
fn noise2([x, y]: [f32; 2]) -> f32 {
X.set(x);
Y.set(y);
x + y
}
fn noise3([x, y, z]: [f32; 3]) -> f32 {
X.set(x);
Y.set(y);
Z.set(z);
x + y + z
}
fn noise4([x, y, z, w]: [f32; 4]) -> f32 {
X.set(x);
Y.set(y);
Z.set(z);
W.set(w);
x + y + z + w
}
reset();
NoiseFn(noise2).translate_xy(1.0, 2.0).sample2([OFFSET; 2]);
assert(2);
reset();
NoiseFn(noise3).translate_xyz(1.0, 2.0, 3.0).sample3([OFFSET; 3]);
assert(3);
reset();
NoiseFn(noise4).translate_xyzw(1.0, 2.0, 3.0, 4.0).sample4([OFFSET; 4]);
assert(4);
}
#[test]
fn translate_doesnt_crash() {
Sample::<0>::sample_with_seed(&Constant(0.0).translate_x(0.0), [0.0; 0], 0);
Sample::<0>::sample_with_seed(&Constant(0.0).translate_xy(0.0, 0.0), [0.0; 0], 0);
Sample::<0>::sample_with_seed(&Constant(0.0).translate_xyz(0.0, 0.0, 0.0), [0.0; 0], 0);
Sample::<0>::sample_with_seed(&Constant(0.0).translate_xyzw(0.0, 0.0, 0.0, 0.0), [0.0; 0], 0);
Sample::<1>::sample_with_seed(&Constant(0.0).translate_x(0.0), [0.0; 1], 0);
Sample::<1>::sample_with_seed(&Constant(0.0).translate_xy(0.0, 0.0), [0.0; 1], 0);
Sample::<1>::sample_with_seed(&Constant(0.0).translate_xyz(0.0, 0.0, 0.0), [0.0; 1], 0);
Sample::<1>::sample_with_seed(&Constant(0.0).translate_xyzw(0.0, 0.0, 0.0, 0.0), [0.0; 1], 0);
Sample::<2>::sample_with_seed(&Constant(0.0).translate_x(0.0), [0.0; 2], 0);
Sample::<2>::sample_with_seed(&Constant(0.0).translate_xy(0.0, 0.0), [0.0; 2], 0);
Sample::<2>::sample_with_seed(&Constant(0.0).translate_xyz(0.0, 0.0, 0.0), [0.0; 2], 0);
Sample::<2>::sample_with_seed(&Constant(0.0).translate_xyzw(0.0, 0.0, 0.0, 0.0), [0.0; 2], 0);
Sample::<3>::sample_with_seed(&Constant(0.0).translate_x(0.0), [0.0; 3], 0);
Sample::<3>::sample_with_seed(&Constant(0.0).translate_xy(0.0, 0.0), [0.0; 3], 0);
Sample::<3>::sample_with_seed(&Constant(0.0).translate_xyz(0.0, 0.0, 0.0), [0.0; 3], 0);
Sample::<3>::sample_with_seed(&Constant(0.0).translate_xyzw(0.0, 0.0, 0.0, 0.0), [0.0; 3], 0);
Sample::<4>::sample_with_seed(&Constant(0.0).translate_x(0.0), [0.0; 4], 0);
Sample::<4>::sample_with_seed(&Constant(0.0).translate_xy(0.0, 0.0), [0.0; 4], 0);
Sample::<4>::sample_with_seed(&Constant(0.0).translate_xyz(0.0, 0.0, 0.0), [0.0; 4], 0);
Sample::<4>::sample_with_seed(&Constant(0.0).translate_xyzw(0.0, 0.0, 0.0, 0.0), [0.0; 4], 0);
}
#[test]
#[cfg(feature = "nightly-simd")]
fn sound_when_out_of_bounds() {
_ = crate::ValueCubic.sample3a(std::simd::f32x4::splat(f32::INFINITY));
_ = crate::ValueCubic.sample3a(std::simd::f32x4::splat(i32::MAX as f32 * 10.0));
}
#[test]
fn clamp() {
assert_eq!(Constant(0.0).clamp(1.0, 2.0).sample2([0.0; 2]), 1.0);
assert_eq!(Constant(1.5).clamp(1.0, 2.0).sample2([0.0; 2]), 1.5);
assert_eq!(Constant(3.0).clamp(1.0, 2.0).sample2([0.0; 2]), 2.0);
assert!(Constant(f32::NAN).clamp(1.0, 2.0).sample2([0.0; 2]).is_nan());
Constant(0.0).clamp(2.0, 1.0).sample2([0.0; 2]);
assert_eq!(Constant(0.0).clamp(f32::NAN, f32::NAN).sample2([0.0; 2]), 0.0);
}
#[test]
fn modifiers_manual_ridged() {
let base = OpenSimplex2;
let a = base.ridged();
let b = base.abs().mul(-2.0).add(1.0);
assert_eq(a, b);
}
fn assert_eq<A, B>(a: A, b: B)
where
A: Sample<2>,
B: Sample<2>,
{
assert_approx_eq(0.0, a, b);
}
fn assert_approx_eq<A, B>(epsilon: f32, a: A, b: B)
where
A: Sample<2>,
B: Sample<2>,
{
let mut max_error = 0.0f32;
for x in 0..100 {
for y in 0..100 {
let x = x as f32 * 1.34900342;
let y = y as f32 * 0.93124235;
let p = [x, y];
let av = a.sample2(p);
let bv = b.sample2(p);
max_error = max_error.max((av - bv).abs());
}
}
std::println!("max error: {max_error}");
if max_error > epsilon {
panic!("error too big!");
}
}
#[test]
fn test_trait_object_noise() {
let perlin_dyn: &dyn Sample<2> = &Perlin;
let _value = (&perlin_dyn).sample2([1.0, 2.0]);
let perlin_box_dyn: Box<dyn Sample<2>> = Box::new(Perlin);
let _value = perlin_box_dyn.sample2([1.0, 2.0]);
}