1use crate::clipping::ClipMode::*;
2use graphics_shapes::coord::Coord;
3use graphics_shapes::prelude::{Circle, Rect};
4use graphics_shapes::Shape;
5use log::error;
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8use std::mem::swap;
9
10#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11#[derive(Clone, Debug, Eq, PartialEq)]
12enum ClipShape {
13 Box(Rect),
14 Round(Circle),
15}
16
17impl ClipShape {
18 pub fn contains(&self, xy: (isize, isize)) -> bool {
19 match self {
20 ClipShape::Box(rect) => rect.contains(Coord::from(xy)),
21 ClipShape::Round(circle) => circle.contains(Coord::from(xy)),
22 }
23 }
24}
25
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27#[derive(Clone, Debug, Eq, PartialEq)]
28enum ClipElement {
29 Add(ClipShape),
30 Remove(ClipShape),
31}
32
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[derive(Clone, Debug, Eq, PartialEq)]
35enum ClipMode {
36 Nothing,
37 Simple(ClipShape),
38 Complex(Vec<ClipElement>),
39 Custom(Vec<bool>),
40}
41
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
56#[derive(Debug, Clone, Eq, PartialEq)]
57pub struct Clip {
58 width: usize,
59 height: usize,
60 mode: ClipMode,
61 old_mode: ClipMode,
62 valid_pixel_map: Option<Vec<bool>>,
63 old_valid_pixel_map: Option<Vec<bool>>,
64 auto_build_map: bool,
65}
66
67impl Clip {
68 pub fn new(width: usize, height: usize) -> Self {
69 Self {
70 width,
71 height,
72 mode: Nothing,
73 old_mode: Nothing,
74 valid_pixel_map: None,
75 old_valid_pixel_map: None,
76 auto_build_map: true,
77 }
78 }
79}
80
81impl Clip {
82 pub fn is_nothing(&self) -> bool {
83 matches!(&self.mode, Nothing)
84 }
85
86 pub fn is_simple(&self) -> bool {
87 matches!(&self.mode, Simple(_))
88 }
89
90 pub fn is_complex(&self) -> bool {
91 matches!(&self.mode, Complex(_))
92 }
93
94 pub fn is_custom(&self) -> bool {
95 matches!(&self.mode, Custom(_))
96 }
97
98 pub fn set_auto_build_map(&mut self, auto_build_map: bool) {
99 self.auto_build_map = auto_build_map;
100 }
101}
102
103impl Clip {
104 pub fn is_valid(&self, xy: (isize, isize)) -> bool {
105 let i = xy.0 + xy.1 * (self.width as isize);
106 let u = i.max(0) as usize;
107 match &self.mode {
108 Nothing => true,
109 Simple(shape) => shape.contains(xy),
110 Complex(_) => {
111 if let Some(map) = &self.valid_pixel_map {
112 map[u]
113 } else {
114 error!("Using complex clip but pixel map hasn't been built");
115 true
116 }
117 }
118 Custom(map) => map[u],
119 }
120 }
121}
122
123impl Clip {
124 fn store_current_mode(&mut self) {
125 swap(&mut self.old_mode, &mut self.mode);
126 swap(&mut self.old_valid_pixel_map, &mut self.valid_pixel_map);
127 self.valid_pixel_map = None;
128 }
129}
130
131impl Clip {
132 pub fn set_all_valid(&mut self) {
134 self.store_current_mode();
135 self.mode = Nothing;
136 }
137
138 pub fn set_valid_rect(&mut self, rect: Rect) {
140 self.store_current_mode();
141 self.mode = Simple(ClipShape::Box(rect));
142 }
143
144 pub fn set_valid_circle(&mut self, circle: Circle) {
146 self.store_current_mode();
147 self.mode = Simple(ClipShape::Round(circle));
148 }
149
150 pub fn custom(&mut self, pixel_map: Vec<bool>) {
152 self.store_current_mode();
153 self.mode = Custom(pixel_map);
154 }
155
156 pub fn get_pixel_map(&mut self) -> Vec<bool> {
157 match &self.mode {
158 Simple(_) | Nothing => self.build_pixel_map(),
159 Complex(_) => {
160 if let Some(map) = &self.valid_pixel_map {
161 map.clone()
162 } else {
163 self.update_pixel_map();
164 self.valid_pixel_map.as_ref().unwrap().clone()
165 }
166 }
167 Custom(map) => map.clone(),
168 }
169 }
170}
171
172impl Clip {
173 fn swap_to_complex(&mut self) {
174 if !self.is_complex() {
175 self.store_current_mode();
176 self.mode = Complex(vec![]);
177 }
178 }
179
180 fn add(&mut self, element: ClipElement) {
181 self.swap_to_complex();
182 if let Complex(list) = &mut self.mode {
183 list.push(element);
184 }
185 if self.auto_build_map {
186 self.update_pixel_map();
187 }
188 }
189
190 pub fn add_rect(&mut self, rect: Rect) {
193 self.add(ClipElement::Add(ClipShape::Box(rect)));
194 }
195
196 pub fn remove_rect(&mut self, rect: Rect) {
199 self.add(ClipElement::Remove(ClipShape::Box(rect)));
200 }
201
202 pub fn add_circle(&mut self, circle: Circle) {
205 self.add(ClipElement::Add(ClipShape::Round(circle)));
206 }
207
208 pub fn remove_circle(&mut self, circle: Circle) {
211 self.add(ClipElement::Remove(ClipShape::Round(circle)));
212 }
213}
214
215impl Clip {
216 pub fn update_pixel_map(&mut self) {
217 if self.is_complex() {
218 self.valid_pixel_map = Some(self.build_pixel_map())
219 } else {
220 self.valid_pixel_map = None;
221 }
222 }
223
224 fn build_pixel_map(&self) -> Vec<bool> {
225 match &self.mode {
226 Nothing => vec![true; self.width * self.height],
227 Simple(shape) => self.build_simple_map(shape),
228 Complex(elements) => self.build_complex_map(elements),
229 Custom(map) => map.clone(),
230 }
231 }
232
233 fn build_simple_map(&self, shape: &ClipShape) -> Vec<bool> {
234 let mut output = vec![false; self.width * self.height];
235 for x in 0..self.width {
236 for y in 0..self.height {
237 let i = x + y * self.width;
238 let contains = shape.contains((x as isize, y as isize));
239 if contains {
240 output[i] = true;
241 }
242 }
243 }
244 output
245 }
246
247 fn build_complex_map(&self, elements: &[ClipElement]) -> Vec<bool> {
248 let mut output = vec![true; self.width * self.height];
249 for x in 0..self.width {
250 for y in 0..self.height {
251 let mut valid = true;
252 for element in elements {
253 match element {
254 ClipElement::Add(shape) => {
255 if shape.contains((x as isize, y as isize)) {
256 valid = true;
257 }
258 }
259 ClipElement::Remove(shape) => {
260 if shape.contains((x as isize, y as isize)) {
261 valid = false;
262 }
263 }
264 }
265 }
266 let i = x + y * self.width;
267 if i >= output.len() {
268 break;
269 }
270 output[x + y * self.width] = valid;
271 }
272 }
273 output
274 }
275}
276
277#[cfg(test)]
278mod test {
279 use crate::clipping::Clip;
280 use graphics_shapes::rect::Rect;
281
282 #[test]
283 fn check_all_pixels_valid_for_none() {
284 let mut clip = Clip::new(4, 4);
285 assert_eq!(clip.get_pixel_map(), vec![true; 16]);
286 }
287
288 #[test]
289 fn check_pixels_valid_for_square() {
290 let mut clip = Clip::new(4, 4);
291 clip.set_valid_rect(Rect::new((1, 1), (2, 2)));
292 let expected = vec![
293 false, false, false, false, false, true, true, false, false, true, true, false, false,
294 false, false, false,
295 ];
296 assert_eq!(clip.get_pixel_map(), expected);
297 }
298
299 #[test]
300 fn check_pixels_split_horz() {
301 let mut clip = Clip::new(4, 4);
302 clip.set_valid_rect(Rect::new((2, 0), (3, 3)));
303 let expected = vec![
304 false, false, true, true, false, false, true, true, false, false, true, true, false,
305 false, true, true,
306 ];
307 assert_eq!(clip.get_pixel_map(), expected);
308 }
309
310 #[test]
311 fn complex() {
312 let mut clip = Clip::new(4, 4);
313
314 clip.add_rect(Rect::new((0, 0), (3, 3))); let expected = vec![true; 16];
316 assert_eq!(clip.get_pixel_map(), expected);
317
318 clip.remove_rect(Rect::new((2, 0), (3, 3))); let expected = vec![
320 true, true, false, false, true, true, false, false, true, true, false, false, true,
321 true, false, false,
322 ];
323 assert_eq!(clip.get_pixel_map(), expected);
324
325 clip.add_rect(Rect::new((3, 0), (3, 3))); let expected = vec![
327 true, true, false, true, true, true, false, true, true, true, false, true, true, true,
328 false, true,
329 ];
330 assert_eq!(clip.get_pixel_map(), expected);
331 }
332}