spatial_slice/
lib.rs

1use std::marker::PhantomData;
2
3/// A Space represents a rectangular 2 dimensional array of contiguous
4/// dynamically allocated memory
5pub struct Space<T> {
6    data: Box<[T]>,
7    width: usize,
8    height: usize
9}
10
11impl<T> Space<T> {
12    /// Creates a space full of the provided value,
13    /// with the provided dimensions
14    #[inline]
15    pub fn new(value: T, width: usize, height: usize) -> Self
16        where T: Clone {
17
18        Space {
19            data: vec![ value; width * height ].into_boxed_slice(),
20            width,
21            height
22        }
23    }
24
25    #[inline]
26    pub fn width(&self) -> usize {
27        self.width
28    }
29
30    #[inline]
31    pub fn height(&self) -> usize {
32        self.height
33    }
34
35    /// Creates an immutable reference to an element at an absolute position
36    /// in the space
37    /// If the position specified is outside the space None is returned
38    #[inline]
39    pub fn get(&self, x: usize, y: usize) -> Option<&T> {
40        let index = y * self.width + x;
41
42        self.data.get(index)
43    }
44
45    /// Creates a mutable reference to an element at an absolute position
46    /// in the space
47    /// If the position specified is outside the space None is returned
48    #[inline]
49    pub fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut T> {
50        let index = y * self.width + x;
51
52        self.data.get_mut(index)
53    }
54
55    /// Sets the value for the specified absolute position in the space
56    /// If the position specified is outside the space false is returned
57    #[inline]
58    pub fn set(&mut self, x: usize, y: usize, value: T) -> bool {
59        let index = y * self.width + x;
60
61        if index < self.data.len() {
62            self.data[index] = value;
63            true
64        } else {
65            false
66        }
67    }
68
69    /// Create a mutable slice representing the entire space
70    #[inline]
71    pub fn as_slice_mut(&mut self) -> SpaceSliceMut<'_, T> {
72        SpaceSliceMut {
73            parent: self,
74            phantom: PhantomData,
75
76            x: 0,
77            y: 0,
78
79            width: self.width,
80            height: self.height
81        }
82    }
83}
84
85/// A positioning type indicates how to interpret an X/Y coordinate in a slice
86pub enum PostioningType {
87    /// Absolute positioning indexes directly into the space that this slice references
88    Absolute, 
89
90    /// Relative positioning indexes into the slice,
91    /// it treats the slices (x,y) values as the origin (0,0)
92    /// therefore values must be offset by (x,y) to be interpretted absolutely
93    Relative
94}
95
96/// Represents a partition with left and right values
97pub struct HorizontalSplit<T> {
98    pub left: T,
99    pub right: T
100}
101
102/// Represents a partition with above and below values
103pub struct VerticalSplit<T> {
104    pub above: T,
105    pub below: T
106}
107
108/// The data structure that represents a mutable view of a subspace
109/// of some parent space
110pub struct SpaceSliceMut<'a, T> {
111    
112    parent: *mut Space<T>,
113    phantom: PhantomData<&'a mut Space<T>>,
114
115    x: usize,
116    y: usize,
117
118    width: usize,
119    height: usize
120}
121
122impl<'a, T> SpaceSliceMut<'a, T> {
123
124    #[inline]
125    pub fn width(&self) -> usize {
126        self.width
127    }
128
129    #[inline]
130    pub fn height(&self) -> usize {
131        self.height
132    }
133
134    #[inline]
135    pub fn convert_coord(&self, pos_type: PostioningType, x: usize, y: usize) -> Option<(usize, usize)> {
136        match pos_type {
137            PostioningType::Absolute =>  {
138                if x < self.x 
139                    || x >= self.x + self.width
140                    || y < self.y
141                    || y >= self.y + self.height {
142                    return None;
143                }
144
145                Some((x, y))
146            }
147            PostioningType::Relative => Some((self.x + x, self.y + y))
148        }
149    }
150
151    /// Creates an immutable reference to a value in this slice using 
152    /// the specified addressing mode
153    /// If the value queried is outside the slice None will be returned
154    #[inline]
155    pub fn get(&self, pos_type: PostioningType, x: usize, y: usize) -> Option<&T> {
156        let (abs_x, abs_y) = self.convert_coord(pos_type, x, y)?;
157        
158        unsafe {
159            (*self.parent).get(abs_x, abs_y)
160        }
161    }
162
163    /// Sets the value for the specified absolute position in the space
164    /// If the position specified is outside the space false is returned
165    #[inline]
166    pub fn set(&mut self, pos_type: PostioningType, x: usize, y: usize, value: T) -> bool {
167        if let Some((abs_x, abs_y)) = self.convert_coord(pos_type, x, y) {
168            unsafe {
169                (*self.parent).set(abs_x, abs_y, value)
170            }
171        } else {
172            false
173        }
174    }
175
176    #[inline]
177    pub fn split_horizontal(self, pos_type: PostioningType, x_value: usize) -> HorizontalSplit<SpaceSliceMut<'a, T>> {
178        let left_x = self.x;
179
180        let right_x = match pos_type {
181            PostioningType::Absolute => x_value,
182            PostioningType::Relative => self.x + x_value
183        };
184
185        if right_x > self.width {
186            panic!("Invalid x value ({}) provided for slice with width {}", right_x, self.width);
187        }
188        
189        let left_width = right_x - left_x;
190        let right_width = self.width - left_width;
191
192        HorizontalSplit {
193            left: SpaceSliceMut {
194                parent: self.parent,
195                phantom: PhantomData,
196                
197                x: left_x,
198                width: left_width,
199                
200                y: self.y,
201                height: self.height
202            },
203            right: SpaceSliceMut {
204                parent: self.parent,
205                phantom: PhantomData,
206                
207                x: right_x,
208                width: right_width,
209
210                y: self.y,
211                height: self.height
212            }
213        }
214    }
215    
216    #[inline]
217    pub fn split_vertical(self, pos_type: PostioningType, y_value: usize) -> VerticalSplit<SpaceSliceMut<'a, T>> {
218        let above_y = self.y;
219
220        let below_y = match pos_type {
221            PostioningType::Absolute => y_value,
222            PostioningType::Relative => self.y + y_value
223        };
224
225        if below_y > self.height {
226            panic!("Invalid y value ({}) provided for slice with height {}", below_y, self.height);
227        }
228        
229        let above_height = below_y - above_y;
230        let below_height = self.height - above_height;
231
232        VerticalSplit {
233            above: SpaceSliceMut {
234                parent: self.parent,
235                phantom: PhantomData,
236                
237                y: above_y,
238                height: above_height,
239                
240                x: self.x,
241                width: self.width
242            },
243
244            below: SpaceSliceMut {
245                parent: self.parent,
246                phantom: PhantomData,
247                
248                y: below_y,
249                height: below_height,
250                
251                x: self.x,
252                width: self.width
253            }
254        }
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261
262    #[test]
263    fn horizontal_split_width_check() {
264        let mut space = Space::new(1u32, 4, 4);
265        let space_slice = space.as_slice_mut();
266
267        let HorizontalSplit { left, right } = space_slice.split_horizontal(PostioningType::Absolute, 2);
268
269        assert_eq!(left.width(), 2);
270        assert_eq!(right.width(), 2);
271    }
272    
273    #[test]
274    fn vertical_split_height_check() {
275        let mut space = Space::new(1u32, 4, 4);
276        let space_slice = space.as_slice_mut();
277
278        let VerticalSplit { above, below } = space_slice.split_vertical(PostioningType::Absolute, 2);
279
280        assert_eq!(above.height(), 2);
281        assert_eq!(below.height(), 2);
282    }
283}