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