From 6243278f5fc56d3293d67299b43002cee5eeeda8 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 23 Feb 2025 14:41:36 +0100 Subject: [PATCH] region: add tagged regions --- algorithms/src/lib.rs | 1 + algorithms/src/rect.rs | 55 +++- algorithms/src/rect/region.rs | 300 ++++++++++++++--- src/rect.rs | 136 ++++++-- src/rect/region.rs | 102 ++++-- src/rect/tests.rs | 592 +++++++++++++++++++++++++++++++++- 6 files changed, 1087 insertions(+), 99 deletions(-) diff --git a/algorithms/src/lib.rs b/algorithms/src/lib.rs index 76553ac9..8887c5b2 100644 --- a/algorithms/src/lib.rs +++ b/algorithms/src/lib.rs @@ -1,6 +1,7 @@ #![allow( clippy::mem_replace_with_default, clippy::comparison_chain, + clippy::collapsible_else_if, clippy::needless_lifetimes )] diff --git a/algorithms/src/rect.rs b/algorithms/src/rect.rs index a0cc6e28..5e720071 100644 --- a/algorithms/src/rect.rs +++ b/algorithms/src/rect.rs @@ -5,31 +5,72 @@ use { std::fmt::{Debug, Formatter}, }; +pub trait Tag: Copy + Eq + Ord + Debug + Default + Sized { + const IS_SIGNIFICANT: bool; + + fn constrain(self) -> Self; +} + +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Default)] +pub struct NoTag; + +impl Tag for NoTag { + const IS_SIGNIFICANT: bool = false; + + #[inline(always)] + fn constrain(self) -> Self { + Self + } +} + +impl Tag for u32 { + const IS_SIGNIFICANT: bool = true; + + #[inline(always)] + fn constrain(self) -> Self { + self & 1 + } +} + #[derive(Copy, Clone, Eq, PartialEq, Default)] -pub struct RectRaw { +pub struct RectRaw +where + T: Tag, +{ pub x1: i32, pub y1: i32, pub x2: i32, pub y2: i32, + pub tag: T, } -impl Debug for RectRaw { +impl Debug for RectRaw +where + T: Tag, +{ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Rect") + let mut debug = f.debug_struct("Rect"); + debug .field("x1", &self.x1) .field("y1", &self.y1) .field("x2", &self.x2) .field("y2", &self.y2) .field("width", &(self.x2 - self.x1)) - .field("height", &(self.y2 - self.y1)) - .finish() + .field("height", &(self.y2 - self.y1)); + if T::IS_SIGNIFICANT { + debug.field("tag", &self.tag); + } + debug.finish() } } -impl RectRaw { +impl RectRaw +where + T: Tag, +{ fn is_empty(&self) -> bool { self.x1 == self.x2 || self.y1 == self.y2 } } -type Container = SmallVec<[RectRaw; 1]>; +type Container = SmallVec<[RectRaw; 1]>; diff --git a/algorithms/src/rect/region.rs b/algorithms/src/rect/region.rs index 6cb8fa1c..1ed22bd5 100644 --- a/algorithms/src/rect/region.rs +++ b/algorithms/src/rect/region.rs @@ -1,44 +1,64 @@ use { crate::{ - rect::{Container, RectRaw}, + rect::{Container, NoTag, RectRaw, Tag}, windows::WindowsExt, }, - std::{cmp::Ordering, collections::BinaryHeap, mem, ops::Deref}, + std::{cmp::Ordering, collections::BinaryHeap, marker::PhantomData, mem, ops::Deref}, }; pub fn union(left: &Container, right: &Container) -> Container { - op::(left, right) + op::<_, _, _, Union>(left, right) } pub fn subtract(left: &Container, right: &Container) -> Container { - op::(left, right) + op::<_, _, _, Subtract>(left, right) } -struct Bands<'a> { - rects: &'a [RectRaw], +pub fn intersect(left: &Container, right: &Container) -> Container { + op::<_, _, _, Intersect>(left, right) +} + +pub fn intersect_tagged(left: &Container, right: &Container) -> Container { + op::<_, _, _, Intersect>(left, right) +} + +struct Bands<'a, T> +where + T: Tag, +{ + rects: &'a [RectRaw], } #[derive(Copy, Clone)] -struct Band<'a> { - rects: &'a [RectRaw], +struct Band<'a, T> +where + T: Tag, +{ + rects: &'a [RectRaw], y1: i32, y2: i32, } -impl<'a> Band<'a> { - fn can_merge_with(&self, next: &Band) -> bool { +impl<'a, T> Band<'a, T> +where + T: Tag, +{ + fn can_merge_with(&self, next: &Band<'_, T>) -> 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)) + .all(|(a, b)| (a.x1, a.x2, a.tag) == (b.x1, b.x2, b.tag)) } } -impl<'a> Iterator for Bands<'a> { - type Item = Band<'a>; +impl<'a, T> Iterator for Bands<'a, T> +where + T: Tag, +{ + type Item = Band<'a, T>; fn next(&mut self) -> Option { if self.rects.is_empty() { @@ -62,7 +82,10 @@ impl<'a> Iterator for Bands<'a> { } #[inline] -pub fn extents(a: &[RectRaw]) -> RectRaw { +pub fn extents(a: &[RectRaw]) -> RectRaw +where + T: Tag, +{ let mut a = a.iter(); let mut res = match a.next() { Some(a) => *a, @@ -74,10 +97,21 @@ pub fn extents(a: &[RectRaw]) -> RectRaw { res.x2 = res.x2.max(a.x2); res.y2 = res.y2.max(a.y2); } - res + RectRaw { + x1: res.x1, + y1: res.y1, + x2: res.x2, + y2: res.y2, + tag: NoTag, + } } -fn op(a: &[RectRaw], b: &[RectRaw]) -> Container { +fn op>(a: &[RectRaw], b: &[RectRaw]) -> Container +where + T1: Tag, + T2: Tag, + T3: Tag, +{ let mut res = Container::new(); let mut prev_band_y2 = 0; @@ -100,7 +134,7 @@ fn op(a: &[RectRaw], b: &[RectRaw]) -> Container { } macro_rules! append_nonoverlapping { - ($append_opt:expr, $a:expr, $a_opt:expr, $a_bands:expr, $b:expr) => {{ + ($append_opt:expr, $map:ident, $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(); @@ -111,6 +145,7 @@ fn op(a: &[RectRaw], b: &[RectRaw]) -> Container { y1: $a.y1, x2: rect.x2, y2, + tag: O::$map(rect.tag), }); } fixup_new_band!($a.y1, y2); @@ -125,9 +160,9 @@ fn op(a: &[RectRaw], b: &[RectRaw]) -> Container { 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); + append_nonoverlapping!(O::APPEND_NON_A, map_t2_to_t1, a, a_opt, a_bands, b); } else if b.y1 < a.y1 { - append_nonoverlapping!(O::APPEND_NON_B, b, b_opt, b_bands, a); + append_nonoverlapping!(O::APPEND_NON_B, map_t3_to_t1, b, b_opt, b_bands, a); } else { let y2 = a.y2.min(b.y2); cur_band_start = res.len(); @@ -149,7 +184,7 @@ fn op(a: &[RectRaw], b: &[RectRaw]) -> Container { } macro_rules! push_trailing { - ($a_opt:expr, $a_bands:expr) => {{ + ($a_opt:expr, $a_bands:expr, $map:ident) => {{ while let Some(a) = $a_opt { cur_band_start = res.len(); res.reserve(a.rects.len()); @@ -159,6 +194,7 @@ fn op(a: &[RectRaw], b: &[RectRaw]) -> Container { y1: a.y1, x2: rect.x2, y2: a.y2, + tag: O::$map(rect.tag), }); } fixup_new_band!(a.y1, a.y2); @@ -168,25 +204,28 @@ fn op(a: &[RectRaw], b: &[RectRaw]) -> Container { } if O::APPEND_NON_A { - push_trailing!(a_opt, a_bands); + push_trailing!(a_opt, a_bands, map_t2_to_t1); } if O::APPEND_NON_B { - push_trailing!(b_opt, b_bands); + push_trailing!(b_opt, b_bands, map_t3_to_t1); } res.shrink_to_fit(); res } -fn coalesce(new: &mut Container, a: usize, b: usize, y2: i32) -> bool { +fn coalesce(new: &mut Container, a: usize, b: usize, y2: i32) -> bool +where + T: Tag, +{ 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) { + if (a.x1, a.x2, a.tag) != (b.x1, b.x2, b.tag) { return false; } } @@ -197,16 +236,25 @@ fn coalesce(new: &mut Container, a: usize, b: usize, y2: i32) -> bool { true } -trait Op { +trait Op +where + T1: Tag, + T2: Tag, + T3: Tag, +{ const APPEND_NON_A: bool; const APPEND_NON_B: bool; - fn handle_band(new: &mut Container, a: &[RectRaw], b: &[RectRaw], y1: i32, y2: i32); + fn handle_band(new: &mut Container, a: &[RectRaw], b: &[RectRaw], y1: i32, y2: i32); + + fn map_t2_to_t1(tag: T2) -> T1; + + fn map_t3_to_t1(tag: T3) -> T1; } struct Union; -impl Op for Union { +impl Op for Union { const APPEND_NON_A: bool = true; const APPEND_NON_B: bool = true; @@ -216,7 +264,13 @@ impl Op for Union { macro_rules! push { () => { - new.push(RectRaw { x1, y1, x2, y2 }); + new.push(RectRaw { + x1, + y1, + x2, + y2, + tag: NoTag, + }); }; } @@ -272,11 +326,21 @@ impl Op for Union { push!(); } + + #[inline(always)] + fn map_t2_to_t1(_tag: NoTag) -> NoTag { + NoTag + } + + #[inline(always)] + fn map_t3_to_t1(_tag: NoTag) -> NoTag { + NoTag + } } struct Subtract; -impl Op for Subtract { +impl Op for Subtract { const APPEND_NON_A: bool = true; const APPEND_NON_B: bool = false; @@ -291,6 +355,7 @@ impl Op for Subtract { y1, x2: $x2, y2, + tag: NoTag, }); }; } @@ -337,33 +402,145 @@ impl Op for Subtract { pull!(); } } + + #[inline(always)] + fn map_t2_to_t1(_tag: NoTag) -> NoTag { + NoTag + } + + #[inline(always)] + fn map_t3_to_t1(_tag: NoTag) -> NoTag { + NoTag + } +} + +struct Intersect(PhantomData); + +impl Op for Intersect +where + T: Tag, +{ + const APPEND_NON_A: bool = false; + const APPEND_NON_B: bool = false; + + fn handle_band(new: &mut Container, a: &[RectRaw], b: &[RectRaw], y1: i32, y2: i32) { + let mut x1; + let mut x2; + let mut tag; + + macro_rules! push { + ($x2:expr) => { + new.push(RectRaw { + x1, + y1, + x2: $x2, + y2, + tag, + }); + }; + } + + 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; + tag = n.tag; + } + _ => 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 { + pull!(); + } else { + x1 = x1.max(b.x1); + if x2 <= b.x2 { + push!(x2); + pull!(); + } else { + push!(b.x2); + b_opt = b_iter.next(); + } + } + } + } + + #[inline(always)] + fn map_t2_to_t1(_tag: T) -> T { + unreachable!() + } + + #[inline(always)] + fn map_t3_to_t1(_tag: NoTag) -> T { + unreachable!() + } } pub fn rects_to_bands(rects_tmp: &[RectRaw]) -> Container { + rects_to_bands_(rects_tmp) +} + +pub fn rects_to_bands_tagged(rects_tmp: &[RectRaw]) -> Container { + rects_to_bands_(rects_tmp) +} + +#[inline(always)] +fn rects_to_bands_(rects_tmp: &[RectRaw]) -> Container +where + T: Tag, +{ #[derive(Copy, Clone)] - struct W(RectRaw); - impl Eq for W {} - impl PartialEq for W { + struct W(RectRaw) + where + T: Tag; + impl Eq for W where T: Tag {} + impl PartialEq for W + where + T: Tag, + { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } - impl PartialOrd for W { + impl PartialOrd for W + where + T: Tag, + { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } - impl Ord for W { + impl Ord for W + where + T: Tag, + { fn cmp(&self, other: &Self) -> Ordering { self.0 .y1 .cmp(&other.0.y1) .then_with(|| self.0.x1.cmp(&other.0.x1)) + .then_with(|| self.0.tag.cmp(&other.0.tag)) .reverse() } } - impl Deref for W { - type Target = RectRaw; + impl Deref for W + where + T: Tag, + { + type Target = RectRaw; fn deref(&self) -> &Self::Target { &self.0 } @@ -411,17 +588,60 @@ pub fn rects_to_bands(rects_tmp: &[RectRaw]) -> Container { check_rect!(rect); let mut x1 = rect.x1; let mut x2 = rect.x2; + let mut tag: T = rect.tag; while let Some(mut rect) = rects.peek().copied() { check_rect!(rect); - if rect.x1 > x2 { - res.push(RectRaw { x1, x2, y1, y2 }); + if rect.x1 > x2 || (rect.tag != tag && rect.x1 == x2) { + res.push(RectRaw { + x1, + x2, + y1, + y2, + tag: tag.constrain(), + }); x1 = rect.x1; x2 = rect.x2; + tag = rect.tag; } else { - x2 = x2.max(rect.x2); + if rect.tag == tag { + x2 = x2.max(rect.x2); + } else if rect.tag > tag { + if rect.x2 > x2 { + rect.0.x1 = x2; + rect.0.y1 = y1; + rect.0.y2 = y2; + rects.push(rect); + } + } else { + if x2 > rect.x2 { + rects.push(W(RectRaw { + x1: rect.x2, + y1, + x2, + y2, + tag, + })); + } + res.push(RectRaw { + x1, + y1, + x2: rect.x1, + y2, + tag: tag.constrain(), + }); + x1 = rect.x1; + x2 = rect.x2; + tag = rect.tag; + } } } - res.push(RectRaw { x1, x2, y1, y2 }); + res.push(RectRaw { + x1, + x2, + y1, + y2, + tag: tag.constrain(), + }); } break; } diff --git a/src/rect.rs b/src/rect.rs index d9a22cb5..62103392 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -5,20 +5,26 @@ mod tests; pub use region::{DamageQueue, RegionBuilder}; use { - jay_algorithms::rect::RectRaw, + jay_algorithms::rect::{NoTag, RectRaw, Tag}, smallvec::SmallVec, std::fmt::{Debug, Formatter}, }; #[derive(Copy, Clone, Eq, PartialEq, Default)] #[repr(transparent)] -pub struct Rect { - raw: RectRaw, +pub struct Rect +where + T: Tag, +{ + raw: RectRaw, } #[derive(Clone, Eq, PartialEq, Debug, Default)] -pub struct Region { - rects: SmallVec<[RectRaw; 1]>, +pub struct Region +where + T: Tag, +{ + rects: SmallVec<[RectRaw; 1]>, extents: Rect, } @@ -50,6 +56,23 @@ impl RectOverflow { } } +impl Rect +where + T: Tag, +{ + pub fn untag(&self) -> Rect { + Rect { + raw: RectRaw { + x1: self.raw.x1, + y1: self.raw.y1, + x2: self.raw.x2, + y2: self.raw.y2, + tag: NoTag, + }, + } + } +} + impl Rect { pub fn new_empty(x: i32, y: i32) -> Self { Self { @@ -58,6 +81,7 @@ impl Rect { y1: y, x2: x, y2: y, + tag: NoTag, }, } } @@ -67,7 +91,13 @@ impl Rect { return None; } Some(Self { - raw: RectRaw { x1, y1, x2, y2 }, + raw: RectRaw { + x1, + y1, + x2, + y2, + tag: NoTag, + }, }) } @@ -76,10 +106,16 @@ impl Rect { Self::new(x1, y1, x2, y2).unwrap() } - #[expect(dead_code)] + #[cfg_attr(not(test), expect(dead_code))] fn new_unchecked_danger(x1: i32, y1: i32, x2: i32, y2: i32) -> Self { Self { - raw: RectRaw { x1, y1, x2, y2 }, + raw: RectRaw { + x1, + y1, + x2, + y2, + tag: NoTag, + }, } } @@ -102,6 +138,58 @@ impl Rect { y1: self.raw.y1.min(other.raw.y1), x2: self.raw.x2.max(other.raw.x2), y2: self.raw.y2.max(other.raw.y2), + tag: NoTag, + }, + } + } + + pub fn intersect(&self, other: Self) -> Self { + let x1 = self.raw.x1.max(other.raw.x1); + let y1 = self.raw.y1.max(other.raw.y1); + let x2 = self.raw.x2.min(other.raw.x2).max(x1); + let y2 = self.raw.y2.min(other.raw.y2).max(y1); + Self { + raw: RectRaw { + x1, + y1, + x2, + y2, + tag: NoTag, + }, + } + } + + pub fn with_size(&self, width: i32, height: i32) -> Option { + Self::new_sized(self.raw.x1, self.raw.y1, width, height) + } + + #[expect(dead_code)] + pub fn with_tag(&self, tag: u32) -> Rect { + Rect { + raw: RectRaw { + x1: self.raw.x1, + y1: self.raw.y1, + x2: self.raw.x2, + y2: self.raw.y2, + tag, + }, + } + } +} + +impl Rect +where + T: Tag, +{ + #[cfg_attr(not(test), expect(dead_code))] + fn new_unchecked_danger_tagged(x1: i32, y1: i32, x2: i32, y2: i32, tag: T) -> Self { + Self { + raw: RectRaw { + x1, + y1, + x2, + y2, + tag, }, } } @@ -113,16 +201,6 @@ impl Rect { && other.raw.y1 < self.raw.y2 } - pub fn intersect(&self, other: Self) -> Self { - let x1 = self.raw.x1.max(other.raw.x1); - let y1 = self.raw.y1.max(other.raw.y1); - let x2 = self.raw.x2.min(other.raw.x2).max(x1); - let y2 = self.raw.y2.min(other.raw.y2).max(y1); - Self { - raw: RectRaw { x1, y1, x2, y2 }, - } - } - pub fn contains(&self, x: i32, y: i32) -> bool { self.raw.x1 <= x && self.raw.y1 <= y && self.raw.x2 > x && self.raw.y2 > y } @@ -144,14 +222,20 @@ impl Rect { } #[expect(dead_code)] - pub fn contains_rect(&self, rect: &Self) -> bool { + pub fn contains_rect(&self, rect: &Rect) -> bool + where + U: Tag, + { self.raw.x1 <= rect.raw.x1 && self.raw.y1 <= rect.raw.x1 && rect.raw.x2 <= self.raw.x2 && rect.raw.y2 <= self.raw.y2 } - pub fn get_overflow(&self, child: &Self) -> RectOverflow { + pub fn get_overflow(&self, child: &Rect) -> RectOverflow + where + U: Tag, + { RectOverflow { left: self.raw.x1 - child.raw.x1, right: child.raw.x2 - self.raw.x2, @@ -177,6 +261,7 @@ impl Rect { y1: 0, x2: self.raw.x2 - self.raw.x1, y2: self.raw.y2 - self.raw.y1, + tag: self.raw.tag, }, } } @@ -188,6 +273,7 @@ impl Rect { y1: self.raw.y1.saturating_add(dy), x2: self.raw.x2.saturating_add(dx), y2: self.raw.y2.saturating_add(dy), + tag: self.raw.tag, }, } } @@ -199,14 +285,11 @@ impl Rect { y1, x2: x1 + self.raw.x2 - self.raw.x1, y2: y1 + self.raw.y2 - self.raw.y1, + tag: self.raw.tag, }, } } - pub fn with_size(&self, width: i32, height: i32) -> Option { - Self::new_sized(self.raw.x1, self.raw.y1, width, height) - } - pub fn translate(&self, x: i32, y: i32) -> (i32, i32) { (x.wrapping_sub(self.raw.x1), y.wrapping_sub(self.raw.y1)) } @@ -253,4 +336,9 @@ impl Rect { self.raw.y1 + self.height() / 2, ) } + + #[expect(dead_code)] + pub fn tag(&self) -> T { + self.raw.tag + } } diff --git a/src/rect/region.rs b/src/rect/region.rs index 980dfc7a..2218a51d 100644 --- a/src/rect/region.rs +++ b/src/rect/region.rs @@ -7,8 +7,11 @@ use { }, }, jay_algorithms::rect::{ - RectRaw, - region::{extents, rects_to_bands, subtract, union}, + RectRaw, Tag, + region::{ + extents, intersect, intersect_tagged, rects_to_bands, rects_to_bands_tagged, subtract, + union, + }, }, smallvec::SmallVec, std::{ @@ -29,19 +32,6 @@ thread_local! { } impl Region { - pub fn new(rect: Rect) -> Rc { - Rc::new(Self::new2(rect)) - } - - pub fn new2(rect: Rect) -> Self { - let mut rects = SmallVec::new(); - rects.push(rect.raw); - Self { - rects, - extents: rect, - } - } - pub fn empty() -> Rc { EMPTY.with(|e| e.clone()) } @@ -96,13 +86,82 @@ impl Region { }) } + #[cfg_attr(not(test), expect(dead_code))] + pub fn intersect(&self, other: &Region) -> Self { + if self.is_empty() || other.is_empty() { + return Self::default(); + } + let rects = intersect(&self.rects, &other.rects); + Self { + extents: Rect { + raw: extents(&rects), + }, + rects, + } + } +} + +impl Region { + #[cfg_attr(not(test), expect(dead_code))] + pub fn from_rects_tagged(rects: &[Rect]) -> Self { + if rects.is_empty() { + return Self::default(); + } + if rects.len() == 1 { + let mut rect = rects[0]; + rect.raw.tag = rect.raw.tag.constrain(); + return Self::new2(rect); + } + let rects = rects_to_bands_tagged(unsafe { + mem::transmute::<&[Rect], &[RectRaw]>(rects) + }); + Self { + extents: Rect { + raw: extents(&rects), + }, + rects, + } + } + + #[cfg_attr(not(test), expect(dead_code))] + pub fn intersect_tagged(&self, other: &Region) -> Self { + if self.is_empty() || other.is_empty() { + return Self::default(); + } + let rects = intersect_tagged(&self.rects, &other.rects); + Self { + extents: Rect { + raw: extents(&rects), + }, + rects, + } + } +} + +impl Region +where + T: Tag, +{ + pub fn new(rect: Rect) -> Rc { + Rc::new(Self::new2(rect)) + } + + pub fn new2(rect: Rect) -> Self { + let mut rects = SmallVec::new(); + rects.push(rect.raw); + Self { + rects, + extents: rect.untag(), + } + } + #[cfg_attr(not(feature = "it"), expect(dead_code))] pub fn extents(&self) -> Rect { self.extents } - pub fn rects(&self) -> &[Rect] { - unsafe { mem::transmute::<&[RectRaw], &[Rect]>(&self.rects[..]) } + pub fn rects(&self) -> &[Rect] { + unsafe { mem::transmute::<&[RectRaw], &[Rect]>(&self.rects[..]) } } pub fn contains(&self, x: i32, y: i32) -> bool { @@ -118,11 +177,14 @@ impl Region { } } -impl Deref for Region { - type Target = [Rect]; +impl Deref for Region +where + T: Tag, +{ + type Target = [Rect]; fn deref(&self) -> &Self::Target { - unsafe { mem::transmute::<&[RectRaw], _>(&self.rects) } + unsafe { mem::transmute::<&[RectRaw], _>(&self.rects) } } } diff --git a/src/rect/tests.rs b/src/rect/tests.rs index 5ded4804..0725f472 100644 --- a/src/rect/tests.rs +++ b/src/rect/tests.rs @@ -1,6 +1,6 @@ use { crate::rect::{Rect, Region}, - jay_algorithms::rect::RectRaw, + jay_algorithms::rect::{NoTag, RectRaw}, }; #[test] @@ -43,25 +43,29 @@ fn subtract1() { x1: 0, y1: 0, x2: 20, - y2: 5 + y2: 5, + tag: NoTag, }, RectRaw { x1: 0, y1: 5, x2: 5, - y2: 15 + y2: 15, + tag: NoTag, }, RectRaw { x1: 15, y1: 5, x2: 20, - y2: 15 + y2: 15, + tag: NoTag, }, RectRaw { x1: 0, y1: 15, x2: 20, - y2: 20 + y2: 20, + tag: NoTag, }, ] ); @@ -83,19 +87,22 @@ fn rects_to_bands() { x1: 0, y1: 0, x2: 30, - y2: 5 + y2: 5, + tag: NoTag, }, RectRaw { x1: 0, y1: 5, x2: 50, - y2: 10 + y2: 10, + tag: NoTag, }, RectRaw { x1: 30, y1: 10, x2: 50, - y2: 15 + y2: 15, + tag: NoTag, }, ] ); @@ -111,3 +118,572 @@ fn rects_to_bands2() { // println!("{:#?}", r.rects); assert_eq!(&r.rects[..], &[Rect::new(0, 0, 10, 20).unwrap().raw,]); } + +#[test] +fn rects_to_bands_tagged1() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 200, 200, 1), + Rect::new_unchecked_danger_tagged(50, 50, 150, 150, 0), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 200, + y2: 50, + tag: 1, + }, + RectRaw { + x1: 0, + y1: 50, + x2: 50, + y2: 150, + tag: 1, + }, + RectRaw { + x1: 50, + y1: 50, + x2: 150, + y2: 150, + tag: 0, + }, + RectRaw { + x1: 150, + y1: 50, + x2: 200, + y2: 150, + tag: 1, + }, + RectRaw { + x1: 0, + y1: 150, + x2: 200, + y2: 200, + tag: 1, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged2() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 200, 200, 1), + Rect::new_unchecked_danger_tagged(50, 50, 150, 150, 0), + Rect::new_unchecked_danger_tagged(60, 60, 140, 140, 2), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 200, + y2: 50, + tag: 1, + }, + RectRaw { + x1: 0, + y1: 50, + x2: 50, + y2: 150, + tag: 1, + }, + RectRaw { + x1: 50, + y1: 50, + x2: 150, + y2: 150, + tag: 0, + }, + RectRaw { + x1: 150, + y1: 50, + x2: 200, + y2: 150, + tag: 1, + }, + RectRaw { + x1: 0, + y1: 150, + x2: 200, + y2: 200, + tag: 1, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged3() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 200, 200, 2), + Rect::new_unchecked_danger_tagged(50, 50, 150, 150, 1), + Rect::new_unchecked_danger_tagged(60, 60, 140, 140, 0), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 200, + y2: 50, + tag: 0, + }, + RectRaw { + x1: 0, + y1: 50, + x2: 50, + y2: 60, + tag: 0, + }, + RectRaw { + x1: 50, + y1: 50, + x2: 150, + y2: 60, + tag: 1, + }, + RectRaw { + x1: 150, + y1: 50, + x2: 200, + y2: 60, + tag: 0, + }, + RectRaw { + x1: 0, + y1: 60, + x2: 50, + y2: 140, + tag: 0, + }, + RectRaw { + x1: 50, + y1: 60, + x2: 60, + y2: 140, + tag: 1, + }, + RectRaw { + x1: 60, + y1: 60, + x2: 140, + y2: 140, + tag: 0, + }, + RectRaw { + x1: 140, + y1: 60, + x2: 150, + y2: 140, + tag: 1, + }, + RectRaw { + x1: 150, + y1: 60, + x2: 200, + y2: 140, + tag: 0, + }, + RectRaw { + x1: 0, + y1: 140, + x2: 50, + y2: 150, + tag: 0, + }, + RectRaw { + x1: 50, + y1: 140, + x2: 150, + y2: 150, + tag: 1, + }, + RectRaw { + x1: 150, + y1: 140, + x2: 200, + y2: 150, + tag: 0, + }, + RectRaw { + x1: 0, + y1: 150, + x2: 200, + y2: 200, + tag: 0, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged4() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1), + Rect::new_unchecked_danger_tagged(100, 0, 200, 200, 0), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 100, + y2: 100, + tag: 1, + }, + RectRaw { + x1: 100, + y1: 0, + x2: 200, + y2: 100, + tag: 0, + }, + RectRaw { + x1: 100, + y1: 100, + x2: 200, + y2: 200, + tag: 0, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged5() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 200, 100, 1), + Rect::new_unchecked_danger_tagged(100, 0, 200, 100, 0), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 100, + y2: 100, + tag: 1, + }, + RectRaw { + x1: 100, + y1: 0, + x2: 200, + y2: 100, + tag: 0, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged6() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 200, 100, 1), + Rect::new_unchecked_danger_tagged(100, 0, 300, 100, 0), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 100, + y2: 100, + tag: 1, + }, + RectRaw { + x1: 100, + y1: 0, + x2: 300, + y2: 100, + tag: 0, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged7() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 200, 100, 0), + Rect::new_unchecked_danger_tagged(100, 0, 300, 200, 1), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 200, + y2: 100, + tag: 0, + }, + RectRaw { + x1: 200, + y1: 0, + x2: 300, + y2: 100, + tag: 1, + }, + RectRaw { + x1: 100, + y1: 100, + x2: 300, + y2: 200, + tag: 1, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged8() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1), + Rect::new_unchecked_danger_tagged(100, 0, 200, 100, 0), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 100, + y2: 100, + tag: 1, + }, + RectRaw { + x1: 100, + y1: 0, + x2: 200, + y2: 100, + tag: 0, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged9() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1), + Rect::new_unchecked_danger_tagged(100, 0, 200, 100, 1), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[RectRaw { + x1: 0, + y1: 0, + x2: 200, + y2: 100, + tag: 1, + },], + ); +} + +#[test] +fn rects_to_bands_tagged10() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1), + Rect::new_unchecked_danger_tagged(0, 100, 100, 200, 1), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[RectRaw { + x1: 0, + y1: 0, + x2: 100, + y2: 200, + tag: 1, + },], + ); +} + +#[test] +fn rects_to_bands_tagged11() { + let rects = [Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 11)]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[RectRaw { + x1: 0, + y1: 0, + x2: 100, + y2: 100, + tag: 1, + },], + ); +} + +#[test] +fn rects_to_bands_tagged12() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 11), + Rect::new_unchecked_danger_tagged(200, 0, 300, 100, 10), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 100, + y2: 100, + tag: 1, + }, + RectRaw { + x1: 200, + y1: 0, + x2: 300, + y2: 100, + tag: 0, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged13() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1), + Rect::new_unchecked_danger_tagged(0, 100, 100, 200, 0), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[ + RectRaw { + x1: 0, + y1: 0, + x2: 100, + y2: 100, + tag: 1, + }, + RectRaw { + x1: 0, + y1: 100, + x2: 100, + y2: 200, + tag: 0, + }, + ], + ); +} + +#[test] +fn rects_to_bands_tagged14() { + let rects = [ + Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1), + Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 0), + ]; + let r = Region::from_rects_tagged(&rects[..]); + assert_eq!( + &r.rects[..], + &[RectRaw { + x1: 0, + y1: 0, + x2: 100, + y2: 100, + tag: 0, + },], + ); +} + +#[test] +fn intersect1() { + let rects = [Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 0)]; + let r1 = Region::from_rects_tagged(&rects[..]); + let rects = [Rect::new_unchecked_danger(100, 100, 200, 200)]; + let r2 = Region::from_rects2(&rects[..]); + let r3 = r1.intersect_tagged(&r2); + assert_eq!(&r3.rects[..], &[],); +} + +#[test] +fn intersect2() { + let rects = [Rect::new_unchecked_danger_tagged(0, 0, 200, 200, 0)]; + let r1 = Region::from_rects_tagged(&rects[..]); + let rects = [Rect::new_unchecked_danger(50, 50, 150, 150)]; + let r2 = Region::from_rects2(&rects[..]); + let r3 = r1.intersect_tagged(&r2); + assert_eq!( + &r3.rects[..], + &[RectRaw { + x1: 50, + y1: 50, + x2: 150, + y2: 150, + tag: 0, + }], + ); +} + +#[test] +fn intersect3() { + macro_rules! t { + ($l:expr, $r:expr, $t:expr) => { + Rect::new_unchecked_danger_tagged($l, 0, $r, 1, $t) + }; + } + macro_rules! u { + ($l:expr, $r:expr) => { + Rect::new_unchecked_danger($l, 0, $r, 1) + }; + } + macro_rules! r { + ($l:expr, $r:expr, $t:expr) => { + RectRaw { + x1: $l, + y1: 0, + x2: $r, + y2: 1, + tag: $t, + } + }; + } + let rects = [ + t!(0, 100, 0), + t!(110, 130, 1), + t!(140, 160, 2), + t!(170, 180, 0), + ]; + let r1 = Region::from_rects_tagged(&rects[..]); + let rects = [ + u!(10, 20), + u!(50, 60), + u!(70, 100), + u!(120, 150), + u!(170, 180), + ]; + let r2 = Region::from_rects2(&rects[..]); + let r3 = r1.intersect_tagged(&r2); + assert_eq!( + &r3.rects[..], + &[ + r!(10, 20, 0), + r!(50, 60, 0), + r!(70, 100, 0), + r!(120, 130, 1), + r!(140, 150, 0), + r!(170, 180, 0), + ], + ); +}