use nalgebra::Complex;
use num_traits::{Float, FloatConst};
use rand::{
Rng,
distr::{Distribution, StandardUniform, uniform::SampleUniform},
};
#[non_exhaustive]
pub enum Generator<T> {
Aabb {
centre: Complex<T>,
half_size: Complex<T>,
},
Circle {
centre: Complex<T>,
radius: T,
},
Gaussian {
centre: Complex<T>,
std_dev: T,
},
}
impl<T: Float + FloatConst + SampleUniform> Generator<T>
where
StandardUniform: Distribution<T>,
{
#[must_use]
#[inline]
pub fn sample<R: Rng>(&self, rng: &mut R) -> Complex<T> {
match *self {
Self::Aabb { centre, half_size } => {
let re = rng.random_range(T::from(-half_size.re).unwrap()..T::from(half_size.re).unwrap());
let im = rng.random_range(T::from(-half_size.im).unwrap()..T::from(half_size.im).unwrap());
centre + Complex::new(re, im)
}
Self::Circle { centre, radius } => {
let theta = rng.random_range(T::zero()..T::TAU());
let rho = rng.random_range(T::zero()..radius).sqrt();
let re = centre.re + rho * theta.cos();
let im = centre.im + rho * theta.sin();
centre + Complex::new(re, im)
}
Self::Gaussian { centre, std_dev } => {
let u1: T = rng.random();
let u2: T = rng.random();
let r = (T::from(-2).unwrap() * u1.ln()).sqrt() * std_dev;
let theta = T::TAU() * u2;
let x = r * theta.cos();
let y = r * theta.sin();
centre + Complex::new(x, y)
}
}
}
}