rollgrid/
lib.rs

1use std::marker::PhantomData;
2
3pub mod bounds2d;
4pub mod bounds3d;
5pub(crate) mod fixedarray;
6pub mod grid2d;
7pub mod grid3d;
8pub mod math;
9pub mod rollgrid2d;
10pub mod rollgrid3d;
11
12mod error_messages {
13    macro_rules! error_messages {
14        ($($name:ident = $message:literal;)*) => {
15            $(
16                pub const $name: panicmsg::PanicMsg = panicmsg::PanicMsg::new($message);
17            )*
18        };
19    }
20    error_messages!(
21        UNALLOCATED_BUFFER      = "Buffer was not allocated.";
22        X_MAX_EXCEEDS_MAXIMUM   = "X max bound exceeds maximum.";
23        Y_MAX_EXCEEDS_MAXIMUM   = "Y max bound exceeds maximum.";
24        Z_MAX_EXCEEDS_MAXIMUM   = "Z max bound exceeds maximum.";
25        NOT_ALLOCATED           = "Not allocated.";
26        SIZE_TOO_LARGE          = "Size is too large.";
27        OUT_OF_BOUNDS           = "Out of bounds.";
28        INDEX_OUT_OF_BOUNDS     = "Index is out of bounds.";
29        AREA_IS_ZERO            = "Width/Height cannot be 0.";
30        VOLUME_IS_ZERO          = "Width/Height/Depth cannot be 0.";
31        INFLATE_OVERFLOW        = "Inflate operation results in integer overflow.";
32        DEFLATE_OVERFLOW        = "Deflate operation results in integer overflow.";
33        RESIZE_OVERFLOW         = "Resize operation results in overflow.";
34    );
35}
36
37/// A trait for managing cells during resize operations on grids.
38///
39/// You can easily create a [CellManager] to use as a [CellManage].
40pub trait CellManage<C, T> {
41    fn load(&mut self, position: C) -> T;
42    fn unload(&mut self, position: C, old_value: T);
43    fn reload(&mut self, old_position: C, new_position: C, value: &mut T);
44}
45
46/// A trait for managing cells during fallible resize operations on grids.
47pub trait TryCellManage<C, T, E> {
48    fn try_load(&mut self, position: C) -> Result<T, E>;
49    fn try_unload(&mut self, position: C, old_value: T) -> Result<(), E>;
50    fn try_reload(&mut self, old_position: C, new_position: C, value: &mut T) -> Result<(), E>;
51}
52
53/// Use the utility function [cell_manager] to create a [CellManager].
54pub struct CellManager<C, T, FL, FU, FR, Marker = ()> {
55    load: FL,
56    unload: FU,
57    reload: FR,
58    phantom: std::marker::PhantomData<(C, T, Marker)>,
59}
60
61impl<C, T, FL, FU, FR> CellManage<C, T> for CellManager<C, T, FL, FU, FR>
62where
63    T: Sized,
64    FL: FnMut(C) -> T,
65    FU: FnMut(C, T),
66    FR: FnMut(C, C, &mut T),
67{
68    /// Load the cell at `position`.
69    fn load(&mut self, position: C) -> T {
70        (self.load)(position)
71    }
72
73    /// Unload cell that was at `position`.
74    fn unload(&mut self, position: C, value: T) {
75        (self.unload)(position, value);
76    }
77
78    /// Reload cell that was at `old_position` and is being moved to `new_position`.
79    fn reload(&mut self, old_position: C, new_position: C, value: &mut T) {
80        (self.reload)(old_position, new_position, value);
81    }
82}
83
84impl<C, T, E, FL, FU, FR> TryCellManage<C, T, E> for CellManager<C, T, FL, FU, FR, (E,)>
85where
86    T: Sized,
87    FL: FnMut(C) -> Result<T, E>,
88    FU: FnMut(C, T) -> Result<(), E>,
89    FR: FnMut(C, C, &mut T) -> Result<(), E>,
90{
91    /// Load the cell at `position`.
92    fn try_load(&mut self, position: C) -> Result<T, E> {
93        (self.load)(position)
94    }
95
96    /// Unload cell that was at `position`.
97    fn try_unload(&mut self, position: C, old_value: T) -> Result<(), E> {
98        (self.unload)(position, old_value)
99    }
100
101    /// Reload cell that was at `old_position` and is being moved to `new_position`.
102    fn try_reload(&mut self, old_position: C, new_position: C, value: &mut T) -> Result<(), E> {
103        (self.reload)(old_position, new_position, value)
104    }
105}
106
107/// Creates a [CellManager] instance that implements [CellManage] using the given `load`, `unload`, and `reload` functions.
108pub fn cell_manager<C, T, FL, FU, FR>(
109    load: FL,
110    unload: FU,
111    reload: FR,
112) -> CellManager<C, T, FL, FU, FR>
113where
114    CellManager<C, T, FL, FU, FR>: CellManage<C, T>,
115{
116    CellManager {
117        load,
118        unload,
119        reload,
120        phantom: PhantomData,
121    }
122}
123
124/// Creates a [CellManager] instance that implements [TryCellManage] using the given `load`, `unload`, and `reload` functions.
125pub fn try_cell_manager<C, T, E, FL, FU, FR>(
126    load: FL,
127    unload: FU,
128    reload: FR,
129) -> CellManager<C, T, FL, FU, FR, (E,)>
130where
131    CellManager<C, T, FL, FU, FR, (E,)>: TryCellManage<C, T, E>,
132{
133    CellManager {
134        load,
135        unload,
136        reload,
137        phantom: PhantomData,
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    #![allow(unused)]
144    use crate::bounds2d::Bounds2D;
145    use crate::rollgrid2d::RollGrid2D;
146
147    use super::*;
148
149    #[test]
150    pub fn roll_test() {
151        const HEX_CHARS: [char; 16] = [
152            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
153        ];
154        let mut hex = HEX_CHARS.into_iter();
155        let mut grid = RollGrid2D::new((4, 4), (0, 0), |pos: (i32, i32)| hex.next().unwrap());
156        fn print_grid(grid: &RollGrid2D<char>) {
157            for y in grid.y_min()..grid.y_max() {
158                for x in grid.x_min()..grid.x_max() {
159                    if let Some(c) = grid.get((x, y)) {
160                        print!("{}", *c);
161                    }
162                }
163                println!();
164            }
165        }
166        print_grid(&grid);
167        grid.translate((1, 1), |old_pos, new_pos, old_value| {});
168        print_grid(&grid);
169    }
170
171    #[test]
172    pub fn bounds_test() {
173        // let a = Bounds2D::from_bounds((0, 0), (3, 3));
174        // a.iter().for_each(|(x, y)| {
175        //     println!("({x}, {y})");
176        // });
177        macro_rules! intersect {
178            (($a_min:expr, $a_max:expr) -=> ($b_min:expr, $b_max:expr)) => {
179                assert!(Bounds2D::from_bounds($a_min, $a_max)
180                    .intersects(Bounds2D::from_bounds($b_min, $b_max)));
181            };
182            (($a_min:expr, $a_max:expr) -!> ($b_min:expr, $b_max:expr)) => {
183                assert!(!Bounds2D::from_bounds($a_min, $a_max)
184                    .intersects(Bounds2D::from_bounds($b_min, $b_max)));
185            };
186        }
187        intersect!(((0, 0), (3, 3)) -!> ((3, 0), (6, 3)));
188        intersect!(((0, 0), (1, 1)) -=> ((0, 0), (1, 1)));
189        intersect!(((-1, -1), (0, 0)) -=> ((-1, -1), (0, 0)));
190        intersect!(((0, 0), (3, 3)) -=> ((1, 1), (2, 2)));
191        intersect!(((1, 1), (2, 2)) -=> ((0, 0), (3, 3)));
192        intersect!(((0, 0), (1, 1)) -!> ((1, 0), (2, 1)));
193        intersect!(((1, 0), (2, 1)) -!> ((0, 0), (1, 1)));
194        intersect!(((0, 0), (1, 1)) -!> ((0, 1), (1, 2)));
195        intersect!(((0, 1), (1, 2)) -!> ((0, 0), (1, 1)));
196    }
197
198    #[test]
199    pub fn rollgrid2d_test() {
200        let mut grid = RollGrid2D::new((2, 2), (0, 0), |coord: (i32, i32)| coord);
201        fn print_grid(grid: &RollGrid2D<(i32, i32)>) {
202            println!("***");
203            for y in grid.y_min()..grid.y_max() {
204                for x in grid.x_min()..grid.x_max() {
205                    if let Some(&(cx, cy)) = grid.get((x, y)) {
206                        print!("({cx:3},{cy:3})");
207                    }
208                }
209                println!();
210            }
211        }
212        print_grid(&grid);
213        grid.translate((1, 1), |old, new, old_value| {
214            *old_value = old;
215        });
216        print_grid(&grid);
217        return;
218        grid.inflate_size(
219            (1, 1),
220            cell_manager(
221                |pos: (i32, i32)| {
222                    println!("Load: ({}, {})", pos.0, pos.1);
223                    pos
224                },
225                |pos, value| {},
226                |old_pos, new_pos, value| {},
227            ),
228        );
229        println!("***");
230        print_grid(&grid);
231        if let Some(&(x, y)) = grid.get((-5, -16)) {
232            println!("({x}, {y})");
233        } else {
234            println!("None");
235        }
236    }
237}