use crate::noise::algorithms::AlgorithmInitializer;
use crate::noise::Algorithm;
use crate::random::algorithms::Algorithm as RandomAlgorithm;
use crate::random::{Random, Rng};
use crate::util::FloorRem;
use derivative::Derivative;
use std::mem::MaybeUninit;
#[allow(dead_code)]
const WAVELET_TILE_SIZE: usize = 32;
const WAVELET_TILE_SIZE_SQUARED: usize = WAVELET_TILE_SIZE * WAVELET_TILE_SIZE;
const WAVELET_TILE_SIZE_CUBED: usize = WAVELET_TILE_SIZE_SQUARED * WAVELET_TILE_SIZE;
const WAVELET_ARAD: usize = 16;
const WAVELET_SCALE: f32 = 2.0;
#[derive(Clone, Derivative)]
#[derivative(Debug)]
pub struct Wavelet {
dimensions: usize,
#[derivative(Debug = "ignore")]
tile_data: Box<[f32; WAVELET_TILE_SIZE_CUBED]>,
}
impl Algorithm for Wavelet {
fn new<R: RandomAlgorithm>(dimensions: usize, initializer: AlgorithmInitializer<R>) -> Self {
let mut random = initializer.random;
let tile_data = WaveletTileData::initialize(&mut random);
Self {
dimensions,
tile_data,
}
}
#[allow(clippy::many_single_char_names)]
fn generate(&self, f: &[f32]) -> f32 {
if self.dimensions > 3 {
panic!("Wavelet noise only supports up to 3 dimensions");
}
let mut pf = [0.0; 3];
for (pfe, &fe) in Iterator::zip(
pf.iter_mut().take(self.dimensions),
f.iter().take(self.dimensions),
) {
*pfe = fe * WAVELET_SCALE;
}
let mut mid = [0; 3];
let mut w = [[0.0; 3]; 3];
let mut t;
for i in 0..3 {
mid[i] = (pf[i] - 0.5).ceil() as i32;
t = mid[i] as f32 - (pf[i] - 0.5);
w[i][0] = t * t * 0.5;
w[i][2] = (1.0 - t) * (1.0 - t) * 0.5;
w[i][1] = 1.0 - w[i][0] - w[i][2];
}
let mut c = [0; 3];
let mut result = 0.0;
let mid = mid;
for p2 in -1..=1 {
for p1 in -1..=1 {
for p0 in -1..=1 {
let mut weight = 1.0;
for i in 0..3 {
let p = match i {
0 => p0,
1 => p1,
2 => p2,
_ => unreachable!(),
};
c[i] = (mid[i].wrapping_add(p)).floor_modulo(WAVELET_TILE_SIZE as i32);
weight *= w[i][(p + 1) as usize];
}
result += weight
* self.tile_data[c[2] as usize * WAVELET_TILE_SIZE_SQUARED
+ c[1] as usize * WAVELET_TILE_SIZE
+ c[0] as usize];
}
}
}
result.max(-1.0).min(1.0)
}
}
pub(crate) struct WaveletTileData;
impl WaveletTileData {
pub(crate) fn initialize<R: RandomAlgorithm>(
random: &mut Random<R>,
) -> Box<[f32; WAVELET_TILE_SIZE_CUBED]> {
let mut noise = Self::generate_noise(random);
let mut temp1 = [0.0; WAVELET_TILE_SIZE_CUBED];
let mut temp2 = [0.0; WAVELET_TILE_SIZE_CUBED];
for iy in 0..WAVELET_TILE_SIZE {
for iz in 0..WAVELET_TILE_SIZE {
let i = iy * WAVELET_TILE_SIZE + iz * WAVELET_TILE_SIZE_SQUARED;
Self::downsample(&noise[i..], &mut temp1[i..], 1);
Self::upsample(&temp1[i..], &mut temp2[i..], 1);
}
}
for ix in 0..WAVELET_TILE_SIZE {
for iz in 0..WAVELET_TILE_SIZE {
let i = ix + iz * WAVELET_TILE_SIZE_SQUARED;
Self::downsample(&temp2[i..], &mut temp1[i..], WAVELET_TILE_SIZE);
Self::upsample(&temp1[i..], &mut temp2[i..], WAVELET_TILE_SIZE);
}
}
for ix in 0..WAVELET_TILE_SIZE {
for iy in 0..WAVELET_TILE_SIZE {
let i = ix + iy * WAVELET_TILE_SIZE;
Self::downsample(&temp2[i..], &mut temp1[i..], WAVELET_TILE_SIZE_SQUARED);
Self::upsample(&temp1[i..], &mut temp2[i..], WAVELET_TILE_SIZE_SQUARED);
}
}
for (n, &t) in Iterator::zip(noise.iter_mut(), temp2.iter()) {
*n -= t;
}
let mut offset = WAVELET_TILE_SIZE / 2;
if offset & 1 == 0 {
offset += 1
}
let mut i = 0;
for ix in 0..WAVELET_TILE_SIZE {
for iy in 0..WAVELET_TILE_SIZE {
for iz in 0..WAVELET_TILE_SIZE {
temp1[i] = noise[((ix + offset) % (WAVELET_TILE_SIZE))
+ ((iy + offset) % (WAVELET_TILE_SIZE)) * WAVELET_TILE_SIZE
+ ((iz + offset) % (WAVELET_TILE_SIZE)) * WAVELET_TILE_SIZE_SQUARED];
i += 1;
}
}
}
for (n, &t) in Iterator::zip(noise.iter_mut(), temp1.iter()) {
*n += t;
}
noise
}
#[allow(unsafe_code)]
fn generate_noise<R: RandomAlgorithm>(
random: &mut Random<R>,
) -> Box<[f32; WAVELET_TILE_SIZE_CUBED]> {
let mut noise: Box<[MaybeUninit<f32>; WAVELET_TILE_SIZE_CUBED]> =
Box::new(unsafe { MaybeUninit::uninit().assume_init() });
for n in noise.iter_mut() {
unsafe {
n.as_mut_ptr().write(random.get_f32(-1.0, 1.0));
}
}
unsafe { Box::from_raw(Box::into_raw(noise).cast::<[f32; WAVELET_TILE_SIZE_CUBED]>()) }
}
fn downsample(from: &[f32], to: &mut [f32], stride: usize) {
const A_COEFFICIENTS: [f32; 2 * WAVELET_ARAD] = [
0.000_334, -0.001_528, 0.000_410, 0.003_545, -0.000_938, -0.008_233, 0.002_172,
0.019_120, -0.005_040, -0.044_412, 0.011_655, 0.103_311, -0.025_936, -0.243_780,
0.033_979, 0.655_340, 0.655_340, 0.033_979, -0.243_780, -0.025_936, 0.103_311,
0.011_655, -0.044_412, -0.005_040, 0.019_120, 0.002_172, -0.008_233, -0.000_938,
0.003_546, 0.000_410, -0.001_528, 0.000_334,
];
for i in 0..WAVELET_TILE_SIZE as isize / 2 {
to[i as usize * stride] = 0.0;
for k in 2 * i - WAVELET_ARAD as isize..2 * i + WAVELET_ARAD as isize {
to[i as usize * stride] += A_COEFFICIENTS[(16 + k - 2 * i) as usize]
* from[k.floor_modulo(WAVELET_TILE_SIZE as isize) as usize * stride];
}
}
}
fn upsample(from: &[f32], to: &mut [f32], stride: usize) {
const P_COEFFICIENTS: [f32; 4] = [0.25, 0.75, 0.75, 0.25];
for i in 0..WAVELET_TILE_SIZE as isize {
to[i as usize * stride] = 0.0;
for k in i / 2..=i / 2 {
to[i as usize * stride] += P_COEFFICIENTS[(2 + i - 2 * k) as usize]
* from[k.floor_modulo(WAVELET_TILE_SIZE as isize / 2) as usize * stride];
}
}
}
}