#![allow(unused)] use poisson::{algorithm, Builder, Float, Type, Vector}; use rand::distributions::{Distribution, Standard}; use rand::{rngs::SmallRng, SeedableRng}; use num_traits::NumCast; use alga::general::AbstractField; use alga::linear::{FiniteDimVectorSpace, NormedSpace}; use std::fmt::Debug; pub fn print_v>(v: V) -> String { let mut result = "(".to_owned(); for i in 0..V::dimension() { result.push_str(&format!("{}, ", v[i].to_f64().unwrap())); } if V::dimension() != 0 { result.pop(); } result.push(')'); result } #[derive(Clone, Copy)] pub enum When { Always, Sometimes, Never, } pub fn test_with_samples(samples: usize, relative_radius: f64, seeds: u32, ptype: Type) where T: Debug + Vector + Copy, Standard: Distribution, { test_with_samples_prefilled( samples, relative_radius, seeds, ptype, |_| |_| None::, When::Always, ); } pub fn test_with_samples_prefilled<'r, T, F, I>( samples: usize, relative_radius: f64, seeds: u32, ptype: Type, mut prefiller: F, valid: When, ) where T: 'r + Debug + Vector + Copy, F: FnMut(f64) -> I, I: FnMut(Option) -> Option, Standard: Distribution, Standard: Distribution, { test_algo( samples, relative_radius, seeds, ptype, &mut prefiller, valid, algorithm::Ebeida, ); test_algo( samples, relative_radius, seeds, ptype, &mut prefiller, valid, algorithm::Bridson, ); } fn test_algo<'r, T, F, I, A>( samples: usize, relative_radius: f64, seeds: u32, ptype: Type, prefiller: &mut F, valid: When, algo: A, ) where T: 'r + Debug + Vector + Copy, F: FnMut(f64) -> I, I: FnMut(Option) -> Option, A: algorithm::Creator, Standard: Distribution, Standard: Distribution, { use self::When::*; for i in 0..seeds { let mut prefilled = vec![]; let rand = SmallRng::from_seed([ (i * 3 + 2741) as u8, (i * 7 + 2729) as u8, (i * 13 + 2713) as u8, (i * 19 + 2707) as u8, (i * 29 + 2693) as u8, (i * 37 + 2687) as u8, (i * 43 + 2677) as u8, (i * 53 + 2663) as u8, (i * 61 + 2657) as u8, (i * 71 + 2633) as u8, (i * 79 + 2609) as u8, (i * 89 + 2591) as u8, (i * 101 + 2557) as u8, (i * 107 + 2549) as u8, (i * 113 + 2539) as u8, (i * 131 + 2521) as u8, ]); let mut poisson_iter = Builder::with_samples(samples, relative_radius, ptype) .build(rand, algo) .into_iter(); let mut poisson = vec![]; let mut prefill = (prefiller)(poisson_iter.radius()); let mut last = None; let mut does_prefill = false; loop { while let Some(p) = (prefill)(last) { does_prefill = true; match valid { Always => assert!( poisson_iter.stays_legal(p), "All prefilled should be accepted by the '{:?}' algorithm. \ {} was rejected.", algo, print_v(p) ), Never => assert!( !poisson_iter.stays_legal(p), "All prefilled should be rejected by the '{:?}' algorithm. \ {} was allowed even though {:?} was last to be generated.", algo, print_v(p), last.map(print_v) ), _ => {} } prefilled.push(p); poisson_iter.restrict(p); } if let Some(pp) = poisson_iter.next() { last = Some(pp); poisson.push(pp); } else { break; } } let radius = poisson_iter.radius(); let poisson_type = poisson_iter.poisson_type(); let poisson = poisson.into_iter().chain( if let Always = valid { prefilled } else { vec![] } .into_iter(), ); test_poisson(poisson, radius, poisson_type, algo, does_prefill); } } pub fn test_poisson(poisson: I, radius: F, poisson_type: Type, algo: A, does_prefill: bool) where I: Iterator, F: Float, T: Debug + Vector + Copy, A: algorithm::Creator, { use poisson::Type::*; let dim = T::dimension(); let mut vecs = vec![]; let mut hints = vec![]; { let mut iter = poisson.into_iter(); while let Some(v) = iter.next() { if let (low, Some(high)) = iter.size_hint() { hints.push((low, high)); } else { panic!( "There wasn't hint for {}th iteration for the '{:?}' algorithm.", hints.len(), algo ); } vecs.push(v); } } let len = hints.len(); for (n, (l, h)) in hints.into_iter().enumerate() { let remaining = len - (n + 1); assert!(l <= remaining, "For the '{:?}' algorithm the lower bound of hint should be smaller than or equal to actual: {} <= {}", algo, l, remaining); assert!(h >= remaining, "For the '{:?}' algorithm the upper bound of hint should be larger than or equal to actual: {} >= {}", algo, h, remaining); } if !does_prefill { for v in &vecs { for n in 0..T::dimension() { assert!(v[n] >= F::cast(0)); assert!(v[n] < F::cast(1)); } } } let vecs = match poisson_type { Perioditic => { let mut vecs2 = vec![]; for n in 0..3i64.pow(dim as u32) { let mut t = T::zero(); let mut div = n; for i in 0..T::dimension() { let rem = div % 3; div /= 3; t[i] = NumCast::from(rem - 1).unwrap(); } for v in &vecs { vecs2.push(*v + t); } } vecs2 } Normal => vecs, }; //TODO: Figure out how to check if distribution is maximal. assert_legal_poisson(&vecs, radius, algo); } pub fn assert_legal_poisson(vecs: &Vec, radius: F, algo: A) where F: Float, T: Debug + Vector + Copy, A: algorithm::Creator, { for &v1 in vecs { for &v2 in vecs { if v1 == v2 { continue; } let dist = (v1 - v2).norm(); assert!(dist > radius * F::cast(2), "Poisson-disk distribution requirement not met while generating using the '{:?}' algorithm: There exists 2 vectors with \ distance to each other of {} which is smaller than smallest allowed one {}. \ The samples: [{:?}, {:?}]", algo, dist.to_f64().unwrap(), radius.to_f64().unwrap() * 2., v1, v2); } } }