use nalgebra as na;
use na::RealField;
use super::Heightmap;
use rand::{Rng, distributions::Distribution};
#[derive(Debug, PartialEq)]
pub enum Error {
NotSquare,
NotPowerOf2Plus1,
}
pub fn midpoint_displacement<F, R: Rng, D: Distribution<F>>(
m: &mut Heightmap<F>,
n0: u32,
rng: &mut R,
distr: D) -> Result<(), Error>
where F: RealField + Copy
{
if m.len0() != m.len1() {
return Err(Error::NotSquare);
}
let size_1 = m.len0() - 1;
let n = size_1.trailing_zeros();
if m.len0() != 2usize.pow(n) + 1 {
return Err(Error::NotPowerOf2Plus1);
}
let mid2 = |a: F, b: F| { (a + b) * na::convert(0.5) };
let mid4 = |a, b, c, d| { (a + b + c + d) * na::convert(0.25) };
for i in n0..n {
let quad_len = 2usize.pow(n - i);
let mid_len = quad_len / 2;
let scale: F = na::convert(mid_len as f64);
let mut x = (0, quad_len);
let mut y = (0, quad_len);
let adv = |x: &mut (usize, usize)| {
x.0 = x.1;
x.1 += quad_len;
x.1 > size_1
};
loop {
let h00 = m.get(x.0, y.0);
let h01 = m.get(x.0, y.1);
let h10 = m.get(x.1, y.0);
let h11 = m.get(x.1, y.1);
let h0m = mid2(h00, h01) + scale * distr.sample(rng);
let h1m = mid2(h10, h11) + scale * distr.sample(rng);
let hm0 = mid2(h00, h10) + scale * distr.sample(rng);
let hm1 = mid2(h01, h11) + scale * distr.sample(rng);
let hmm = mid4(h0m, h1m, hm0, hm1) + scale * distr.sample(rng);
let xm = x.0 + mid_len;
let ym= y.0 + mid_len;
m.set(x.0, ym, h0m);
m.set(x.1, ym, h1m);
m.set(xm, y.0, hm0);
m.set(xm, y.1, hm1);
m.set(xm, ym, hmm);
if adv(&mut y) {
y = (0, quad_len);
if adv(&mut x) {
break;
}
}
}
}
Ok(())
}
pub fn diamond_square<F, R: Rng, D: Distribution<F>>(
m: &mut Heightmap<F>,
n0: u32,
rng: &mut R,
distr: D) -> Result<(), Error>
where F: RealField + Copy
{
#![allow(non_snake_case)]
if m.len0() != m.len1() {
return Err(Error::NotSquare);
}
let size_1 = m.len0() - 1;
let n = size_1.trailing_zeros();
if m.len0() != 2usize.pow(n) + 1 {
return Err(Error::NotPowerOf2Plus1);
}
let mid3 = |a, b, c| { (a + b + c) * na::convert(1.0 / 3.0) };
let mid4 = |a, b, c, d| { (a + b + c + d) * na::convert(0.25) };
for i in n0..n {
let quad_len = 2usize.pow(n - i);
let mid_len = quad_len / 2;
let scale: F = na::convert(mid_len as f64);
let scale2: F = scale * na::convert(std::f64::consts::SQRT_2);
let mut x = (0, quad_len);
let mut y = (0, quad_len);
let adv = |x: &mut (usize, usize)| {
x.0 = x.1;
x.1 += quad_len;
x.1 > size_1
};
loop {
let h00 = m.get(x.0, y.0);
let h01 = m.get(x.0, y.1);
let h10 = m.get(x.1, y.0);
let h11 = m.get(x.1, y.1);
let xm = x.0 + mid_len;
let ym= y.0 + mid_len;
let hmm = mid4(h00, h01, h10, h11) + scale * distr.sample(rng);
let h0m = if x.0 > mid_len {
let hMm = m.get(x.0 - mid_len, ym);
mid4(h00, h01, hmm, hMm) + scale2 * distr.sample(rng)
} else {
mid3(h00, h01, hmm) + scale2 * distr.sample(rng)
};
let hm0 = if y.0 > mid_len {
let hmM = m.get(xm, y.0 - mid_len);
mid4(h00, h10, hmm, hmM) + scale2 * distr.sample(rng)
} else {
mid3(h00, h10, hmm) + scale2 * distr.sample(rng)
};
m.set(x.0, ym, h0m);
m.set(xm, y.0, hm0);
m.set(xm, ym, hmm);
if adv(&mut y) {
let h00 = m.get(x.0, y.0);
let h10 = m.get(x.1, y.0);
let xm = x.0 + mid_len;
let hmM = m.get(xm, y.0 - mid_len);
let hm0 = mid3(h00, h10, hmM) + scale2 * distr.sample(rng);
m.set(xm, y.0, hm0);
y = (0, quad_len);
if adv(&mut x) {
let mut h00;
let mut h01 = m.get(x.0, y.0);
loop {
h00 = h01;
h01 = m.get(x.0, y.1);
let ym = y.0 + mid_len;
let hMm = m.get(x.0 - mid_len, ym);
let h0m = mid3(h00, h01, hMm) + scale2 * distr.sample(rng);
m.set(x.0, ym, h0m);
if adv(&mut y) {
break;
}
}
break;
}
}
}
}
Ok(())
}