1
0
Fork 0
forked from wry/wry

utils: move damage algorithms to algorithm crate

This commit is contained in:
Julian Orth 2022-06-03 21:01:20 +02:00
parent 259340938b
commit 6e244a08ab
12 changed files with 710 additions and 542 deletions

13
Cargo.lock generated
View file

@ -28,6 +28,13 @@ dependencies = [
"version_check",
]
[[package]]
name = "algorithms"
version = "0.1.0"
dependencies = [
"smallvec",
]
[[package]]
name = "anyhow"
version = "1.0.56"
@ -308,6 +315,7 @@ name = "jay"
version = "0.1.0"
dependencies = [
"ahash",
"algorithms",
"anyhow",
"backtrace",
"bincode",
@ -329,7 +337,6 @@ dependencies = [
"num-traits",
"once_cell",
"pin-project",
"qoi",
"rand",
"repc",
"smallvec",
@ -517,10 +524,6 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "qoi"
version = "0.1.0"
[[package]]
name = "quote"
version = "1.0.16"

View file

@ -5,7 +5,7 @@ edition = "2021"
build = "build/build.rs"
[workspace]
members = ["jay-config", "default-config", "qoi"]
members = ["jay-config", "default-config", "algorithms"]
[profile.release]
panic = "abort"
@ -32,7 +32,7 @@ byteorder = "1.4.3"
bincode = "2.0.0-rc.1"
jay-config = { path = "jay-config" }
default-config = { path = "default-config" }
qoi = { path = "qoi" }
algorithms = { path = "algorithms" }
pin-project = "1.0.10"
clap = { version = "3.1.6", features = ["derive", "wrap_help"] }
clap_complete = "3.1.1"
@ -49,7 +49,10 @@ bstr = { version = "0.2.17", default-features = false, features = ["std"] }
#[profile.dev.build-override]
#opt-level = 3
[profile.dev.package."qoi"]
[profile.dev.package."algorithms"]
opt-level = 3
[profile.dev.package."smallvec"]
opt-level = 3
[features]

View file

@ -1,8 +1,9 @@
[package]
name = "qoi"
name = "algorithms"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
smallvec = { version = "1.8.0", features = ["const_generics", "const_new", "union"] }

5
algorithms/src/lib.rs Normal file
View file

@ -0,0 +1,5 @@
#![feature(generic_associated_types)]
pub mod qoi;
pub mod rect;
mod windows;

35
algorithms/src/rect.rs Normal file
View file

@ -0,0 +1,35 @@
pub mod region;
use {
smallvec::SmallVec,
std::fmt::{Debug, Formatter},
};
#[derive(Copy, Clone, Eq, PartialEq, Default)]
pub struct RectRaw {
pub x1: i32,
pub y1: i32,
pub x2: i32,
pub y2: i32,
}
impl Debug for RectRaw {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Rect")
.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()
}
}
impl RectRaw {
fn is_empty(&self) -> bool {
self.x1 == self.x2 || self.y1 == self.y2
}
}
type Container = SmallVec<[RectRaw; 1]>;

View file

@ -0,0 +1,466 @@
use {
crate::{
rect::{Container, RectRaw},
windows::WindowsExt,
},
std::{cmp::Ordering, collections::BinaryHeap, mem, ops::Deref},
};
pub fn union(left: &Container, right: &Container) -> Container {
op::<Union>(left, &right)
}
pub fn subtract(left: &Container, right: &Container) -> Container {
op::<Subtract>(left, right)
}
struct Bands<'a> {
rects: &'a [RectRaw],
}
#[derive(Copy, Clone)]
struct Band<'a> {
rects: &'a [RectRaw],
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<Self::Item> {
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,
})
}
}
#[inline]
pub fn extents(a: &[RectRaw]) -> RectRaw {
let mut a = a.iter();
let mut res = match a.next() {
Some(a) => *a,
_ => return RectRaw::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<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> 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(RectRaw {
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(RectRaw {
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: &[RectRaw], b: &[RectRaw], 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: &[RectRaw], mut b: &[RectRaw], y1: i32, y2: i32) {
let mut x1;
let mut x2;
macro_rules! push {
() => {
new.push(RectRaw { 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: &[RectRaw], b: &[RectRaw], y1: i32, y2: i32) {
let mut x1;
let mut x2;
macro_rules! push {
($x2:expr) => {
new.push(RectRaw {
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!();
}
}
}
pub fn rects_to_bands(rects_tmp: &[RectRaw]) -> Container {
#[derive(Copy, Clone)]
struct W(RectRaw);
impl Eq for W {}
impl PartialEq<Self> for W {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl PartialOrd<Self> for W {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
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 = RectRaw;
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(RectRaw { x1, x2, y1, y2 });
x1 = rect.x1;
x2 = rect.x2;
} else {
x2 = x2.max(rect.x2);
}
}
res.push(RectRaw { 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
}

35
algorithms/src/windows.rs Normal file
View file

@ -0,0 +1,35 @@
pub trait WindowsExt<T> {
type Windows<'a, const N: usize>: Iterator<Item = &'a [T; N]>
where
Self: 'a,
T: 'a;
fn array_windows_ext<'a, const N: usize>(&'a self) -> Self::Windows<'a, N>;
}
impl<T> WindowsExt<T> for [T] {
type Windows<'a, const N: usize> = WindowsIter<'a, T, N> where T: 'a;
fn array_windows_ext<'a, const N: usize>(&'a self) -> Self::Windows<'a, N> {
WindowsIter { slice: self }
}
}
pub struct WindowsIter<'a, T, const N: usize> {
slice: &'a [T],
}
impl<'a, T, const N: usize> Iterator for WindowsIter<'a, T, N> {
type Item = &'a [T; N];
fn next(&mut self) -> Option<Self::Item> {
if self.slice.len() < N {
return None;
}
let res = unsafe { &*self.slice.as_ptr().cast::<[T; N]>() };
if N > 0 {
self.slice = &self.slice[1..];
}
Some(res)
}
}

View file

@ -15,8 +15,8 @@ use {
jay_screenshot::{Dmabuf, Error},
},
},
algorithms::qoi::xrgb8888_encode_qoi,
chrono::Local,
qoi::xrgb8888_encode_qoi,
std::rc::Rc,
};

View file

@ -5,36 +5,26 @@ mod tests;
pub use region::RegionBuilder;
use {
algorithms::rect::RectRaw,
smallvec::SmallVec,
std::fmt::{Debug, Formatter},
};
#[derive(Copy, Clone, Eq, PartialEq, Default)]
#[repr(transparent)]
pub struct Rect {
x1: i32,
y1: i32,
x2: i32,
y2: i32,
raw: RectRaw,
}
type Container = SmallVec<[Rect; 1]>;
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Region {
rects: Container,
rects: SmallVec<[RectRaw; 1]>,
extents: Rect,
}
impl Debug for Rect {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Rect")
.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()
Debug::fmt(&self.raw, f)
}
}
@ -64,10 +54,12 @@ impl Rect {
#[allow(dead_code)]
pub fn new_empty(x: i32, y: i32) -> Self {
Self {
x1: x,
y1: y,
x2: x,
y2: y,
raw: RectRaw {
x1: x,
y1: y,
x2: x,
y2: y,
},
}
}
@ -75,12 +67,16 @@ impl Rect {
if x2 < x1 || y2 < y1 {
return None;
}
Some(Self { x1, y1, x2, y2 })
Some(Self {
raw: RectRaw { x1, y1, x2, y2 },
})
}
#[allow(dead_code)]
fn new_unchecked(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
Self { x1, y1, x2, y2 }
Self {
raw: RectRaw { x1, y1, x2, y2 },
}
}
pub fn new_sized(x1: i32, y1: i32, width: i32, height: i32) -> Option<Self> {
@ -92,113 +88,129 @@ impl Rect {
pub fn union(&self, other: Self) -> Self {
Self {
x1: self.x1.min(other.x1),
y1: self.y1.min(other.y1),
x2: self.x2.max(other.x2),
y2: self.y2.max(other.y2),
raw: RectRaw {
x1: self.raw.x1.min(other.raw.x1),
y1: self.raw.y1.min(other.raw.y1),
x2: self.raw.x2.max(other.raw.x2),
y2: self.raw.y2.max(other.raw.y2),
},
}
}
pub fn intersects(&self, other: &Self) -> bool {
self.x1 < other.x2 && other.x1 < self.x2 && self.y1 < other.y2 && other.y1 < self.y2
self.raw.x1 < other.raw.x2
&& other.raw.x1 < self.raw.x2
&& self.raw.y1 < other.raw.y2
&& other.raw.y1 < self.raw.y2
}
pub fn intersect(&self, other: Self) -> Self {
let x1 = self.x1.max(other.x1);
let y1 = self.y1.max(other.y1);
let x2 = self.x2.min(other.x2).max(x1);
let y2 = self.y2.min(other.y2).max(y1);
Self { x1, y1, x2, y2 }
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.x1 <= x && self.y1 <= y && self.x2 > x && self.y2 > y
self.raw.x1 <= x && self.raw.y1 <= y && self.raw.x2 > x && self.raw.y2 > y
}
#[allow(dead_code)]
pub fn contains_rect(&self, rect: &Self) -> bool {
self.x1 <= rect.x1 && self.y1 <= rect.x1 && rect.x2 <= self.x2 && rect.y2 <= self.y2
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 {
RectOverflow {
left: self.x1 - child.x1,
right: child.x2 - self.x2,
top: self.y1 - child.y1,
bottom: child.y2 - self.y2,
left: self.raw.x1 - child.raw.x1,
right: child.raw.x2 - self.raw.x2,
top: self.raw.y1 - child.raw.y1,
bottom: child.raw.y2 - self.raw.y2,
}
}
pub fn is_empty(&self) -> bool {
self.x1 == self.x2 || self.y1 == self.y2
self.raw.x1 == self.raw.x2 || self.raw.y1 == self.raw.y2
}
#[allow(dead_code)]
pub fn to_origin(&self) -> Self {
Self {
x1: 0,
y1: 0,
x2: self.x2 - self.x1,
y2: self.y2 - self.y1,
raw: RectRaw {
x1: 0,
y1: 0,
x2: self.raw.x2 - self.raw.x1,
y2: self.raw.y2 - self.raw.y1,
},
}
}
pub fn move_(&self, dx: i32, dy: i32) -> Self {
Self {
x1: self.x1.saturating_add(dx),
y1: self.y1.saturating_add(dy),
x2: self.x2.saturating_add(dx),
y2: self.y2.saturating_add(dy),
raw: RectRaw {
x1: self.raw.x1.saturating_add(dx),
y1: self.raw.y1.saturating_add(dy),
x2: self.raw.x2.saturating_add(dx),
y2: self.raw.y2.saturating_add(dy),
},
}
}
pub fn at_point(&self, x1: i32, y1: i32) -> Self {
Self {
x1,
y1,
x2: x1 + self.x2 - self.x1,
y2: y1 + self.y2 - self.y1,
raw: RectRaw {
x1,
y1,
x2: x1 + self.raw.x2 - self.raw.x1,
y2: y1 + self.raw.y2 - self.raw.y1,
},
}
}
pub fn with_size(&self, width: i32, height: i32) -> Option<Self> {
Self::new_sized(self.x1, self.y1, width, height)
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.x1), y.wrapping_sub(self.y1))
(x.wrapping_sub(self.raw.x1), y.wrapping_sub(self.raw.y1))
}
pub fn translate_inv(&self, x: i32, y: i32) -> (i32, i32) {
(x.wrapping_add(self.x1), y.wrapping_add(self.y1))
(x.wrapping_add(self.raw.x1), y.wrapping_add(self.raw.y1))
}
pub fn x1(&self) -> i32 {
self.x1
self.raw.x1
}
pub fn x2(&self) -> i32 {
self.x2
self.raw.x2
}
pub fn y1(&self) -> i32 {
self.y1
self.raw.y1
}
pub fn y2(&self) -> i32 {
self.y2
self.raw.y2
}
pub fn width(&self) -> i32 {
self.x2 - self.x1
self.raw.x2 - self.raw.x1
}
pub fn height(&self) -> i32 {
self.y2 - self.y1
self.raw.y2 - self.raw.y1
}
pub fn position(&self) -> (i32, i32) {
(self.x1, self.y1)
(self.raw.x1, self.raw.y1)
}
pub fn size(&self) -> (i32, i32) {

View file

@ -1,11 +1,12 @@
use {
crate::{
rect::{Container, Rect, Region},
utils::windows::WindowsExt,
crate::rect::{Rect, Region},
algorithms::rect::{
region::{extents, rects_to_bands, subtract, union},
RectRaw,
},
once_cell::unsync::Lazy,
smallvec::SmallVec,
std::{cmp::Ordering, collections::BinaryHeap, mem, ops::Deref, rc::Rc},
std::{mem, ops::Deref, rc::Rc},
};
#[thread_local]
@ -19,7 +20,7 @@ static EMPTY: Lazy<Rc<Region>> = Lazy::new(|| {
impl Region {
pub fn new(rect: Rect) -> Rc<Self> {
let mut rects = SmallVec::new();
rects.push(rect);
rects.push(rect.raw);
Rc::new(Self {
rects,
extents: rect,
@ -37,9 +38,11 @@ impl Region {
if rects.len() == 1 {
return Self::new(rects[0]);
}
let rects = rects_to_bands(rects);
let rects = rects_to_bands(unsafe { mem::transmute(rects) });
Rc::new(Self {
extents: extents(&rects),
extents: Rect {
raw: extents(&rects),
},
rects,
})
}
@ -51,7 +54,7 @@ impl Region {
if other.extents.is_empty() {
return self.clone();
}
let rects = op::<Union>(&self.rects, &other.rects);
let rects = union(&self.rects, &other.rects);
Rc::new(Self {
rects,
extents: self.extents.union(other.extents),
@ -62,9 +65,11 @@ impl Region {
if self.extents.is_empty() || other.extents.is_empty() {
return self.clone();
}
let rects = op::<Subtract>(&self.rects, &other.rects);
let rects = subtract(&self.rects, &other.rects);
Rc::new(Self {
extents: extents(&rects),
extents: Rect {
raw: extents(&rects),
},
rects,
})
}
@ -79,460 +84,10 @@ impl Deref for Region {
type Target = [Rect];
fn deref(&self) -> &Self::Target {
&self.rects
unsafe { mem::transmute::<&[RectRaw], _>(&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<Self::Item> {
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<O: 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<Self> for W {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl PartialOrd<Self> for W {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
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, Debug)]
enum BuilderOp {
Add,

View file

@ -1,4 +1,7 @@
use crate::rect::{Rect, Region};
use {
crate::rect::{Rect, Region},
algorithms::rect::RectRaw,
};
#[test]
fn union1() {
@ -10,10 +13,10 @@ fn union1() {
assert_eq!(
&r3.rects[..],
&[
Rect::new(0, 0, 10, 5).unwrap(),
Rect::new(0, 5, 15, 10).unwrap(),
Rect::new(5, 10, 20, 15).unwrap(),
Rect::new(10, 15, 20, 20).unwrap(),
Rect::new(0, 0, 10, 5).unwrap().raw,
Rect::new(0, 5, 15, 10).unwrap().raw,
Rect::new(5, 10, 20, 15).unwrap().raw,
Rect::new(10, 15, 20, 20).unwrap().raw,
]
);
}
@ -24,7 +27,7 @@ fn union2() {
let r2 = Region::new(Rect::new(0, 10, 10, 20).unwrap());
let r3 = r1.union(&r2);
assert_eq!(r3.extents, Rect::new(0, 0, 10, 20).unwrap());
assert_eq!(&r3.rects[..], &[Rect::new(0, 0, 10, 20).unwrap(),]);
assert_eq!(&r3.rects[..], &[Rect::new(0, 0, 10, 20).unwrap().raw,]);
}
#[test]
@ -33,7 +36,35 @@ fn subtract1() {
let r2 = Region::new(Rect::new(5, 5, 15, 15).unwrap());
let r3 = r1.subtract(&r2);
assert_eq!(r3.extents, Rect::new(0, 0, 20, 20).unwrap());
assert_eq!(&r3.rects[..], &[Rect::new(0, 0, 10, 20).unwrap(),]);
assert_eq!(
&r3.rects[..],
&[
RectRaw {
x1: 0,
y1: 0,
x2: 20,
y2: 5
},
RectRaw {
x1: 0,
y1: 5,
x2: 5,
y2: 15
},
RectRaw {
x1: 15,
y1: 5,
x2: 20,
y2: 15
},
RectRaw {
x1: 0,
y1: 15,
x2: 20,
y2: 20
},
]
);
}
#[test]
@ -44,8 +75,30 @@ fn rects_to_bands() {
Rect::new_unchecked(30, 5, 50, 15),
];
let r = Region::from_rects(&rects[..]);
println!("{:#?}", r.rects);
assert_eq!(&r.rects[..], &[Rect::new(0, 0, 10, 20).unwrap(),]);
// println!("{:#?}", r.rects);
assert_eq!(
&r.rects[..],
&[
RectRaw {
x1: 0,
y1: 0,
x2: 30,
y2: 5
},
RectRaw {
x1: 0,
y1: 5,
x2: 50,
y2: 10
},
RectRaw {
x1: 30,
y1: 10,
x2: 50,
y2: 15
},
]
);
}
#[test]
@ -55,6 +108,6 @@ fn rects_to_bands2() {
Rect::new_unchecked(0, 10, 10, 20),
];
let r = Region::from_rects(&rects[..]);
println!("{:#?}", r.rects);
assert_eq!(&r.rects[..], &[Rect::new(0, 0, 10, 20).unwrap(),]);
// println!("{:#?}", r.rects);
assert_eq!(&r.rects[..], &[Rect::new(0, 0, 10, 20).unwrap().raw,]);
}