use mint::Vector2;
use rand::RngCore;
pub mod best_candidate;
pub mod clustered;
pub mod fibonacci_lattice;
pub mod halton;
pub mod hex_jitter_grid;
pub mod jitter_grid;
pub mod poisson_disk;
pub mod stratified_multi_jitter;
pub mod uniform_random;
pub use best_candidate::BestCandidateSampling;
pub use clustered::ClusteredSampling;
pub use fibonacci_lattice::FibonacciLatticeSampling;
pub use halton::HaltonSampling;
pub use hex_jitter_grid::HexJitterGridSampling;
pub use jitter_grid::JitterGridSampling;
pub use poisson_disk::PoissonDiskSampling;
pub use stratified_multi_jitter::StratifiedMultiJitterSampling;
pub use uniform_random::UniformRandomSampling;
pub trait PositionSampling: Send + Sync {
fn generate(&self, domain_extent: Vector2<f32>, rng: &mut dyn RngCore) -> Vec<Vector2<f32>>;
}
#[inline]
pub(crate) fn rand01(rng: &mut dyn RngCore) -> f32 {
(rng.next_u32() as f32) / ((u32::MAX as f32) + 1.0)
}
#[inline]
pub(crate) fn next_down(val: f32) -> f32 {
if val.is_nan() {
return f32::NAN;
}
if val == f32::NEG_INFINITY {
return f32::NEG_INFINITY;
}
if val == f32::INFINITY {
return f32::MAX;
}
if val == 0.0 {
return -f32::MIN_POSITIVE;
}
let bits = val.to_bits();
if val > 0.0 {
f32::from_bits(bits.saturating_sub(1))
} else {
f32::from_bits(bits.saturating_add(1))
}
}
#[cfg(test)]
mod tests {
use super::*;
struct FixedRng {
value: u32,
}
impl RngCore for FixedRng {
fn next_u32(&mut self) -> u32 {
self.value
}
fn next_u64(&mut self) -> u64 {
self.value as u64
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
let bytes = self.value.to_le_bytes();
for (i, b) in dest.iter_mut().enumerate() {
*b = bytes[i % 4];
}
}
}
#[test]
fn rand01_returns_zero_for_zero_input() {
let mut rng = FixedRng { value: 0 };
let result = rand01(&mut rng);
assert_eq!(result, 0.0);
}
#[test]
fn rand01_handles_max_value_correctly() {
let mut rng = FixedRng { value: u32::MAX };
let result = rand01(&mut rng);
assert_eq!(result, u32::MAX as f32 / (u32::MAX as f32 + 1.0));
assert!((0.0..=1.0).contains(&result));
assert!(result < 1.0 || (result - 1.0).abs() < f32::EPSILON);
}
#[test]
fn rand01_values_in_range() {
let test_values = vec![0, 1, 100, 1000, u32::MAX / 2, u32::MAX - 1, u32::MAX];
for value in test_values {
let mut rng = FixedRng { value };
let result = rand01(&mut rng);
assert!(
(0.0..=1.0).contains(&result),
"rand01({value}) = {result} is out of range [0,1]"
);
}
}
#[test]
fn next_down_handles_edge_cases() {
assert!(next_down(1.0) < 1.0);
assert!(next_down(0.5) < 0.5);
let down_min_pos = next_down(f32::MIN_POSITIVE);
assert!(down_min_pos >= 0.0);
assert!(down_min_pos < f32::MIN_POSITIVE);
assert_eq!(next_down(0.0), -f32::MIN_POSITIVE);
assert!(next_down(-1.0) < -1.0);
assert!(next_down(-100.0) < -100.0);
assert_eq!(next_down(f32::INFINITY), f32::MAX);
assert_eq!(next_down(f32::NEG_INFINITY), f32::NEG_INFINITY);
assert!(next_down(f32::NAN).is_nan());
}
#[test]
fn rand01_distribution_properties() {
let mut rng = FixedRng {
value: u32::MAX / 2,
};
let result = rand01(&mut rng);
assert!((result - 0.5).abs() < 0.001);
}
}