|
1 use std::{ |
|
2 cmp, |
|
3 ops::Index |
|
4 }; |
|
5 |
|
6 use integral_geometry::{ArcPoints, EquidistantPoints, Line, Point, Rect, Size, SizeMask}; |
|
7 |
|
8 pub struct Land2D<T> { |
|
9 pixels: vec2d::Vec2D<T>, |
|
10 play_box: Rect, |
|
11 mask: SizeMask, |
|
12 } |
|
13 |
|
14 impl<T: Copy + PartialEq> Land2D<T> { |
|
15 pub fn new(play_size: Size, fill_value: T) -> Self { |
|
16 let real_size = play_size.next_power_of_two(); |
|
17 let top_left = Point::new( |
|
18 ((real_size.width - play_size.width) / 2) as i32, |
|
19 (real_size.height - play_size.height) as i32, |
|
20 ); |
|
21 let play_box = Rect::from_size(top_left, play_size); |
|
22 Self { |
|
23 play_box, |
|
24 pixels: vec2d::Vec2D::new(real_size, fill_value), |
|
25 mask: real_size.to_mask(), |
|
26 } |
|
27 } |
|
28 |
|
29 pub fn raw_pixels(&self) -> &[T] { |
|
30 &self.pixels.as_slice() |
|
31 } |
|
32 |
|
33 pub fn raw_pixel_bytes(&self) -> &[u8] { |
|
34 unsafe { |
|
35 self.pixels.as_bytes() |
|
36 } |
|
37 } |
|
38 |
|
39 #[inline] |
|
40 pub fn width(&self) -> usize { |
|
41 self.pixels.width() |
|
42 } |
|
43 |
|
44 #[inline] |
|
45 pub fn height(&self) -> usize { |
|
46 self.pixels.height() |
|
47 } |
|
48 |
|
49 #[inline] |
|
50 pub fn size(&self) -> Size { |
|
51 self.pixels.size() |
|
52 } |
|
53 |
|
54 #[inline] |
|
55 pub fn play_width(&self) -> usize { |
|
56 self.play_box.width() |
|
57 } |
|
58 |
|
59 #[inline] |
|
60 pub fn play_height(&self) -> usize { |
|
61 self.play_box.height() |
|
62 } |
|
63 |
|
64 #[inline] |
|
65 pub fn play_size(&self) -> Size { |
|
66 self.play_box.size() |
|
67 } |
|
68 |
|
69 #[inline] |
|
70 pub fn play_box(&self) -> Rect { |
|
71 self.play_box |
|
72 } |
|
73 |
|
74 #[inline] |
|
75 pub fn is_valid_x(&self, x: i32) -> bool { |
|
76 self.mask.contains_x(x as usize) |
|
77 } |
|
78 |
|
79 #[inline] |
|
80 pub fn is_valid_y(&self, y: i32) -> bool { |
|
81 self.mask.contains_y(y as usize) |
|
82 } |
|
83 |
|
84 #[inline] |
|
85 pub fn is_valid_coordinate(&self, x: i32, y: i32) -> bool { |
|
86 self.is_valid_x(x) && self.is_valid_y(y) |
|
87 } |
|
88 |
|
89 #[inline] |
|
90 pub fn rows(&self) -> impl DoubleEndedIterator<Item = &[T]> { |
|
91 self.pixels.rows() |
|
92 } |
|
93 |
|
94 #[inline] |
|
95 pub fn map<U: Default, F: FnOnce(&mut T) -> U>(&mut self, y: i32, x: i32, f: F) -> U { |
|
96 if self.is_valid_coordinate(x, y) { |
|
97 unsafe { |
|
98 // hey, I just checked that coordinates are valid! |
|
99 f(self.pixels.get_unchecked_mut(y as usize, x as usize)) |
|
100 } |
|
101 } else { |
|
102 U::default() |
|
103 } |
|
104 } |
|
105 |
|
106 #[inline] |
|
107 pub fn map_point<U: Default, F: FnOnce(&mut T) -> U>(&mut self, point: Point, f: F) -> U { |
|
108 self.map(point.y, point.x, f) |
|
109 } |
|
110 |
|
111 pub fn fill_from_iter<I>(&mut self, i: I, value: T) -> usize |
|
112 where |
|
113 I: std::iter::Iterator<Item = Point>, |
|
114 { |
|
115 i.map(|p| { |
|
116 self.map(p.y, p.x, |v| { |
|
117 *v = value; |
|
118 1 |
|
119 }) |
|
120 }).count() |
|
121 } |
|
122 |
|
123 pub fn draw_line(&mut self, line: Line, value: T) -> usize { |
|
124 self.fill_from_iter(line.into_iter(), value) |
|
125 } |
|
126 |
|
127 pub fn fill(&mut self, start_point: Point, border_value: T, fill_value: T) { |
|
128 assert!(self.is_valid_coordinate(start_point.x - 1, start_point.y)); |
|
129 assert!(self.is_valid_coordinate(start_point.x, start_point.y)); |
|
130 |
|
131 let mask = self.mask; |
|
132 let width = self.width(); |
|
133 |
|
134 let mut stack: Vec<(usize, usize, usize, isize)> = Vec::new(); |
|
135 fn push( |
|
136 mask: SizeMask, |
|
137 stack: &mut Vec<(usize, usize, usize, isize)>, |
|
138 xl: usize, |
|
139 xr: usize, |
|
140 y: usize, |
|
141 dir: isize, |
|
142 ) { |
|
143 let yd = y as isize + dir; |
|
144 if mask.contains_y(yd as usize) { |
|
145 stack.push((xl, xr, yd as usize, dir)); |
|
146 } |
|
147 }; |
|
148 |
|
149 let start_x_l = (start_point.x - 1) as usize; |
|
150 let start_x_r = start_point.x as usize; |
|
151 for dir in [-1, 1].iter().cloned() { |
|
152 push(mask, &mut stack, start_x_l, start_x_r, start_point.y as usize, dir); |
|
153 } |
|
154 |
|
155 while let Some((mut xl, mut xr, y, dir)) = stack.pop() { |
|
156 let row = &mut self.pixels[y][..]; |
|
157 while xl > 0 && row[xl] != border_value && row[xl] != fill_value |
|
158 { |
|
159 xl -= 1; |
|
160 } |
|
161 |
|
162 while xr < width - 1 && row[xr] != border_value && row[xr] != fill_value |
|
163 { |
|
164 xr += 1; |
|
165 } |
|
166 |
|
167 while xl < xr { |
|
168 while xl <= xr && (row[xl] == border_value || row[xl] == fill_value) |
|
169 { |
|
170 xl += 1; |
|
171 } |
|
172 |
|
173 let x = xl; |
|
174 |
|
175 while xl <= xr && row[xl] != border_value && row[xl] != fill_value |
|
176 { |
|
177 row[xl] = fill_value; |
|
178 xl += 1; |
|
179 } |
|
180 |
|
181 if x < xl { |
|
182 push(mask, &mut stack, x, xl - 1, y, dir); |
|
183 push(mask, &mut stack, x, xl - 1, y, -dir); |
|
184 } |
|
185 } |
|
186 } |
|
187 } |
|
188 |
|
189 #[inline] |
|
190 fn fill_circle_line<F: Fn(&mut T) -> usize>( |
|
191 &mut self, |
|
192 y: i32, |
|
193 x_from: i32, |
|
194 x_to: i32, |
|
195 f: &F, |
|
196 ) -> usize { |
|
197 let mut result = 0; |
|
198 |
|
199 if self.is_valid_y(y) { |
|
200 for i in cmp::min(x_from, 0) as usize..cmp::max(x_to as usize, self.width() - 1) { |
|
201 unsafe { |
|
202 // coordinates are valid at this point |
|
203 result += f(self.pixels.get_unchecked_mut(y as usize, i)); |
|
204 } |
|
205 } |
|
206 } |
|
207 |
|
208 result |
|
209 } |
|
210 |
|
211 #[inline] |
|
212 fn fill_circle_lines<F: Fn(&mut T) -> usize>( |
|
213 &mut self, |
|
214 x: i32, |
|
215 y: i32, |
|
216 dx: i32, |
|
217 dy: i32, |
|
218 f: &F, |
|
219 ) -> usize { |
|
220 self.fill_circle_line(y + dy, x - dx, x + dx, f) |
|
221 + self.fill_circle_line(y - dy, x - dx, x + dx, f) |
|
222 + self.fill_circle_line(y + dx, x - dy, x + dy, f) |
|
223 + self.fill_circle_line(y - dx, x - dy, x + dy, f) |
|
224 } |
|
225 |
|
226 pub fn change_round<F: Fn(&mut T) -> usize>( |
|
227 &mut self, |
|
228 x: i32, |
|
229 y: i32, |
|
230 radius: i32, |
|
231 f: F, |
|
232 ) -> usize { |
|
233 ArcPoints::new(radius) |
|
234 .map(&mut |p: Point| self.fill_circle_lines(x, y, p.x, p.y, &f)) |
|
235 .sum() |
|
236 } |
|
237 |
|
238 fn fill_row(&mut self, center: Point, offset: Point, value: T) -> usize { |
|
239 let row_index = center.y + offset.y; |
|
240 if self.is_valid_y(row_index) { |
|
241 let from_x = cmp::max(0, center.x - offset.x) as usize; |
|
242 let to_x = cmp::min(self.width() - 1, (center.x + offset.x) as usize); |
|
243 self.pixels[row_index as usize][from_x..=to_x] |
|
244 .iter_mut() |
|
245 .for_each(|v| *v = value); |
|
246 to_x - from_x + 1 |
|
247 } else { |
|
248 0 |
|
249 } |
|
250 } |
|
251 |
|
252 pub fn fill_circle(&mut self, center: Point, radius: i32, value: T) -> usize { |
|
253 let transforms = [[0, 1, 1, 0], [0, 1, -1, 0], [1, 0, 0, 1], [1, 0, 0, -1]]; |
|
254 ArcPoints::new(radius) |
|
255 .map(|vector| { |
|
256 transforms |
|
257 .iter() |
|
258 .map(|m| self.fill_row(center, vector.transform(m), value)) |
|
259 .sum::<usize>() |
|
260 }).sum() |
|
261 } |
|
262 |
|
263 pub fn draw_thick_line(&mut self, line: Line, radius: i32, value: T) -> usize { |
|
264 let mut result = 0; |
|
265 |
|
266 for vector in ArcPoints::new(radius) { |
|
267 for delta in EquidistantPoints::new(vector) { |
|
268 for point in line.into_iter() { |
|
269 self.map_point(point + delta, |p| { |
|
270 if *p != value { |
|
271 *p = value; |
|
272 result += 1; |
|
273 } |
|
274 }) |
|
275 } |
|
276 } |
|
277 } |
|
278 |
|
279 result |
|
280 } |
|
281 } |
|
282 |
|
283 impl<T> Index<usize> for Land2D<T> { |
|
284 type Output = [T]; |
|
285 #[inline] |
|
286 fn index(&self, row: usize) -> &[T] { |
|
287 &self.pixels[row] |
|
288 } |
|
289 } |
|
290 |
|
291 #[cfg(test)] |
|
292 mod tests { |
|
293 use super::*; |
|
294 |
|
295 #[test] |
|
296 fn basics() { |
|
297 let l: Land2D<u8> = Land2D::new(Size::new(30, 50), 0); |
|
298 |
|
299 assert_eq!(l.play_width(), 30); |
|
300 assert_eq!(l.play_height(), 50); |
|
301 assert_eq!(l.width(), 32); |
|
302 assert_eq!(l.height(), 64); |
|
303 |
|
304 assert!(l.is_valid_coordinate(0, 0)); |
|
305 assert!(!l.is_valid_coordinate(-1, -1)); |
|
306 |
|
307 assert!(l.is_valid_coordinate(31, 63)); |
|
308 assert!(!l.is_valid_coordinate(32, 63)); |
|
309 assert!(!l.is_valid_coordinate(31, 64)); |
|
310 } |
|
311 |
|
312 #[test] |
|
313 fn fill() { |
|
314 let mut l: Land2D<u8> = Land2D::new(Size::square(128), 0); |
|
315 |
|
316 l.draw_line(Line::new(Point::new(0, 0), Point::new(32, 96)), 1); |
|
317 l.draw_line(Line::new(Point::new(32, 96), Point::new(64, 32)), 1); |
|
318 l.draw_line(Line::new(Point::new(64, 32), Point::new(96, 80)), 1); |
|
319 l.draw_line(Line::new(Point::new(96, 80), Point::new(128, 0)), 1); |
|
320 |
|
321 l.draw_line(Line::new(Point::new(0, 128), Point::new(64, 96)), 1); |
|
322 l.draw_line(Line::new(Point::new(128, 128), Point::new(64, 96)), 1); |
|
323 |
|
324 l.fill(Point::new(32, 32), 1, 2); |
|
325 l.fill(Point::new(16, 96), 1, 3); |
|
326 l.fill(Point::new(60, 100), 1, 4); |
|
327 |
|
328 assert_eq!(l.pixels[0][0], 1); |
|
329 assert_eq!(l.pixels[96][64], 1); |
|
330 |
|
331 assert_eq!(l.pixels[40][32], 2); |
|
332 assert_eq!(l.pixels[40][96], 2); |
|
333 assert_eq!(l.pixels[5][0], 3); |
|
334 assert_eq!(l.pixels[120][0], 3); |
|
335 assert_eq!(l.pixels[5][127], 3); |
|
336 assert_eq!(l.pixels[120][127], 3); |
|
337 assert_eq!(l.pixels[35][64], 3); |
|
338 assert_eq!(l.pixels[120][20], 4); |
|
339 assert_eq!(l.pixels[120][100], 4); |
|
340 assert_eq!(l.pixels[100][64], 4); |
|
341 } |
|
342 } |