layer_proc_gen/
generic_layers.rs

1//! Various useful layer/chunk type combinations that you can reuse in many kind of games.
2
3use arrayvec::ArrayVec;
4use rand::prelude::*;
5
6use crate::{
7    Chunk,
8    debug::{Debug, DebugContent},
9    rolling_grid::GridPoint,
10    vec2::{Num, Point2d},
11};
12
13/// How many points are in a chunk if the
14/// average is 1 point. Input is a value picked
15/// from a uniform distribution in 0..1
16fn poisson_1(val: f32) -> u8 {
17    match val {
18        0.0..0.3679 => 0,
19        0.3670..0.7358 => 1,
20        0.7358..0.9197 => 2,
21        0.9197..0.981 => 3,
22        0.981..0.9963 => 4,
23        0.9963..0.9994 => 5,
24        0.9994..0.9999 => 6,
25        0.9999..1.0 => 7,
26        _ => panic!("{val} is not in range 0..1"),
27    }
28}
29
30#[derive(PartialEq, Debug, Clone)]
31/// A type of chunk that contains on average one point.
32/// You can specify a size in real world coordinates as well as
33/// a random number generator salt for picking different points
34/// even for the same chunk coordinates.
35pub struct UniformPoint<P, const SIZE: u8, const SALT: u64> {
36    /// The actual points. Can be up to 7, as a poisson distribution of one point
37    /// per chunk has a negligible probability for more than 7 points.
38    pub points: ArrayVec<P, 7>,
39}
40
41impl<P, const SIZE: u8, const SALT: u64> Default for UniformPoint<P, SIZE, SALT> {
42    fn default() -> Self {
43        Self {
44            points: Default::default(),
45        }
46    }
47}
48
49impl<P: Reducible, const SIZE: u8, const SALT: u64> Chunk for UniformPoint<P, SIZE, SALT> {
50    type LayerStore<T> = T;
51    type Dependencies = ();
52
53    const SIZE: Point2d<u8> = Point2d::splat(SIZE);
54
55    fn compute((): &Self::Dependencies, index: GridPoint<Self>) -> Self {
56        let points = generate_points::<SALT, Self>(index);
57        Self {
58            points: points.map(P::from).collect(),
59        }
60    }
61
62    fn clear((): &Self::Dependencies, _index: GridPoint<Self>) {
63        // Nothing to do, we do not have dependencies
64    }
65}
66
67impl<P: Reducible, const SIZE: u8, const SALT: u64> Debug for UniformPoint<P, SIZE, SALT> {
68    fn debug(&self) -> Vec<DebugContent> {
69        self.points.iter().flat_map(Reducible::debug).collect()
70    }
71}
72
73fn generate_points<const SALT: u64, C: Chunk + 'static>(
74    index: GridPoint<C>,
75) -> impl Iterator<Item = Point2d> {
76    let chunk_bounds = C::bounds(index);
77    let mut rng = rng_for_point::<SALT, _>(index);
78    let n = poisson_1(rng.random_range(0.0..=1.0)).into();
79    std::iter::from_fn(move || Some(chunk_bounds.sample(&mut rng))).take(n)
80}
81
82/// Create a random number generator seeded with a specific point.
83pub fn rng_for_point<const SALT: u64, T: Num>(index: Point2d<T>) -> SmallRng {
84    let x = SmallRng::seed_from_u64(index.x.as_u64());
85    let y = SmallRng::seed_from_u64(index.y.as_u64());
86    let salt = SmallRng::seed_from_u64(SALT);
87    let mut seed = <SmallRng as SeedableRng>::Seed::default();
88    for mut rng in [x, y, salt] {
89        let mut tmp = <SmallRng as SeedableRng>::Seed::default();
90        rng.fill_bytes(&mut tmp);
91        for (seed, tmp) in seed.iter_mut().zip(tmp.iter()) {
92            *seed ^= *tmp;
93        }
94    }
95    SmallRng::from_seed(seed)
96}
97
98mod reduced_points;
99pub use reduced_points::*;