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;