1use std::marker::PhantomData;
2
3pub struct Space<T> {
6 data: Box<[T]>,
7 width: usize,
8 height: usize
9}
10
11impl<T> Space<T> {
12 #[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 #[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 #[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 #[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 #[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
85pub enum PostioningType {
87 Absolute,
89
90 Relative
94}
95
96pub struct HorizontalSplit<T> {
98 pub left: T,
99 pub right: T
100}
101
102pub struct VerticalSplit<T> {
104 pub above: T,
105 pub below: T
106}
107
108pub 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 #[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 #[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}