grid_trait/grid2/
mod.rs

1//! Two-dimensional data grid.
2
3/// Combinators.
4pub mod combinate;
5
6/// Implementations.
7pub mod backends;
8
9use crate::{
10    range::{
11        Range0To,
12        RangeBoundsTimes,
13        RangeBoundsPlus,
14        BoundRange,
15    },
16};
17use mint::Vector2;
18use std::{
19    ops::RangeBounds,
20    fmt::Debug,
21};
22
23/// Allocate a grid on the heap.
24pub fn alloc<I, T>(x_len: i32, y_len: i32, startval: T) -> backends::heap::ArrayGrid2<T>
25where
26    T: Clone
27{
28    backends::heap::ArrayGrid2::broadcast(x_len, y_len, startval)
29}
30
31/// Allocate a grid on the heap, populate with a function.
32pub fn alloc_gen<I, T, F>(x_len: i32, y_len: i32, generator: F) -> backends::heap::ArrayGrid2<T>
33where
34    I: From<Vector2<i32>>,
35    F: FnMut(I) -> T,
36{
37    backends::heap::ArrayGrid2::new(x_len, y_len, generator)
38}
39
40/// Inline 3x3 array grid.
41pub fn array3x3<I, T>(startval: T) -> backends::inline3x3::Inline3x3Grid<T>
42where
43    T: Clone
44{
45    backends::inline3x3::Inline3x3Grid::broadcast(startval)
46}
47
48/// Inline 3x3 array grid, populate with a function.
49pub fn array3x3_gen<I, T, F>(generator: F) -> backends::inline3x3::Inline3x3Grid<T>
50where
51    I: From<Vector2<i32>>,
52    F: FnMut(I) -> T,
53{
54    backends::inline3x3::Inline3x3Grid::new(generator)
55}
56
57/// Represent a coord → Item function as a grid.
58pub fn value_fn<I, T, F>(f: F) -> backends::kolmo::KolmoGrid2<F, I, T>
59where
60    I: From<Vector2<i32>>,
61    F: Fn(I) -> T,
62{
63    backends::kolmo::KolmoGrid2::new(f)
64}
65
66/// Represent a coord → &Item function as a grid.
67pub fn ref_fn<'a, I, T, F>(f: F) -> backends::kolmoref::KolmoRefGrid2<'a, F, I, T>
68where
69    I: From<Vector2<i32>>,
70    T: 'a,
71    F: Fn(I) -> &'a T,
72{
73    backends::kolmoref::KolmoRefGrid2::new(f)
74}
75
76/// Represent a coord → &mut Item function as a grid.
77pub fn mut_fn<'a, I, T, F>(f: F) -> backends::kolmomut::KolmoMutGrid2<'a, F, I, T>
78where
79    I: From<Vector2<i32>>,
80    T: 'a,
81    F: Fn(I) -> &'a mut T,
82{
83    backends::kolmomut::KolmoMutGrid2::new(f)
84}
85
86/// Read/write through closures.
87///
88/// This is a powerful type, which acts like a combination
89/// of `ref_fn` and `mut_fn`. This grid owns a *referent* 
90/// value, and contains a *reader* and *writer* function
91/// which immutable and mutable (respectively) borrow the
92/// elements from the referent.
93pub fn reader_writer<I, R, T, Fr, Fw>(referent: R, reader: Fr, writer: Fw) -> backends::kolmorw::KolmoRwGrid2<I, R, T, Fr, Fw>
94where
95    I: From<Vector2<i32>>,
96    Fr: Fn(I, &R) -> &T,
97    Fw: Fn(I, &mut R) -> &mut T,
98{
99    backends::kolmorw::KolmoRwGrid2::new(referent, reader, writer)
100}
101
102/// Top-level trait for 2D grids.
103pub trait Grid2 {
104    type Item;
105    type XBound: RangeBounds<i32>;
106    type YBound: RangeBounds<i32>;
107    
108    fn x_bound(&self) -> Self::XBound;
109    fn y_bound(&self) -> Self::YBound;
110    
111    fn in_bounds<I>(&self, coord: I) -> bool 
112    where
113        I: Into<Vector2<i32>>
114    {
115        let Vector2 { x, y } = coord.into();
116        
117        self.x_bound().contains(&x)
118        && self.y_bound().contains(&y)
119    }
120    
121    /// Element by-value mapping.
122    fn map<F, T>(self, func: F) -> combinate::map::Grid2Map<Self, F, T>
123    where
124        Self: Sized,
125        F: Fn(Self::Item) -> T,
126    {
127        combinate::map::Grid2Map::new(self, func)
128    }
129    
130    /// Element by-value+coord mapping.
131    fn enumap<I, F, T>(self, func: F) -> combinate::enumap::Grid2EnuMap<Self, F, T, I>
132    where
133        Self: Sized,
134        I: From<Vector2<i32>>,
135        F: Fn(I, Self::Item) -> T,
136    {
137        combinate::enumap::Grid2EnuMap::new(self, func)
138    }
139    
140    /// Flattening a grid of grids with a regular stride.
141    fn flatten<I>(self, stride: I) -> combinate::flatten::Grid2Flat<Self>
142    where
143        Self: Sized,
144        Self::Item: Grid2,
145        Self::XBound: Clone + RangeBoundsTimes,
146        Self::YBound: Clone + RangeBoundsTimes,
147        I: Into<Vector2<i32>>,
148    {
149        combinate::flatten::Grid2Flat::new(self, stride)
150    }
151    
152    /// <0, 0> in this grid becomes new_origin in resultant grid.
153    fn new_origin<I>(self, new_origin: I) -> combinate::neworigin::Grid2NewOrigin<Self>
154    where
155        Self: Sized,
156        Self::XBound: RangeBoundsPlus,
157        Self::YBound: RangeBoundsPlus,
158        I: Into<Vector2<i32>>,
159    {
160        combinate::neworigin::Grid2NewOrigin::new(self, new_origin)
161    }
162    
163    /// Provide function to provide elments at out-of-bounds coordinates.
164    ///
165    /// This produces an unbounded grid.
166    fn oob_handler<I, F>(self, handler: F) -> combinate::oobhandler::Grid2OobHandler<Self, I, F>
167    where
168        Self: Sized,
169        I: From<Vector2<i32>>,
170        F: Fn(I) -> Self::Item,
171    {
172        combinate::oobhandler::Grid2OobHandler::new(self, handler)
173    }
174    
175    /// View a sub-rectangle of this grid.
176    /// 
177    /// If the new bounds are not a subset of the current bounds,
178    /// this will panic.
179    fn subview<X, Y>(self, new_x: X, new_y: Y) -> combinate::slice::Grid2Slice<Self, X, Y>
180    where
181        Self: Sized,
182        Self::XBound: Debug,
183        Self::YBound: Debug,
184        X: RangeBounds<i32> + Clone + Debug,
185        Y: RangeBounds<i32> + Clone + Debug,
186    {
187        combinate::slice::Grid2Slice::new(self, new_x, new_y)
188    }
189    
190    /// View a sub-rectangle of this grid.
191    /// 
192    /// If the new bounds are not a subset of the current bounds,
193    /// this will fail.
194    fn try_subview<X, Y>(self, new_x: X, new_y: Y) -> Result<combinate::slice::Grid2Slice<Self, X, Y>, Self>
195    where
196        Self: Sized,
197        Self::XBound: Debug,
198        Self::YBound: Debug,
199        X: RangeBounds<i32> + Clone + Debug,
200        Y: RangeBounds<i32> + Clone + Debug,
201    {
202        combinate::slice::Grid2Slice::try_new(self, new_x, new_y)
203    }
204    
205    /// View a sub-rectangle of this grid, beginning at origin.
206    /// 
207    /// If the new bounds are not a subset of the current bounds,
208    /// this will panic.
209    fn subview_0to(self, new_x_len: i32, new_y_len: i32) -> combinate::slice::Grid2Slice<Self, Range0To, Range0To>
210    where
211        Self: Sized,
212        Self::XBound: Debug,
213        Self::YBound: Debug,
214    {
215        combinate::slice::Grid2Slice::new(
216            self, 
217            Range0To { end: new_x_len },
218            Range0To { end: new_y_len })
219    }
220    
221    /// View a sub-rectangle of this grid, beginning at origin.
222    /// 
223    /// If the new bounds are not a subset of the current bounds,
224    /// this will fail.
225    fn try_subview_0to(self, new_x_len: i32, new_y_len: i32) -> Result<combinate::slice::Grid2Slice<Self, Range0To, Range0To>, Self>
226    where
227        Self: Sized,
228        Self::XBound: Debug,
229        Self::YBound: Debug,
230    {
231        combinate::slice::Grid2Slice::try_new(
232            self, 
233            Range0To { end: new_x_len },
234            Range0To { end: new_y_len })
235    }
236    
237    /// View of this grid which wraps around the edges.
238    ///
239    /// The input grid must be bounded in all directions, and the
240    /// output grid is completely unbounded.
241    fn wrapping(self) -> combinate::wrapping::Grid2Wrapping<Self>
242    where
243        Self: Sized,
244        Self::XBound: BoundRange,
245        Self::YBound: BoundRange,
246    {
247        combinate::wrapping::Grid2Wrapping::new(self)
248    }
249    
250    /// Collect a grid's elements into a heap allocation.
251    ///
252    /// The grid must be bound from 0 to a finite limit.
253    fn collect(&self) -> backends::heap::ArrayGrid2<Self::Item>
254    where
255        Self: Grid2Get,
256        Self::XBound: Into<Range0To>,
257        Self::YBound: Into<Range0To>,
258    {
259        let x_len = self.x_bound().into().end;
260        let y_len = self.y_bound().into().end;
261        backends::heap::ArrayGrid2::new(
262            x_len, y_len,
263            |coord: Vector2<i32>| self.get(coord))
264    }
265}
266
267/// 2D grid bounded from 0 to a finite number.
268pub trait Grid2Len: Grid2<XBound=Range0To, YBound=Range0To> {
269    fn x_len(&self) -> i32 {
270        self.x_bound().end
271    }
272    
273    fn y_len(&self) -> i32 {
274        self.y_bound().end
275    }
276}
277
278/// 2D grid read by value.
279pub trait Grid2Get: Grid2 {
280    fn get<I>(&self, coord: I) -> Self::Item
281    where
282        I: Into<Vector2<i32>>;
283        
284    fn try_get<I>(&self, coord: I) -> Option<Self::Item>
285    where
286        I: Into<Vector2<i32>>
287    {
288        let coord = coord.into();
289        if self.in_bounds(coord) {
290            Some(self.get(coord))
291        } else {
292            None
293        }
294    }
295}
296
297/// 2D grid write by value.
298pub trait Grid2Set: Grid2 {
299    fn set<I>(&mut self, coord: I, elem: Self::Item)
300    where
301        I: Into<Vector2<i32>>;
302        
303    fn try_set<I>(&mut self, coord: I, elem: Self::Item) -> Result<(), Self::Item> 
304    where
305        I: Into<Vector2<i32>>
306    {
307        let coord = coord.into();
308        if self.in_bounds(coord) {
309            self.set(coord, elem);
310            Ok({})
311        } else {
312            Err(elem)
313        }
314    }
315}
316
317/// 2D grid read by reference.
318pub trait Grid2Ref: Grid2 {
319    fn idx<I>(&self, coord: I) -> &Self::Item
320    where
321        I: Into<Vector2<i32>>;
322    
323    fn try_idx<I>(&self, coord: I) -> Option<&Self::Item>
324    where
325        I: Into<Vector2<i32>>
326    {
327        let coord = coord.into();
328        if self.in_bounds(coord) {
329            Some(self.idx(coord))
330        } else {
331            None
332        }
333    }
334}
335
336/// 2D grid write by reference.
337pub trait Grid2Mut: Grid2 {
338    fn midx<I>(&mut self, coord: I) -> &mut Self::Item
339    where
340        I: Into<Vector2<i32>>;
341    
342    fn try_midx<I>(&mut self, coord: I) -> Option<&mut Self::Item>
343    where
344        I: Into<Vector2<i32>>
345    {
346        let coord = coord.into();
347        if self.in_bounds(coord) {
348            Some(self.midx(coord))
349        } else {
350            None
351        }
352    }
353}