layer_proc_gen/
lib.rs

1//! A Rust implementation of <https://github.com/runevision/LayerProcGen>
2//!
3//! The main entry point to access the data of layers is the [Layer] type.
4//!
5//! To create your own layers, you need to define a data type for the [Chunk]s of
6//! that layer, and implement the [Chunk] trait for it. This trait also allows you
7//! to specify other [Chunk] types whose layers you'll need information from to generate
8//! the data of your own chunks.
9//!
10//! All coordinates are integer based (chunk position and borders), but the data within your
11//! chunks can be of arbitrary data types, including floats, strings, etc.
12//!
13//! As an example, here's a layer that generates a point at its center:
14//!
15//! ```rust
16//! use layer_proc_gen::{Chunk, GridPoint, Point2d, debug::DynLayer};
17//!
18//! #[derive(Clone, Default)]
19//! struct MyChunk {
20//!     center: Point2d,
21//! }
22//!
23//! impl Chunk for MyChunk {
24//!     type LayerStore<T> = std::sync::Arc<T>;
25//!     type Dependencies = ();
26//!     fn compute(&(): &(), index: GridPoint<Self>) -> Self {
27//!         let center = Self::bounds(index).center();
28//!         MyChunk { center }
29//!     }
30//! }
31//! ```
32//!
33//! There are builtin [Chunk] types for common patterns like generating uniformly
34//! distributed points on an infinite grid.
35
36#![warn(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
37#![deny(missing_docs)]
38
39use std::{borrow::Borrow, ops::Deref};
40
41use debug::DynLayer;
42use rolling_grid::RollingGrid;
43pub use rolling_grid::{GridIndex, GridPoint};
44pub use vec2::{Bounds, Point2d};
45
46pub mod debug;
47pub mod generic_layers;
48
49#[macro_export]
50/// Generate a struct where all fields are wrapped in `Layer`
51macro_rules! deps {
52    ($(#[$meta:meta])* struct $name:ident {$($field:ident: $ty:ty,)*}) => {
53        $(#[$meta])*
54        struct $name {
55            $($field: Layer<$ty>,)*
56        }
57        impl $crate::Dependencies for $name {
58            fn debug(&self) -> Vec<&dyn $crate::debug::DynLayer> {
59                let $name {
60                    $($field,)*
61                } = self;
62                vec![ $($field,)*]
63            }
64        }
65    }
66}
67
68/// A struct that defines the dependencies of your [Chunk].
69/// Usually generated for structs via the [deps] macro, but you can manually define
70/// it in case you have non-[Layer] dependencies.
71///
72/// If you layer has no dependencies, you can use the `()` type instead.
73pub trait Dependencies {
74    /// For runtime debugging of your layers, you should return references to each of the
75    /// layer types within your dependencies.
76    fn debug(&self) -> Vec<&dyn DynLayer>;
77}
78
79impl Dependencies for () {
80    fn debug(&self) -> Vec<&dyn DynLayer> {
81        vec![]
82    }
83}
84
85impl<C: Chunk + debug::Debug> Dependencies for Layer<C> {
86    fn debug(&self) -> Vec<&dyn DynLayer> {
87        vec![self]
88    }
89}
90
91/// The entry point to access the chunks of a layer.
92///
93/// It exposes various convenience accessors, like iterating over areas in
94/// chunk or world coordinates.
95pub struct Layer<C: Chunk> {
96    layer: Store<C>,
97}
98
99impl<C: Chunk> Deref for Layer<C> {
100    type Target = C::Dependencies;
101
102    fn deref(&self) -> &Self::Target {
103        &self.layer.borrow().1
104    }
105}
106
107impl<C: Chunk> Default for Layer<C>
108where
109    C::Dependencies: Default,
110{
111    /// Create an entirely new layer and its dependencies.
112    /// The dependencies will not be connected to any other dependencies
113    /// of the same type.
114    fn default() -> Self {
115        Self::new(Default::default())
116    }
117}
118
119impl<C: Chunk> Layer<C> {
120    /// Create a new layer, manually specifying the dependencies.
121    /// This is useful if you want to share dependencies with another layer.
122    pub fn new(value: C::Dependencies) -> Self {
123        Layer {
124            layer: Store::<C>::from((RollingGrid::default(), value)),
125        }
126    }
127}
128
129impl<C: Chunk> Clone for Layer<C>
130where
131    Store<C>: Clone,
132{
133    /// Shallowly clone the layer. The clones will share the caches with this
134    /// copy of the layer.
135    fn clone(&self) -> Self {
136        Self {
137            layer: self.layer.clone(),
138        }
139    }
140}
141
142#[expect(type_alias_bounds)]
143type Store<C: Chunk> = C::LayerStore<Tuple<C>>;
144#[expect(type_alias_bounds)]
145type Tuple<C: Chunk> = (RollingGrid<C>, C::Dependencies);
146
147impl<C: Chunk> Layer<C> {
148    /// Eagerly compute all chunks in the given bounds (in world coordinates).
149    /// Load all dependencies' chunks and then compute our chunks.
150    /// May recursively cause the dependencies to load their deps and so on.
151    #[track_caller]
152    pub fn ensure_loaded_in_bounds(&self, chunk_bounds: Bounds) {
153        let indices = C::bounds_to_grid(chunk_bounds);
154        let mut create_indices: Vec<_> = indices.iter().collect();
155        let center = indices.center();
156        // Sort by distance to center, so we load the closest ones first
157        // Difference to
158        create_indices.sort_by_cached_key(|&index| index.dist_squared(center));
159        for index in create_indices {
160            self.get_or_compute(index);
161        }
162    }
163
164    /// Eagerly unload all chunks in the given bounds (in world coordinates).
165    pub fn clear(&self, chunk_bounds: Bounds) {
166        for index in C::bounds_to_grid(chunk_bounds).iter() {
167            self.layer.borrow().0.clear(index, self)
168        }
169    }
170
171    /// Manually (without calling `compute`) set a chunk in the cache.
172    ///
173    /// This violates all the nice properties like the fact that layers
174    /// depending on this one will not even load this chunk if they have
175    /// computed all their chunks that depend on this one. They may
176    /// later have to recompute, and then see the new value, which may
177    /// lead to recomputed chunks being different from non-recomputed chunks.
178    ///
179    /// TLDR: only call this if you have called `clear` on everything that depended
180    /// on this one.
181    pub fn incoherent_override_cache(&self, index: GridPoint<C>, val: C) {
182        self.layer.borrow().0.incoherent_override_cache(index, val)
183    }
184
185    /// Get a chunk or generate it if it wasn't already cached.
186    pub fn get_or_compute(&self, index: GridPoint<C>) -> C {
187        self.layer.borrow().0.get_or_compute(index, self)
188    }
189
190    /// Get an iterator over all chunks that touch the given bounds (in world coordinates)
191    pub fn get_range(&self, range: Bounds) -> impl Iterator<Item = C> + '_ {
192        let range = C::bounds_to_grid(range);
193        self.get_grid_range(range)
194    }
195
196    /// Get an iterator over chunks as given by the bounds (in chunk grid indices).
197    /// Chunks will be generated on the fly.
198    pub fn get_grid_range(&self, range: Bounds<GridIndex<C>>) -> impl Iterator<Item = C> + '_ {
199        // TODO: first request generation, then iterate to increase parallelism
200        range.iter().map(move |pos| self.get_or_compute(pos))
201    }
202
203    /// Get a 3x3 array of chunks around a specific chunk
204    pub fn get_moore_neighborhood(&self, index: GridPoint<C>) -> [[C; 3]; 3] {
205        C::moore_neighborhood(index).map(|line| line.map(|index| self.get_or_compute(index)))
206    }
207}
208
209/// Chunks are rectangular regions of the same size that make up a layer in a grid shape.
210pub trait Chunk: Sized + Default + Clone + 'static {
211    /// Exponent of `2` of the cached area (in grid chunk numbers, not world coordinates).
212    /// This is the area that should stay in memory at all times as it will get requested a lot.
213    const GRID_SIZE: Point2d<u8> = Point2d::splat(5);
214
215    /// Internal `RollingGrid` overlap before the system drops old chunks. Basically scales the grid width/height by
216    /// this number to allow moving across the grid width/height boundaries completely transparently.
217    /// Increasing this number makes indexing the `RollingGrid` more expensive if there is a lot of overlap.
218    const GRID_OVERLAP: u8 = 3;
219
220    /// Data structure that stores the layer. Usually `Arc<Self>`,
221    /// but some layers are only used to simplify another layer, so
222    /// they can get stored directly without the `Arc` indirection.
223    type LayerStore<T>: Borrow<T> + From<T>;
224
225    /// Width and height of the chunk (in powers of two);
226    const SIZE: Point2d<u8> = Point2d::splat(8);
227
228    /// Compute a chunk from its dependencies
229    fn compute(layer: &Self::Dependencies, index: GridPoint<Self>) -> Self;
230
231    /// Clear all information that [compute] would have computed
232    fn clear(layer: &Self::Dependencies, index: GridPoint<Self>);
233
234    /// Get the bounds for the chunk at the given index
235    fn bounds(index: GridPoint<Self>) -> Bounds {
236        let size = Self::SIZE.map(|i| 1 << i);
237        let min = index.map(|i| i.0) * size;
238        Bounds {
239            min,
240            max: min + size,
241        }
242    }
243
244    /// Get the grids that are touched by the given bounds.
245    fn bounds_to_grid(bounds: Bounds) -> Bounds<GridIndex<Self>> {
246        bounds.map(Self::pos_to_grid)
247    }
248
249    /// Get the grid the position is in
250    fn pos_to_grid(point: Point2d) -> GridPoint<Self> {
251        RollingGrid::<Self>::pos_to_grid_pos(point)
252    }
253
254    /// Pad by a chunk size to make sure we see effects from the neighboring chunks
255    fn vision_range(bounds: Bounds) -> Bounds {
256        bounds.pad(Self::SIZE.map(|i| 1 << i))
257    }
258
259    /// Get 3x3 grid points around a central one
260    fn moore_neighborhood(index: GridPoint<Self>) -> [[GridPoint<Self>; 3]; 3] {
261        let p = |x, y| index + GridPoint::new(GridIndex::from_raw(x), GridIndex::from_raw(y));
262        [
263            [p(-1, -1), p(0, -1), p(1, -1)],
264            [p(-1, 0), p(0, 0), p(1, 0)],
265            [p(-1, 1), p(0, 1), p(1, 1)],
266        ]
267    }
268
269    /// The actual dependencies. Usually a struct with fields of `Layer<T>` type, but
270    /// can be of any type to specify non-layer dependencies, too.
271    /// It is the type of the first argument of [Chunk::compute].
272    type Dependencies: Dependencies;
273}
274
275mod rolling_grid;
276pub mod vec2;