use crate::rect::{Container, Rect, Region}; use crate::utils::windows::WindowsExt; use smallvec::SmallVec; use std::cmp::Ordering; use std::collections::{BinaryHeap}; use std::mem; use std::ops::Deref; use std::rc::Rc; impl Region { pub fn new(rect: Rect) -> Rc { let mut rects = SmallVec::new(); rects.push(rect); Rc::new(Self { rects, extents: rect, }) } pub fn from_rects(rects: &[Rect]) -> Rc { if rects.is_empty() { return Rc::new(Self::default()); } if rects.len() == 1 { return Self::new(rects[0]); } let rects = rects_to_bands(rects); Rc::new(Self { extents: extents(&rects), rects, }) } pub fn union(self: &Rc, other: &Rc) -> Rc { if self.extents.is_empty() { return other.clone(); } if other.extents.is_empty() { return self.clone(); } let rects = op::(&self.rects, &other.rects); Rc::new(Self { rects, extents: self.extents.union(other.extents), }) } pub fn subtract(self: &Rc, other: &Rc) -> Rc { if self.extents.is_empty() || other.extents.is_empty() { return self.clone(); } let rects = op::(&self.rects, &other.rects); Rc::new(Self { extents: extents(&rects), rects, }) } pub fn extents(&self) -> Rect { self.extents } } impl Deref for Region { type Target = [Rect]; fn deref(&self) -> &Self::Target { &self.rects } } struct Bands<'a> { rects: &'a [Rect], } #[derive(Copy, Clone)] struct Band<'a> { rects: &'a [Rect], y1: i32, y2: i32, } impl<'a> Band<'a> { fn can_merge_with(&self, next: &Band) -> bool { next.rects.len() == self.rects.len() && next.y1 == self.y2 && next .rects .iter() .zip(self.rects.iter()) .all(|(a, b)| (a.x1, a.x2) == (b.x1, b.x2)) } } impl<'a> Iterator for Bands<'a> { type Item = Band<'a>; fn next(&mut self) -> Option { if self.rects.is_empty() { return None; } let y1 = self.rects[0].y1(); let y2 = self.rects[0].y2(); for (pos, rect) in self.rects[1..].iter().enumerate() { if rect.y1() != y1 { let (res, rects) = self.rects.split_at(pos + 1); self.rects = rects; return Some(Band { rects: res, y1, y2 }); } } Some(Band { rects: mem::replace(&mut self.rects, &[]), y1, y2, }) } } fn extents(a: &[Rect]) -> Rect { let mut a = a.iter(); let mut res = match a.next() { Some(a) => *a, _ => return Rect::default(), }; for a in a { res.x1 = res.x1.min(a.x1); res.y1 = res.y1.min(a.y1); res.x2 = res.x2.max(a.x2); res.y2 = res.y2.max(a.y2); } res } fn op(a: &[Rect], b: &[Rect]) -> Container { let mut res = Container::new(); let mut prev_band_y2 = 0; let mut prev_band_start = 0; let mut cur_band_start; let mut a_bands = Bands { rects: a }; let mut b_bands = Bands { rects: b }; let mut a_opt = a_bands.next(); let mut b_opt = b_bands.next(); macro_rules! fixup_new_band { ($y1:expr, $y2:expr) => {{ if prev_band_y2 != $y1 || !coalesce(&mut res, prev_band_start, cur_band_start, $y2) { prev_band_start = cur_band_start; } prev_band_y2 = $y2; }}; } macro_rules! append_nonoverlapping { ($append_opt:expr, $a:expr, $a_opt:expr, $a_bands:expr, $b:expr) => {{ if $append_opt { let y2 = $a.y2.min($b.y1); cur_band_start = res.len(); res.reserve($a.rects.len()); for rect in $a.rects { res.push(Rect { x1: rect.x1, y1: $a.y1, x2: rect.x2, y2, }); } fixup_new_band!($a.y1, y2); } if $a.y2 <= $b.y1 { $a_opt = $a_bands.next(); } else { $a.y1 = $b.y1; } }}; } while let (Some(a), Some(b)) = (&mut a_opt, &mut b_opt) { if a.y1 < b.y1 { append_nonoverlapping!(O::APPEND_NON_A, a, a_opt, a_bands, b); } else if b.y1 < a.y1 { append_nonoverlapping!(O::APPEND_NON_B, b, b_opt, b_bands, a); } else { let y2 = a.y2.min(b.y2); cur_band_start = res.len(); O::handle_band(&mut res, a.rects, b.rects, a.y1, y2); if res.len() > cur_band_start { fixup_new_band!(a.y1, y2); } if a.y2 == y2 { a_opt = a_bands.next(); } else { a.y1 = y2; } if b.y2 == y2 { b_opt = b_bands.next(); } else { b.y1 = y2; } } } macro_rules! push_trailing { ($a_opt:expr, $a_bands:expr) => {{ while let Some(a) = $a_opt { cur_band_start = res.len(); res.reserve(a.rects.len()); for rect in a.rects { res.push(Rect { x1: rect.x1, y1: a.y1, x2: rect.x2, y2: a.y2, }); } fixup_new_band!(a.y1, a.y2); $a_opt = $a_bands.next(); } }}; } if O::APPEND_NON_A { push_trailing!(a_opt, a_bands); } if O::APPEND_NON_B { push_trailing!(b_opt, b_bands); } res.shrink_to_fit(); res } fn coalesce(new: &mut Container, a: usize, b: usize, y2: i32) -> bool { if new.len() - b != b - a { return false; } let slice_a = &new[a..b]; let slice_b = &new[b..]; for (a, b) in slice_a.iter().zip(slice_b.iter()) { if (a.x1(), a.x2()) != (b.x1(), b.x2()) { return false; } } for rect in &mut new[a..b] { rect.y2 = y2; } new.truncate(b); true } trait Op { const APPEND_NON_A: bool; const APPEND_NON_B: bool; fn handle_band(new: &mut Container, a: &[Rect], b: &[Rect], y1: i32, y2: i32); } struct Union; impl Op for Union { const APPEND_NON_A: bool = true; const APPEND_NON_B: bool = true; fn handle_band(new: &mut Container, mut a: &[Rect], mut b: &[Rect], y1: i32, y2: i32) { let mut x1; let mut x2; macro_rules! push { () => { new.push(Rect { x1, y1, x2, y2 }); }; } macro_rules! merge { ($r:expr) => { if $r.x1 <= x2 { if $r.x2 > x2 { x2 = $r.x2; } } else { push!(); x1 = $r.x1; x2 = $r.x2; } }; } if a[0].x1 < b[0].x1 { x1 = a[0].x1; x2 = a[0].x2; a = &a[1..]; } else { x1 = b[0].x1; x2 = b[0].x2; b = &b[1..]; } let mut a_iter = a.iter(); let mut b_iter = b.iter(); let mut a_opt = a_iter.next(); let mut b_opt = b_iter.next(); while let (Some(a), Some(b)) = (a_opt, b_opt) { if a.x1 < b.x1 { merge!(a); a_opt = a_iter.next(); } else { merge!(b); b_opt = b_iter.next(); } } while let Some(a) = a_opt { merge!(a); a_opt = a_iter.next(); } while let Some(b) = b_opt { merge!(b); b_opt = b_iter.next(); } push!(); } } struct Subtract; impl Op for Subtract { const APPEND_NON_A: bool = true; const APPEND_NON_B: bool = false; fn handle_band(new: &mut Container, a: &[Rect], b: &[Rect], y1: i32, y2: i32) { let mut x1; let mut x2; macro_rules! push { ($x2:expr) => { new.push(Rect { x1, y1, x2: $x2, y2, }); }; } let mut a_iter = a.iter(); let mut b_iter = b.iter(); macro_rules! pull { () => { match a_iter.next() { Some(n) => { x1 = n.x1; x2 = n.x2; } _ => return, } }; } pull!(); let mut b_opt = b_iter.next(); while let Some(b) = b_opt { if b.x2 <= x1 { b_opt = b_iter.next(); } else if b.x1 >= x2 { push!(x2); pull!(); } else { if b.x1 > x1 { push!(b.x1); } if b.x2 < x2 { x1 = b.x2; } else { pull!(); } } } loop { push!(x2); pull!(); } } } fn rects_to_bands(rects_tmp: &[Rect]) -> Container { #[derive(Copy, Clone)] struct W(Rect); impl Eq for W {} impl PartialEq for W { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl PartialOrd for W { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for W { fn cmp(&self, other: &Self) -> Ordering { self.0 .y1 .cmp(&other.0.y1) .then_with(|| self.0.x1.cmp(&other.0.x1)) .reverse() } } impl Deref for W { type Target = Rect; fn deref(&self) -> &Self::Target { &self.0 } } let ys = { let mut tmp: Vec<_> = rects_tmp.iter().flat_map(|r| [r.y1, r.y2]).collect(); tmp.sort_unstable(); let mut last = None; let mut res = vec![]; for y in tmp { if Some(y) != last { last = Some(y); res.push(y); } } res }; let mut rects = BinaryHeap::with_capacity(rects_tmp.len()); for rect in rects_tmp.iter().copied() { if !rect.is_empty() { rects.push(W(rect)); } } let mut res = Container::new(); for &[y1, y2] in ys.array_windows_ext::<2>() { loop { macro_rules! check_rect { ($rect:expr) => {{ if $rect.y1 != y1 { break; } rects.pop(); if y2 < $rect.y2 { $rect.0.y1 = y2; rects.push($rect); } }}; } if let Some(mut rect) = rects.peek().copied() { check_rect!(rect); let mut x1 = rect.x1; let mut x2 = rect.x2; while let Some(mut rect) = rects.peek().copied() { check_rect!(rect); if rect.x1 > x2 { res.push(Rect { x1, x2, y1, y2 }); x1 = rect.x1; x2 = rect.x2; } else { x2 = x2.max(rect.x2); } } res.push(Rect { x1, x2, y1, y2 }); } break; } } let mut needs_merge = false; let mut num_elements = res.len(); let mut bands = Bands { rects: &res }.peekable(); while let Some(band) = bands.next() { let next = match bands.peek() { Some(next) => next, _ => break, }; if band.can_merge_with(next) { needs_merge = true; num_elements -= band.rects.len(); } } if !needs_merge { res.shrink_to_fit(); return res; } let mut merged = Container::with_capacity(num_elements); let mut bands = Bands { rects: &res }.peekable(); while let Some(mut band) = bands.next() { while let Some(next) = bands.peek() { if band.can_merge_with(next) { band.y2 = next.y2; bands.next(); } else { break; } } for mut rect in band.rects.iter().copied() { rect.y2 = band.y2; merged.push(rect); } } merged } #[derive(Copy, Clone, Eq, PartialEq)] enum BuilderOp { Add, Sub, } impl Default for BuilderOp { fn default() -> Self { Self::Add } } #[derive(Default)] pub struct RegionBuilder { base: Rc, op: BuilderOp, pending: Vec, } impl RegionBuilder { pub fn add(&mut self, rect: Rect) { self.set_op(BuilderOp::Add); self.pending.push(rect); } pub fn sub(&mut self, rect: Rect) { self.set_op(BuilderOp::Sub); self.pending.push(rect); } pub fn get(&mut self) -> Rc { self.flush(); self.base.clone() } pub fn clear(&mut self) { self.pending.clear(); self.base = Rc::new(Region::default()); } fn set_op(&mut self, op: BuilderOp) { if self.op != op { self.flush(); self.op = op; } } fn flush(&mut self) { if self.pending.is_empty() { return; } let region = Region::from_rects(&self.pending); self.base = match self.op { BuilderOp::Add => self.base.union(®ion), BuilderOp::Sub => self.base.subtract(®ion), }; } }