ironrdp_graphics/
rectangle_processing.rs

1use core::cmp::{max, min};
2
3use ironrdp_pdu::geometry::{InclusiveRectangle, Rectangle as _};
4
5// TODO(@pacmancoder): This code currently works only on `InclusiveRectangle`, but it should be
6// made generic over `Rectangle` trait
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Region {
10    pub extents: InclusiveRectangle,
11    pub rectangles: Vec<InclusiveRectangle>,
12}
13
14impl Region {
15    pub fn new() -> Self {
16        Self {
17            extents: InclusiveRectangle::empty(),
18            rectangles: Vec::new(),
19        }
20    }
21
22    pub fn union_rectangle(&mut self, rectangle: InclusiveRectangle) {
23        if self.rectangles.is_empty() {
24            *self = Self::from(rectangle);
25        } else {
26            let mut dst = Vec::with_capacity(self.rectangles.len() + 1);
27
28            handle_rectangle_higher_relative_to_extents(&rectangle, &self.extents, &mut dst);
29
30            // treat possibly overlapping region
31            let bands = split_bands(self.rectangles.as_slice());
32            let mut bands = bands.as_slice();
33            while let Some((band, bands_new)) = bands.split_first() {
34                bands = bands_new;
35
36                let top_inter_band = if band[0].bottom <= rectangle.top
37                    || rectangle.bottom <= band[0].top
38                    || rectangle_in_band(band, &rectangle)
39                {
40                    // `rectangle` is lower, higher, or in the current band
41                    dst.extend_from_slice(band);
42
43                    rectangle.top
44                } else {
45                    handle_rectangle_that_overlaps_band(&rectangle, band, &mut dst);
46
47                    band[0].bottom
48                };
49
50                if !bands.is_empty() {
51                    let next_band = bands[0];
52                    handle_rectangle_between_bands(&rectangle, band, next_band, &mut dst, top_inter_band);
53                }
54            }
55
56            handle_rectangle_lower_relative_to_extents(&rectangle, &self.extents, &mut dst);
57
58            self.rectangles = dst;
59            self.extents = self.extents.union(&rectangle);
60
61            self.simplify();
62        }
63    }
64
65    #[must_use]
66    pub fn intersect_rectangle(&self, rectangle: &InclusiveRectangle) -> Self {
67        match self.rectangles.len() {
68            0 => Self::new(),
69            1 => self.extents.intersect(rectangle).map(Self::from).unwrap_or_default(),
70            _ => {
71                let rectangles = self
72                    .rectangles
73                    .iter()
74                    .take_while(|r| r.top <= rectangle.bottom)
75                    .filter_map(|r| r.intersect(rectangle))
76                    .collect::<Vec<_>>();
77                let extents = InclusiveRectangle::union_all(rectangles.as_slice());
78
79                let mut region = Self { rectangles, extents };
80                region.simplify();
81
82                region
83            }
84        }
85    }
86
87    fn simplify(&mut self) {
88        /* Simplify consecutive bands that touch and have the same items
89         *
90         *  ====================          ====================
91         *     | 1 |  | 2   |               |   |  |     |
92         *  ====================            |   |  |     |
93         *     | 1 |  | 2   |	   ====>    | 1 |  |  2  |
94         *  ====================            |   |  |     |
95         *     | 1 |  | 2   |               |   |  |     |
96         *  ====================          ====================
97         *
98         */
99
100        if self.rectangles.len() < 2 {
101            return;
102        }
103
104        let mut current_band_start = 0;
105        while current_band_start < self.rectangles.len()
106            && current_band_start + get_current_band(&self.rectangles[current_band_start..]).len()
107                < self.rectangles.len()
108        {
109            let current_band = get_current_band(&self.rectangles[current_band_start..]);
110            let next_band = get_current_band(&self.rectangles[current_band_start + current_band.len()..]);
111
112            if current_band[0].bottom == next_band[0].top && bands_internals_equal(current_band, next_band) {
113                let first_band_len = current_band.len();
114                let second_band_len = next_band.len();
115                let second_band_bottom = next_band[0].bottom;
116                self.rectangles
117                    .drain(current_band_start + first_band_len..current_band_start + first_band_len + second_band_len);
118                self.rectangles
119                    .iter_mut()
120                    .skip(current_band_start)
121                    .take(first_band_len)
122                    .for_each(|r| r.bottom = second_band_bottom);
123            } else {
124                current_band_start += current_band.len();
125            }
126        }
127    }
128}
129
130impl Default for Region {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136impl From<InclusiveRectangle> for Region {
137    fn from(r: InclusiveRectangle) -> Self {
138        Self {
139            extents: r.clone(),
140            rectangles: vec![r],
141        }
142    }
143}
144
145fn handle_rectangle_higher_relative_to_extents(
146    rectangle: &InclusiveRectangle,
147    extents: &InclusiveRectangle,
148    dst: &mut Vec<InclusiveRectangle>,
149) {
150    if rectangle.top < extents.top {
151        dst.push(InclusiveRectangle {
152            top: rectangle.top,
153            bottom: min(extents.top, rectangle.bottom),
154            left: rectangle.left,
155            right: rectangle.right,
156        });
157    }
158}
159
160fn handle_rectangle_lower_relative_to_extents(
161    rectangle: &InclusiveRectangle,
162    extents: &InclusiveRectangle,
163    dst: &mut Vec<InclusiveRectangle>,
164) {
165    if extents.bottom < rectangle.bottom {
166        dst.push(InclusiveRectangle {
167            top: max(extents.bottom, rectangle.top),
168            bottom: rectangle.bottom,
169            left: rectangle.left,
170            right: rectangle.right,
171        });
172    }
173}
174
175fn handle_rectangle_that_overlaps_band(
176    rectangle: &InclusiveRectangle,
177    band: &[InclusiveRectangle],
178    dst: &mut Vec<InclusiveRectangle>,
179) {
180    /* rect overlaps the band:
181                         |    |  |    |
182       ====^=================|    |==|    |=========================== band
183       |   top split     |    |  |    |
184       v                 | 1  |  | 2  |
185       ^                 |    |  |    |  +----+   +----+
186       |   merge zone    |    |  |    |  |    |   | 4  |
187       v                 +----+  |    |  |    |   +----+
188       ^                         |    |  | 3  |
189       |   bottom split          |    |  |    |
190       ====v=========================|    |==|    |===================
191                                 |    |  |    |
192
193        possible cases:
194        1) no top split, merge zone then a bottom split. The band will be split
195           in two
196        2) not band split, only the merge zone, band merged with rect but not split
197        3) a top split, the merge zone and no bottom split. The band will be split
198           in two
199        4) a top split, the merge zone and also a bottom split. The band will be
200           split in 3, but the coalesce algorithm may merge the created bands
201    */
202
203    let band_top = band[0].top;
204    let band_bottom = band[0].bottom;
205
206    if band_top < rectangle.top {
207        // split current band by the current band top and `rectangle.top` (case 3, 4)
208        copy_band(band, dst, band_top, rectangle.top);
209    }
210
211    // split the merge zone (all cases)
212    copy_band_with_union(
213        band,
214        dst,
215        max(rectangle.top, band_top),
216        min(rectangle.bottom, band_bottom),
217        rectangle,
218    );
219
220    // split current band by the `rectangle.bottom` and the current band bottom (case 1, 4)
221    if rectangle.bottom < band_bottom {
222        copy_band(band, dst, rectangle.bottom, band_bottom);
223    }
224}
225
226fn handle_rectangle_between_bands(
227    rectangle: &InclusiveRectangle,
228    band: &[InclusiveRectangle],
229    next_band: &[InclusiveRectangle],
230    dst: &mut Vec<InclusiveRectangle>,
231    top_inter_band: u16,
232) {
233    /* test if a piece of rect should be inserted as a new band between
234     * the current band and the next one. band n and n+1 shouldn't touch.
235     *
236     * ==============================================================
237     *                                                        band n
238     *            +------+                    +------+
239     * ===========| rect |====================|      |===============
240     *            |      |    +------+        |      |
241     *            +------+    | rect |        | rect |
242     *                        +------+        |      |
243     * =======================================|      |================
244     *                                        +------+         band n+1
245     * ===============================================================
246     *
247     */
248
249    let band_bottom = band[0].bottom;
250
251    let next_band_top = next_band[0].top;
252    if next_band_top != band_bottom && band_bottom < rectangle.bottom && rectangle.top < next_band_top {
253        dst.push(InclusiveRectangle {
254            top: top_inter_band,
255            bottom: min(next_band_top, rectangle.bottom),
256            left: rectangle.left,
257            right: rectangle.right,
258        });
259    }
260}
261
262fn rectangle_in_band(band: &[InclusiveRectangle], rectangle: &InclusiveRectangle) -> bool {
263    // part of `rectangle` is higher or lower
264    if rectangle.top < band[0].top || band[0].bottom < rectangle.bottom {
265        return false;
266    }
267
268    for source_rectangle in band {
269        if source_rectangle.left <= rectangle.left {
270            if rectangle.right <= source_rectangle.right {
271                return true;
272            }
273        } else {
274            // as the band is sorted from left to right,
275            // once we've seen an item that is after `rectangle.left`
276            // we are sure that the result is false
277            return false;
278        }
279    }
280
281    false
282}
283
284fn copy_band_with_union(
285    mut band: &[InclusiveRectangle],
286    dst: &mut Vec<InclusiveRectangle>,
287    band_top: u16,
288    band_bottom: u16,
289    union_rectangle: &InclusiveRectangle,
290) {
291    /* merges a band with the given rect
292     * Input:
293     *                   unionRect
294     *               |                |
295     *               |                |
296     * ==============+===============+================================
297     *   |Item1|  |Item2| |Item3|  |Item4|    |Item5|            Band
298     * ==============+===============+================================
299     *    before     |    overlap     |          after
300     *
301     * Resulting band:
302     *   +-----+  +----------------------+    +-----+
303     *   |Item1|  |      Item2           |    |Item3|
304     *   +-----+  +----------------------+    +-----+
305     *
306     *  We first copy as-is items that are before Item2, the first overlapping
307     *  item.
308     *  Then we find the last one that overlap unionRect to aggregate Item2, Item3
309     *  and Item4 to create Item2.
310     *  Finally Item5 is copied as Item3.
311     *
312     *  When no unionRect is provided, we skip the two first steps to just copy items
313     */
314
315    let items_before_union_rectangle = band
316        .iter()
317        .map(|r| InclusiveRectangle {
318            top: band_top,
319            bottom: band_bottom,
320            left: r.left,
321            right: r.right,
322        })
323        .take_while(|r| r.right < union_rectangle.left);
324    let items_before_union_rectangle_len = items_before_union_rectangle.clone().map(|_| 1).sum::<usize>();
325    dst.extend(items_before_union_rectangle);
326    band = &band[items_before_union_rectangle_len..];
327
328    // treat items overlapping with `union_rectangle`
329    let left = min(
330        band.first().map(|r| r.left).unwrap_or(union_rectangle.left),
331        union_rectangle.left,
332    );
333    let mut right = union_rectangle.right;
334    while !band.is_empty() {
335        if band[0].right >= union_rectangle.right {
336            if band[0].left < union_rectangle.right {
337                right = band[0].right;
338                band = &band[1..];
339            }
340            break;
341        }
342        band = &band[1..];
343    }
344    dst.push(InclusiveRectangle {
345        top: band_top,
346        bottom: band_bottom,
347        left,
348        right,
349    });
350
351    // treat remaining items on the same band
352    copy_band(band, dst, band_top, band_bottom);
353}
354
355fn copy_band(band: &[InclusiveRectangle], dst: &mut Vec<InclusiveRectangle>, band_top: u16, band_bottom: u16) {
356    dst.extend(band.iter().map(|r| InclusiveRectangle {
357        top: band_top,
358        bottom: band_bottom,
359        left: r.left,
360        right: r.right,
361    }));
362}
363
364fn split_bands(mut rectangles: &[InclusiveRectangle]) -> Vec<&[InclusiveRectangle]> {
365    let mut bands = Vec::new();
366    while !rectangles.is_empty() {
367        let band = get_current_band(rectangles);
368        rectangles = &rectangles[band.len()..];
369        bands.push(band);
370    }
371
372    bands
373}
374
375fn get_current_band(rectangles: &[InclusiveRectangle]) -> &[InclusiveRectangle] {
376    let band_top = rectangles[0].top;
377
378    for i in 1..rectangles.len() {
379        if rectangles[i].top != band_top {
380            return &rectangles[..i];
381        }
382    }
383
384    rectangles
385}
386
387fn bands_internals_equal(first_band: &[InclusiveRectangle], second_band: &[InclusiveRectangle]) -> bool {
388    if first_band.len() != second_band.len() {
389        return false;
390    }
391
392    for (first_band_rect, second_band_rect) in first_band.iter().zip(second_band.iter()) {
393        if first_band_rect.left != second_band_rect.left || first_band_rect.right != second_band_rect.right {
394            return false;
395        }
396    }
397
398    true
399}
400
401#[cfg(test)]
402mod tests {
403    use std::sync::LazyLock;
404
405    use super::*;
406
407    static REGION_FOR_RECTANGLES_INTERSECTION: LazyLock<Region> = LazyLock::new(|| Region {
408        extents: InclusiveRectangle {
409            left: 1,
410            top: 1,
411            right: 11,
412            bottom: 9,
413        },
414        rectangles: vec![
415            InclusiveRectangle {
416                left: 1,
417                top: 1,
418                right: 5,
419                bottom: 3,
420            },
421            InclusiveRectangle {
422                left: 7,
423                top: 1,
424                right: 8,
425                bottom: 3,
426            },
427            InclusiveRectangle {
428                left: 9,
429                top: 1,
430                right: 11,
431                bottom: 3,
432            },
433            InclusiveRectangle {
434                left: 7,
435                top: 3,
436                right: 11,
437                bottom: 4,
438            },
439            InclusiveRectangle {
440                left: 3,
441                top: 4,
442                right: 6,
443                bottom: 6,
444            },
445            InclusiveRectangle {
446                left: 7,
447                top: 4,
448                right: 11,
449                bottom: 6,
450            },
451            InclusiveRectangle {
452                left: 1,
453                top: 6,
454                right: 3,
455                bottom: 8,
456            },
457            InclusiveRectangle {
458                left: 4,
459                top: 6,
460                right: 5,
461                bottom: 8,
462            },
463            InclusiveRectangle {
464                left: 6,
465                top: 6,
466                right: 10,
467                bottom: 8,
468            },
469            InclusiveRectangle {
470                left: 4,
471                top: 8,
472                right: 5,
473                bottom: 9,
474            },
475            InclusiveRectangle {
476                left: 6,
477                top: 8,
478                right: 10,
479                bottom: 9,
480            },
481        ],
482    });
483
484    #[test]
485    fn union_rectangle_sets_extents_and_single_rectangle_for_empty_region() {
486        let mut region = Region::new();
487
488        let input_rectangle = InclusiveRectangle {
489            left: 5,
490            top: 1,
491            right: 9,
492            bottom: 2,
493        };
494
495        let expected_region = Region {
496            extents: input_rectangle.clone(),
497            rectangles: vec![input_rectangle.clone()],
498        };
499
500        region.union_rectangle(input_rectangle);
501        assert_eq!(expected_region, region);
502    }
503
504    #[test]
505    fn union_rectangle_places_new_rectangle_higher_relative_to_band() {
506        let existing_band_rectangle = InclusiveRectangle {
507            left: 2,
508            top: 3,
509            right: 7,
510            bottom: 7,
511        };
512        let mut region = Region {
513            extents: existing_band_rectangle.clone(),
514            rectangles: vec![existing_band_rectangle.clone()],
515        };
516
517        let input_rectangle = InclusiveRectangle {
518            left: 5,
519            top: 1,
520            right: 9,
521            bottom: 2,
522        };
523
524        let expected_region = Region {
525            extents: InclusiveRectangle {
526                left: 2,
527                top: 1,
528                right: 9,
529                bottom: 7,
530            },
531            rectangles: vec![input_rectangle.clone(), existing_band_rectangle],
532        };
533
534        region.union_rectangle(input_rectangle);
535        assert_eq!(expected_region, region);
536    }
537
538    #[test]
539    fn union_rectangle_places_new_rectangle_lower_relative_to_band() {
540        let existing_band_rectangle = InclusiveRectangle {
541            left: 2,
542            top: 3,
543            right: 7,
544            bottom: 7,
545        };
546        let mut region = Region {
547            extents: existing_band_rectangle.clone(),
548            rectangles: vec![existing_band_rectangle.clone()],
549        };
550
551        let input_rectangle = InclusiveRectangle {
552            left: 1,
553            top: 8,
554            right: 4,
555            bottom: 10,
556        };
557
558        let expected_region = Region {
559            extents: InclusiveRectangle {
560                left: 1,
561                top: 3,
562                right: 7,
563                bottom: 10,
564            },
565            rectangles: vec![existing_band_rectangle, input_rectangle.clone()],
566        };
567
568        region.union_rectangle(input_rectangle);
569        assert_eq!(expected_region, region);
570    }
571
572    #[test]
573    fn union_rectangle_does_not_add_new_rectangle_which_is_inside_a_band() {
574        let existing_band_rectangle = InclusiveRectangle {
575            left: 2,
576            top: 3,
577            right: 7,
578            bottom: 7,
579        };
580        let mut region = Region {
581            extents: existing_band_rectangle.clone(),
582            rectangles: vec![existing_band_rectangle.clone()],
583        };
584
585        let input_rectangle = InclusiveRectangle {
586            left: 5,
587            top: 4,
588            right: 6,
589            bottom: 5,
590        };
591
592        let expected_region = Region {
593            extents: existing_band_rectangle.clone(),
594            rectangles: vec![existing_band_rectangle],
595        };
596
597        region.union_rectangle(input_rectangle);
598        assert_eq!(expected_region, region);
599    }
600
601    #[test]
602    fn union_rectangle_cuts_new_rectangle_top_part_which_crosses_band_on_top() {
603        let existing_band_rectangle = InclusiveRectangle {
604            left: 2,
605            top: 3,
606            right: 7,
607            bottom: 7,
608        };
609        let mut region = Region {
610            extents: existing_band_rectangle.clone(),
611            rectangles: vec![existing_band_rectangle],
612        };
613
614        let input_rectangle = InclusiveRectangle {
615            left: 1,
616            top: 2,
617            right: 4,
618            bottom: 4,
619        };
620
621        let expected_region = Region {
622            extents: InclusiveRectangle {
623                left: 1,
624                top: 2,
625                right: 7,
626                bottom: 7,
627            },
628            rectangles: vec![
629                InclusiveRectangle {
630                    left: 1,
631                    top: 2,
632                    right: 4,
633                    bottom: 3,
634                },
635                InclusiveRectangle {
636                    left: 1,
637                    top: 3,
638                    right: 7,
639                    bottom: 4,
640                },
641                InclusiveRectangle {
642                    left: 2,
643                    top: 4,
644                    right: 7,
645                    bottom: 7,
646                },
647            ],
648        };
649
650        region.union_rectangle(input_rectangle);
651        assert_eq!(expected_region, region);
652    }
653
654    #[test]
655    fn union_rectangle_cuts_new_rectangle_lower_part_which_crosses_band_on_bottom() {
656        let existing_band_rectangle = InclusiveRectangle {
657            left: 2,
658            top: 3,
659            right: 7,
660            bottom: 7,
661        };
662        let mut region = Region {
663            extents: existing_band_rectangle.clone(),
664            rectangles: vec![existing_band_rectangle],
665        };
666
667        let input_rectangle = InclusiveRectangle {
668            left: 5,
669            top: 6,
670            right: 9,
671            bottom: 8,
672        };
673
674        let expected_region = Region {
675            extents: InclusiveRectangle {
676                left: 2,
677                top: 3,
678                right: 9,
679                bottom: 8,
680            },
681            rectangles: vec![
682                InclusiveRectangle {
683                    left: 2,
684                    top: 3,
685                    right: 7,
686                    bottom: 6,
687                },
688                InclusiveRectangle {
689                    left: 2,
690                    top: 6,
691                    right: 9,
692                    bottom: 7,
693                },
694                InclusiveRectangle {
695                    left: 5,
696                    top: 7,
697                    right: 9,
698                    bottom: 8,
699                },
700            ],
701        };
702
703        region.union_rectangle(input_rectangle);
704        assert_eq!(expected_region, region);
705    }
706
707    #[test]
708    fn union_rectangle_cuts_new_rectangle_higher_and_lower_part_which_crosses_band_on_top_and_bottom() {
709        let existing_band_rectangle = InclusiveRectangle {
710            left: 2,
711            top: 3,
712            right: 7,
713            bottom: 7,
714        };
715        let mut region = Region {
716            extents: existing_band_rectangle.clone(),
717            rectangles: vec![existing_band_rectangle],
718        };
719
720        let input_rectangle = InclusiveRectangle {
721            left: 3,
722            top: 1,
723            right: 5,
724            bottom: 11,
725        };
726
727        let expected_region = Region {
728            extents: InclusiveRectangle {
729                left: 2,
730                top: 1,
731                right: 7,
732                bottom: 11,
733            },
734            rectangles: vec![
735                InclusiveRectangle {
736                    left: 3,
737                    top: 1,
738                    right: 5,
739                    bottom: 3,
740                },
741                InclusiveRectangle {
742                    left: 2,
743                    top: 3,
744                    right: 7,
745                    bottom: 7,
746                },
747                InclusiveRectangle {
748                    left: 3,
749                    top: 7,
750                    right: 5,
751                    bottom: 11,
752                },
753            ],
754        };
755
756        region.union_rectangle(input_rectangle);
757        assert_eq!(expected_region, region);
758    }
759
760    #[test]
761    fn union_rectangle_inserts_new_rectangle_in_band_of_3_rectangles_without_merging_with_rectangles() {
762        let mut region = Region {
763            extents: InclusiveRectangle {
764                left: 2,
765                top: 3,
766                right: 15,
767                bottom: 7,
768            },
769            rectangles: vec![
770                InclusiveRectangle {
771                    left: 2,
772                    top: 3,
773                    right: 7,
774                    bottom: 7,
775                },
776                InclusiveRectangle {
777                    left: 8,
778                    top: 3,
779                    right: 9,
780                    bottom: 7,
781                },
782                InclusiveRectangle {
783                    left: 12,
784                    top: 3,
785                    right: 15,
786                    bottom: 7,
787                },
788            ],
789        };
790
791        let input_rectangle = InclusiveRectangle {
792            left: 10,
793            top: 3,
794            right: 11,
795            bottom: 7,
796        };
797        let expected_region = Region {
798            extents: InclusiveRectangle {
799                left: 2,
800                top: 3,
801                right: 15,
802                bottom: 7,
803            },
804            rectangles: vec![
805                InclusiveRectangle {
806                    left: 2,
807                    top: 3,
808                    right: 7,
809                    bottom: 7,
810                },
811                InclusiveRectangle {
812                    left: 8,
813                    top: 3,
814                    right: 9,
815                    bottom: 7,
816                },
817                InclusiveRectangle {
818                    left: 10,
819                    top: 3,
820                    right: 11,
821                    bottom: 7,
822                },
823                InclusiveRectangle {
824                    left: 12,
825                    top: 3,
826                    right: 15,
827                    bottom: 7,
828                },
829            ],
830        };
831
832        region.union_rectangle(input_rectangle);
833        assert_eq!(expected_region, region);
834    }
835
836    #[test]
837    fn union_rectangle_inserts_new_rectangle_in_band_of_3_rectangles_with_merging_with_side_rectangles() {
838        let mut region = Region {
839            extents: InclusiveRectangle {
840                left: 2,
841                top: 3,
842                right: 15,
843                bottom: 7,
844            },
845            rectangles: vec![
846                InclusiveRectangle {
847                    left: 2,
848                    top: 3,
849                    right: 7,
850                    bottom: 7,
851                },
852                InclusiveRectangle {
853                    left: 8,
854                    top: 3,
855                    right: 10,
856                    bottom: 7,
857                },
858                InclusiveRectangle {
859                    left: 13,
860                    top: 3,
861                    right: 15,
862                    bottom: 7,
863                },
864            ],
865        };
866
867        let input_rectangle = InclusiveRectangle {
868            left: 9,
869            top: 3,
870            right: 14,
871            bottom: 7,
872        };
873        let expected_region = Region {
874            extents: InclusiveRectangle {
875                left: 2,
876                top: 3,
877                right: 15,
878                bottom: 7,
879            },
880            rectangles: vec![
881                InclusiveRectangle {
882                    left: 2,
883                    top: 3,
884                    right: 7,
885                    bottom: 7,
886                },
887                InclusiveRectangle {
888                    left: 8,
889                    top: 3,
890                    right: 15,
891                    bottom: 7,
892                },
893            ],
894        };
895
896        region.union_rectangle(input_rectangle);
897        assert_eq!(expected_region, region);
898    }
899
900    #[test]
901    fn union_rectangle_inserts_new_rectangle_in_band_of_3_rectangles_with_merging_with_side_rectangles_on_board() {
902        let mut region = Region {
903            extents: InclusiveRectangle {
904                left: 2,
905                top: 3,
906                right: 15,
907                bottom: 7,
908            },
909            rectangles: vec![
910                InclusiveRectangle {
911                    left: 2,
912                    top: 3,
913                    right: 7,
914                    bottom: 7,
915                },
916                InclusiveRectangle {
917                    left: 8,
918                    top: 3,
919                    right: 10,
920                    bottom: 7,
921                },
922                InclusiveRectangle {
923                    left: 13,
924                    top: 3,
925                    right: 15,
926                    bottom: 7,
927                },
928            ],
929        };
930
931        let input_rectangle = InclusiveRectangle {
932            left: 10,
933            top: 3,
934            right: 13,
935            bottom: 7,
936        };
937        let expected_region = Region {
938            extents: InclusiveRectangle {
939                left: 2,
940                top: 3,
941                right: 15,
942                bottom: 7,
943            },
944            rectangles: vec![
945                InclusiveRectangle {
946                    left: 2,
947                    top: 3,
948                    right: 7,
949                    bottom: 7,
950                },
951                InclusiveRectangle {
952                    left: 8,
953                    top: 3,
954                    right: 13,
955                    bottom: 7,
956                },
957                InclusiveRectangle {
958                    left: 13,
959                    top: 3,
960                    right: 15,
961                    bottom: 7,
962                },
963            ],
964        };
965
966        region.union_rectangle(input_rectangle);
967        assert_eq!(expected_region, region);
968    }
969
970    #[test]
971    fn union_rectangle_inserts_new_rectangle_between_two_bands() {
972        let mut region = Region {
973            extents: InclusiveRectangle {
974                left: 1,
975                top: 3,
976                right: 7,
977                bottom: 10,
978            },
979            rectangles: vec![
980                InclusiveRectangle {
981                    left: 2,
982                    top: 3,
983                    right: 7,
984                    bottom: 7,
985                },
986                InclusiveRectangle {
987                    left: 1,
988                    top: 8,
989                    right: 4,
990                    bottom: 10,
991                },
992            ],
993        };
994
995        let input_rectangle = InclusiveRectangle {
996            left: 3,
997            top: 4,
998            right: 4,
999            bottom: 9,
1000        };
1001        let expected_region = Region {
1002            extents: InclusiveRectangle {
1003                left: 1,
1004                top: 3,
1005                right: 7,
1006                bottom: 10,
1007            },
1008            rectangles: vec![
1009                InclusiveRectangle {
1010                    left: 2,
1011                    top: 3,
1012                    right: 7,
1013                    bottom: 7,
1014                },
1015                InclusiveRectangle {
1016                    left: 3,
1017                    top: 7,
1018                    right: 4,
1019                    bottom: 8,
1020                },
1021                InclusiveRectangle {
1022                    left: 1,
1023                    top: 8,
1024                    right: 4,
1025                    bottom: 10,
1026                },
1027            ],
1028        };
1029
1030        region.union_rectangle(input_rectangle);
1031        assert_eq!(expected_region, region);
1032    }
1033
1034    #[test]
1035    fn simplify_does_not_change_two_different_bands_with_multiple_rectangles() {
1036        let mut region = Region {
1037            extents: InclusiveRectangle {
1038                left: 1,
1039                top: 1,
1040                right: 7,
1041                bottom: 3,
1042            },
1043            rectangles: vec![
1044                InclusiveRectangle {
1045                    left: 1,
1046                    top: 1,
1047                    right: 2,
1048                    bottom: 2,
1049                },
1050                InclusiveRectangle {
1051                    left: 3,
1052                    top: 1,
1053                    right: 4,
1054                    bottom: 2,
1055                },
1056                InclusiveRectangle {
1057                    left: 5,
1058                    top: 1,
1059                    right: 6,
1060                    bottom: 2,
1061                },
1062                InclusiveRectangle {
1063                    left: 1,
1064                    top: 2,
1065                    right: 2,
1066                    bottom: 3,
1067                },
1068                InclusiveRectangle {
1069                    left: 3,
1070                    top: 2,
1071                    right: 4,
1072                    bottom: 3,
1073                },
1074                InclusiveRectangle {
1075                    left: 5,
1076                    top: 2,
1077                    right: 7,
1078                    bottom: 3,
1079                },
1080            ],
1081        };
1082        let expected_region = region.clone();
1083
1084        region.simplify();
1085        assert_eq!(expected_region, region);
1086    }
1087
1088    #[test]
1089    fn simplify_does_not_change_two_different_bands_with_one_rectangle() {
1090        let mut region = Region {
1091            extents: InclusiveRectangle {
1092                left: 2,
1093                top: 1,
1094                right: 7,
1095                bottom: 11,
1096            },
1097            rectangles: vec![
1098                InclusiveRectangle {
1099                    left: 3,
1100                    top: 1,
1101                    right: 5,
1102                    bottom: 3,
1103                },
1104                InclusiveRectangle {
1105                    left: 2,
1106                    top: 3,
1107                    right: 7,
1108                    bottom: 7,
1109                },
1110            ],
1111        };
1112        let expected_region = region.clone();
1113
1114        region.simplify();
1115        assert_eq!(expected_region, region);
1116    }
1117
1118    #[test]
1119    fn simplify_does_not_change_three_different_bands_with_one_rectangle() {
1120        let mut region = Region {
1121            extents: InclusiveRectangle {
1122                left: 2,
1123                top: 1,
1124                right: 7,
1125                bottom: 11,
1126            },
1127            rectangles: vec![
1128                InclusiveRectangle {
1129                    left: 3,
1130                    top: 1,
1131                    right: 5,
1132                    bottom: 3,
1133                },
1134                InclusiveRectangle {
1135                    left: 2,
1136                    top: 3,
1137                    right: 7,
1138                    bottom: 7,
1139                },
1140                InclusiveRectangle {
1141                    left: 3,
1142                    top: 7,
1143                    right: 5,
1144                    bottom: 11,
1145                },
1146            ],
1147        };
1148        let expected_region = region.clone();
1149
1150        region.simplify();
1151        assert_eq!(expected_region, region);
1152    }
1153
1154    #[test]
1155    fn simplify_merges_bands_with_identical_internal_rectangles() {
1156        let mut region = Region {
1157            extents: InclusiveRectangle {
1158                left: 1,
1159                top: 1,
1160                right: 7,
1161                bottom: 3,
1162            },
1163            rectangles: vec![
1164                InclusiveRectangle {
1165                    left: 1,
1166                    top: 1,
1167                    right: 2,
1168                    bottom: 2,
1169                },
1170                InclusiveRectangle {
1171                    left: 3,
1172                    top: 1,
1173                    right: 4,
1174                    bottom: 2,
1175                },
1176                InclusiveRectangle {
1177                    left: 5,
1178                    top: 1,
1179                    right: 6,
1180                    bottom: 2,
1181                },
1182                InclusiveRectangle {
1183                    left: 1,
1184                    top: 2,
1185                    right: 2,
1186                    bottom: 3,
1187                },
1188                InclusiveRectangle {
1189                    left: 3,
1190                    top: 2,
1191                    right: 4,
1192                    bottom: 3,
1193                },
1194                InclusiveRectangle {
1195                    left: 5,
1196                    top: 2,
1197                    right: 6,
1198                    bottom: 3,
1199                },
1200            ],
1201        };
1202        let expected_region = Region {
1203            extents: InclusiveRectangle {
1204                left: 1,
1205                top: 1,
1206                right: 7,
1207                bottom: 3,
1208            },
1209            rectangles: vec![
1210                InclusiveRectangle {
1211                    left: 1,
1212                    top: 1,
1213                    right: 2,
1214                    bottom: 3,
1215                },
1216                InclusiveRectangle {
1217                    left: 3,
1218                    top: 1,
1219                    right: 4,
1220                    bottom: 3,
1221                },
1222                InclusiveRectangle {
1223                    left: 5,
1224                    top: 1,
1225                    right: 6,
1226                    bottom: 3,
1227                },
1228            ],
1229        };
1230
1231        region.simplify();
1232        assert_eq!(expected_region, region);
1233    }
1234
1235    #[test]
1236    fn simplify_merges_three_bands_with_identical_internal_rectangles() {
1237        let mut region = Region {
1238            extents: InclusiveRectangle {
1239                left: 1,
1240                top: 1,
1241                right: 7,
1242                bottom: 3,
1243            },
1244            rectangles: vec![
1245                InclusiveRectangle {
1246                    left: 1,
1247                    top: 1,
1248                    right: 2,
1249                    bottom: 2,
1250                },
1251                InclusiveRectangle {
1252                    left: 3,
1253                    top: 1,
1254                    right: 4,
1255                    bottom: 2,
1256                },
1257                InclusiveRectangle {
1258                    left: 5,
1259                    top: 1,
1260                    right: 6,
1261                    bottom: 2,
1262                },
1263                InclusiveRectangle {
1264                    left: 1,
1265                    top: 2,
1266                    right: 2,
1267                    bottom: 3,
1268                },
1269                InclusiveRectangle {
1270                    left: 3,
1271                    top: 2,
1272                    right: 4,
1273                    bottom: 3,
1274                },
1275                InclusiveRectangle {
1276                    left: 5,
1277                    top: 2,
1278                    right: 6,
1279                    bottom: 3,
1280                },
1281                InclusiveRectangle {
1282                    left: 1,
1283                    top: 3,
1284                    right: 2,
1285                    bottom: 4,
1286                },
1287                InclusiveRectangle {
1288                    left: 3,
1289                    top: 3,
1290                    right: 4,
1291                    bottom: 4,
1292                },
1293                InclusiveRectangle {
1294                    left: 5,
1295                    top: 3,
1296                    right: 6,
1297                    bottom: 4,
1298                },
1299            ],
1300        };
1301        let expected_region = Region {
1302            extents: InclusiveRectangle {
1303                left: 1,
1304                top: 1,
1305                right: 7,
1306                bottom: 3,
1307            },
1308            rectangles: vec![
1309                InclusiveRectangle {
1310                    left: 1,
1311                    top: 1,
1312                    right: 2,
1313                    bottom: 4,
1314                },
1315                InclusiveRectangle {
1316                    left: 3,
1317                    top: 1,
1318                    right: 4,
1319                    bottom: 4,
1320                },
1321                InclusiveRectangle {
1322                    left: 5,
1323                    top: 1,
1324                    right: 6,
1325                    bottom: 4,
1326                },
1327            ],
1328        };
1329
1330        region.simplify();
1331        assert_eq!(expected_region, region);
1332    }
1333
1334    #[test]
1335    fn simplify_merges_two_pairs_of_bands_with_identical_internal_rectangles() {
1336        let mut region = Region {
1337            extents: InclusiveRectangle {
1338                left: 1,
1339                top: 1,
1340                right: 7,
1341                bottom: 5,
1342            },
1343            rectangles: vec![
1344                InclusiveRectangle {
1345                    left: 1,
1346                    top: 1,
1347                    right: 2,
1348                    bottom: 2,
1349                },
1350                InclusiveRectangle {
1351                    left: 3,
1352                    top: 1,
1353                    right: 4,
1354                    bottom: 2,
1355                },
1356                InclusiveRectangle {
1357                    left: 5,
1358                    top: 1,
1359                    right: 6,
1360                    bottom: 2,
1361                },
1362                InclusiveRectangle {
1363                    left: 1,
1364                    top: 2,
1365                    right: 2,
1366                    bottom: 3,
1367                },
1368                InclusiveRectangle {
1369                    left: 3,
1370                    top: 2,
1371                    right: 4,
1372                    bottom: 3,
1373                },
1374                InclusiveRectangle {
1375                    left: 5,
1376                    top: 2,
1377                    right: 6,
1378                    bottom: 3,
1379                },
1380                InclusiveRectangle {
1381                    left: 2,
1382                    top: 3,
1383                    right: 3,
1384                    bottom: 4,
1385                },
1386                InclusiveRectangle {
1387                    left: 4,
1388                    top: 3,
1389                    right: 5,
1390                    bottom: 4,
1391                },
1392                InclusiveRectangle {
1393                    left: 6,
1394                    top: 3,
1395                    right: 7,
1396                    bottom: 4,
1397                },
1398                InclusiveRectangle {
1399                    left: 2,
1400                    top: 4,
1401                    right: 3,
1402                    bottom: 5,
1403                },
1404                InclusiveRectangle {
1405                    left: 4,
1406                    top: 4,
1407                    right: 5,
1408                    bottom: 5,
1409                },
1410                InclusiveRectangle {
1411                    left: 6,
1412                    top: 4,
1413                    right: 7,
1414                    bottom: 5,
1415                },
1416            ],
1417        };
1418        let expected_region = Region {
1419            extents: InclusiveRectangle {
1420                left: 1,
1421                top: 1,
1422                right: 7,
1423                bottom: 5,
1424            },
1425            rectangles: vec![
1426                InclusiveRectangle {
1427                    left: 1,
1428                    top: 1,
1429                    right: 2,
1430                    bottom: 3,
1431                },
1432                InclusiveRectangle {
1433                    left: 3,
1434                    top: 1,
1435                    right: 4,
1436                    bottom: 3,
1437                },
1438                InclusiveRectangle {
1439                    left: 5,
1440                    top: 1,
1441                    right: 6,
1442                    bottom: 3,
1443                },
1444                InclusiveRectangle {
1445                    left: 2,
1446                    top: 3,
1447                    right: 3,
1448                    bottom: 5,
1449                },
1450                InclusiveRectangle {
1451                    left: 4,
1452                    top: 3,
1453                    right: 5,
1454                    bottom: 5,
1455                },
1456                InclusiveRectangle {
1457                    left: 6,
1458                    top: 3,
1459                    right: 7,
1460                    bottom: 5,
1461                },
1462            ],
1463        };
1464
1465        region.simplify();
1466        assert_eq!(expected_region, region);
1467    }
1468
1469    #[test]
1470    fn intersect_rectangle_returns_empty_region_for_not_intersecting_rectangle() {
1471        let region = &*REGION_FOR_RECTANGLES_INTERSECTION;
1472        let expected_region = Region {
1473            extents: InclusiveRectangle {
1474                left: 0,
1475                top: 0,
1476                right: 0,
1477                bottom: 0,
1478            },
1479            rectangles: Vec::new(),
1480        };
1481        let input_rectangle = InclusiveRectangle {
1482            left: 1,
1483            top: 4,
1484            right: 2,
1485            bottom: 5,
1486        };
1487
1488        let actual_region = region.intersect_rectangle(&input_rectangle);
1489        assert_eq!(expected_region, actual_region);
1490    }
1491
1492    #[test]
1493    fn intersect_rectangle_returns_empty_region_for_empty_intersection_region() {
1494        let expected_region: Region = Region {
1495            extents: InclusiveRectangle {
1496                left: 0,
1497                top: 0,
1498                right: 0,
1499                bottom: 0,
1500            },
1501            rectangles: Vec::new(),
1502        };
1503        let input_rectangle = InclusiveRectangle {
1504            left: 5,
1505            top: 2,
1506            right: 6,
1507            bottom: 3,
1508        };
1509
1510        let actual_region = expected_region.intersect_rectangle(&input_rectangle);
1511        assert_eq!(expected_region, actual_region);
1512    }
1513
1514    #[test]
1515    fn intersect_rectangle_returns_part_of_rectangle_that_overlaps_for_region_with_one_rectangle() {
1516        let region = Region {
1517            extents: InclusiveRectangle {
1518                left: 1,
1519                top: 1,
1520                right: 5,
1521                bottom: 3,
1522            },
1523            rectangles: vec![InclusiveRectangle {
1524                left: 1,
1525                top: 1,
1526                right: 5,
1527                bottom: 3,
1528            }],
1529        };
1530        let expected_region = Region {
1531            extents: InclusiveRectangle {
1532                left: 2,
1533                top: 2,
1534                right: 3,
1535                bottom: 3,
1536            },
1537            rectangles: vec![InclusiveRectangle {
1538                left: 2,
1539                top: 2,
1540                right: 3,
1541                bottom: 3,
1542            }],
1543        };
1544        let input_rectangle = InclusiveRectangle {
1545            left: 2,
1546            top: 2,
1547            right: 3,
1548            bottom: 3,
1549        };
1550
1551        let actual_region = region.intersect_rectangle(&input_rectangle);
1552        assert_eq!(expected_region, actual_region);
1553    }
1554
1555    #[test]
1556    fn intersect_rectangle_returns_region_with_parts_of_rectangles_that_intersect_input_rectangle() {
1557        let region = &*REGION_FOR_RECTANGLES_INTERSECTION;
1558        let expected_region = Region {
1559            extents: InclusiveRectangle {
1560                left: 3,
1561                top: 2,
1562                right: 8,
1563                bottom: 5,
1564            },
1565            rectangles: vec![
1566                InclusiveRectangle {
1567                    left: 3,
1568                    top: 2,
1569                    right: 5,
1570                    bottom: 3,
1571                },
1572                InclusiveRectangle {
1573                    left: 7,
1574                    top: 2,
1575                    right: 8,
1576                    bottom: 3,
1577                },
1578                InclusiveRectangle {
1579                    left: 7,
1580                    top: 3,
1581                    right: 8,
1582                    bottom: 4,
1583                },
1584                InclusiveRectangle {
1585                    left: 3,
1586                    top: 4,
1587                    right: 6,
1588                    bottom: 5,
1589                },
1590                InclusiveRectangle {
1591                    left: 7,
1592                    top: 4,
1593                    right: 8,
1594                    bottom: 5,
1595                },
1596            ],
1597        };
1598        let input_rectangle = InclusiveRectangle {
1599            left: 3,
1600            top: 2,
1601            right: 8,
1602            bottom: 5,
1603        };
1604
1605        let actual_region = region.intersect_rectangle(&input_rectangle);
1606        assert_eq!(expected_region, actual_region);
1607    }
1608
1609    #[test]
1610    fn intersect_rectangle_returns_region_with_exact_sizes_of_rectangle_that_overlaps_it() {
1611        let region = &*REGION_FOR_RECTANGLES_INTERSECTION;
1612        let expected_region = Region {
1613            extents: InclusiveRectangle {
1614                left: 2,
1615                top: 2,
1616                right: 4,
1617                bottom: 3,
1618            },
1619            rectangles: vec![InclusiveRectangle {
1620                left: 2,
1621                top: 2,
1622                right: 4,
1623                bottom: 3,
1624            }],
1625        };
1626        let input_rectangle: InclusiveRectangle = InclusiveRectangle {
1627            left: 2,
1628            top: 2,
1629            right: 4,
1630            bottom: 3,
1631        };
1632
1633        let actual_region = region.intersect_rectangle(&input_rectangle);
1634        assert_eq!(expected_region, actual_region);
1635    }
1636}