use crate::geom::Point;
use crate::spectrum::blackbody_wavelength;
use rand::prelude::*;
use rand_distr::num_traits::{Float, FromPrimitive};
use rand_distr::uniform::SampleUniform;
use rand_distr::Distribution;
use rand_distr::{Normal, StandardNormal};
use std::sync::Arc;
#[derive(Clone)]
pub struct Sampler<T: Copy, R: Rng> {
sample: Arc<dyn Fn(&mut R) -> T + Send + Sync>,
bounds: (T, T),
}
impl<T, R> Sampler<T, R>
where
R: Rng,
T: Copy + Send + Sync + 'static,
{
pub fn new_const<A>(c: A) -> Self
where
A: Into<T>,
{
let c = c.into();
Self {
sample: Arc::new(move |_| c),
bounds: (c, c),
}
}
}
impl<T, R> Sampler<T, R>
where
R: Rng,
T: Copy + Float + SampleUniform + Send + Sync + 'static,
{
pub fn new_range<A, B>(a: A, b: B) -> Self
where
A: Into<T>,
B: Into<T>,
{
let a = a.into();
let b = b.into();
if a == b {
return Self::new_const(a);
}
let lower = T::min(a, b);
let upper = T::max(a, b);
Self {
sample: Arc::new(move |r| r.gen_range(lower..=upper)),
bounds: (lower, upper),
}
}
}
impl<T, R> Sampler<T, R>
where
R: Rng,
T: Copy + Float + From<f32> + Send + Sync + 'static,
StandardNormal: Distribution<T>,
{
pub fn new_gaussian<A, B>(mean: A, std_dev: B) -> Self
where
A: Copy + Into<T>,
B: Copy + Into<T>,
{
let width: T = std_dev.into() * 3.0.into();
let mean = mean.into();
let g = Normal::new(0.0.into(), std_dev.into()).unwrap();
Self {
sample: Arc::new(move |r| (g.sample(r) % width) + mean),
bounds: (mean - width, mean + width),
}
}
}
impl<T, R> Sampler<T, R>
where
R: Rng,
T: Copy + Float + SampleUniform + FromPrimitive + From<f32> + Send + Sync + 'static,
{
pub fn new_blackbody<A>(temperature: A) -> Self
where
A: Into<T>,
{
let t = temperature.into();
Self {
sample: Arc::new(move |r| blackbody_wavelength(t, r.gen_range(0.0.into()..1.0.into()))),
bounds: (0.0.into(), T::max_value()),
}
}
}
impl<T, R> Sampler<T, R>
where
T: Copy,
R: Rng,
{
pub fn from_fn(f: Arc<dyn Fn(&mut R) -> T + Send + Sync>, lower: T, upper: T) -> Self {
Self {
sample: f,
bounds: (lower, upper),
}
}
#[inline(always)]
pub fn sample(&self, rng: &mut R) -> T {
(self.sample)(rng)
}
#[inline(always)]
pub fn bounds(&self) -> (T, T) {
self.bounds
}
}
impl<T, R> From<T> for Sampler<T, R>
where
T: Copy + From<T> + Send + Sync + 'static,
R: Rng,
{
fn from(value: T) -> Self {
Self::new_const(value)
}
}
impl<T, R> From<(T, T)> for Sampler<T, R>
where
T: Copy + Float + SampleUniform + Send + Sync + From<f32> + 'static,
R: Rng,
{
fn from(value: (T, T)) -> Self {
Self::new_range(value.0, value.1)
}
}
#[derive(Clone)]
pub struct SamplerPoint<R: Rng> {
x: Sampler<f64, R>,
y: Sampler<f64, R>,
}
impl<R> From<Point> for SamplerPoint<R>
where
R: Rng,
{
fn from(value: Point) -> Self {
Self {
x: value.x.into(),
y: value.y.into(),
}
}
}
impl<A, B, R> From<(A, B)> for SamplerPoint<R>
where
A: Into<Sampler<f64, R>>,
B: Into<Sampler<f64, R>>,
R: Rng,
{
fn from(value: (A, B)) -> Self {
Self {
x: value.0.into(),
y: value.1.into(),
}
}
}
impl<R> SamplerPoint<R>
where
R: Rng,
{
#[inline(always)]
pub(crate) fn get(&self, rng: &mut R) -> Point {
Point {
x: self.x.sample(rng),
y: self.y.sample(rng),
}
}
}
#[cfg(test)]
mod tests {
type RandGen = rand_pcg::Pcg64Mcg;
use rand::prelude::*;
use super::Sampler;
#[test]
fn const_bounds() {
for _ in 0..1000 {
let mut stdrng = RandGen::from_entropy();
let f: f64 = stdrng.gen();
let s = Sampler::<f64, RandGen>::new_const(f);
let (a, b) = s.bounds();
assert_eq!(a, b);
assert_eq!(a, f);
assert_eq!(b, f);
}
}
#[test]
fn range_bounds() {
for _ in 0..1000 {
let mut stdrng = RandGen::from_entropy();
let a: f64 = stdrng.gen();
let b: f64 = stdrng.gen();
let s = Sampler::<f64, RandGen>::new_range(a, b);
let (c, d) = s.bounds();
assert_eq!(a.min(b), c);
assert_eq!(a.max(b), d);
}
}
#[test]
fn val_const() {
let mut rng = RandGen::from_entropy();
let mut stdrng = RandGen::from_entropy();
let f: f64 = stdrng.gen();
let s = Sampler::new_const(f);
for _ in 0..1000 {
let y: f64 = s.sample(&mut rng);
assert_eq!(y, f);
}
}
#[test]
fn val_range() {
let mut rng = RandGen::from_entropy();
let mut stdrng = RandGen::from_entropy();
let mut f1: f64 = stdrng.gen();
let mut f2: f64 = stdrng.gen();
if f1 < f2 {
let tmp = f1;
f1 = f2;
f2 = tmp;
}
let s = Sampler::new_range(f1, f2);
for _ in 0..100000 {
let y: f64 = s.sample(&mut rng);
assert!(y <= f1);
assert!(y >= f2);
}
}
#[test]
fn blackbody_works() {
let mut rng = RandGen::from_entropy();
let w: Sampler<f64, rand_pcg::Mcg128Xsl64> = Sampler::new_range(780.0, 360.0);
let s = Sampler::new_blackbody(w.sample(&mut rng));
let _: f64 = s.sample(&mut rng);
}
#[test]
fn blackbody_white_light_64() {
let mut rng = RandGen::from_entropy();
let s = Sampler::new_blackbody(0.0);
let _: f64 = s.sample(&mut rng);
}
#[test]
fn blackbody_white_light_32() {
let mut rng = RandGen::from_entropy();
let s = Sampler::new_blackbody(0.0);
let _: f32 = s.sample(&mut rng);
}
#[test]
fn gaussian_at_zero() {
let mut rng = RandGen::from_entropy();
let s = Sampler::new_gaussian(0.0, 10.0);
let _: f64 = s.sample(&mut rng);
}
#[test]
fn range_big_first() {
let mut rng = RandGen::from_entropy();
let s = Sampler::new_range(10.0, 0.0);
let _: f64 = s.sample(&mut rng);
}
#[test]
fn range_small_first() {
let mut rng = RandGen::from_entropy();
let s = Sampler::new_range(0.0, 10.0);
let _: f64 = s.sample(&mut rng);
}
}