oxidasim 0.1.0

Utilities for simulations
Documentation
use pyo3::prelude::*;
use rand::seq::SliceRandom;
use rand_xoshiro::rand_core::SeedableRng;
use rayon::prelude::*;

const RADIUS_DECAY: f32 = 0.98;

macro_rules! sample_poisson_disk_impl {
    ($dim:expr) => {
        paste::paste! {
            #[pyfunction]
            #[must_use] pub fn [< sample_poisson_disk_ $dim d >](
                num_samples: usize,
                bounds: [[f32; $dim]; 2],
                radius: f32,
            ) -> Vec<[f32; $dim]> {
                let dimensions = {
                    let mut dims = bounds[1];
                    for (i, d) in dims.iter_mut().enumerate() {
                        *d -= bounds[0][i];
                    }
                    dims
                };

                let mut distribution = fast_poisson::Poisson::<$dim>::new().with_dimensions(dimensions, radius);

                let mut decay_factor = RADIUS_DECAY;
                loop {
                    let points = distribution.generate();
                    match points.len() {
                        n if n < num_samples => {
                            distribution.set_dimensions(dimensions, decay_factor * radius);
                            decay_factor *= RADIUS_DECAY;
                            continue;
                        }
                        n if n == num_samples => {
                            break points
                                .into_iter()
                                .map(|mut point| {
                                    for (i, p) in point.iter_mut().enumerate() {
                                        *p += bounds[0][i];
                                    }
                                    point
                                })
                                .collect()
                        }
                        _ => {
                            break points
                                .choose_multiple(
                                    &mut rand_xoshiro::Xoshiro128StarStar::from_entropy(),
                                    num_samples,
                                )
                                .cloned()
                                .map(|mut point| {
                                    for (i, p) in point.iter_mut().enumerate() {
                                        *p += bounds[0][i];
                                    }
                                    point
                                })
                                .collect()
                        }
                    }
                }
            }

            #[pyfunction]
            #[must_use] pub fn [< sample_poisson_disk_ $dim d_looped >](
                num_samples: [usize; 2],
                bounds: [[f32; $dim]; 2],
                radius: f32,
            ) -> Vec<Vec<[f32; $dim]>> {
                (0..num_samples[0])
                    .into_iter()
                    .map(|_| [< sample_poisson_disk_ $dim d >](num_samples[1], bounds, radius))
                    .collect()
            }

            #[pyfunction]
            #[must_use] pub fn [< sample_poisson_disk_ $dim d_parallel >](
                num_samples: [usize; 2],
                bounds: [[f32; $dim]; 2],
                radius: f32,
            ) -> Vec<Vec<[f32; $dim]>> {
                (0..num_samples[0])
                    .into_par_iter()
                    .map(|_| [< sample_poisson_disk_ $dim d >](num_samples[1], bounds, radius))
                    .collect()
            }
        }
    };
    [$( $dim:expr ),*] => {
        $( sample_poisson_disk_impl!($dim); )*
    };
}

sample_poisson_disk_impl![2, 3];