refactor: split cargo workspace
This commit is contained in:
parent
5db14936e7
commit
1c21bd1259
695 changed files with 32023 additions and 44964 deletions
10
crates/algorithms/Cargo.toml
Normal file
10
crates/algorithms/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "jay-algorithms"
|
||||
version = "0.4.0"
|
||||
edition = "2024"
|
||||
license = "GPL-3.0-only"
|
||||
description = "Internal dependency of the Jay compositor"
|
||||
repository = "https://github.com/mahkoh/jay"
|
||||
|
||||
[dependencies]
|
||||
smallvec = { version = "1.8.0", features = ["const_generics", "const_new", "union"] }
|
||||
10
crates/algorithms/src/lib.rs
Normal file
10
crates/algorithms/src/lib.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#![allow(
|
||||
clippy::mem_replace_with_default,
|
||||
clippy::comparison_chain,
|
||||
clippy::collapsible_else_if,
|
||||
clippy::needless_lifetimes
|
||||
)]
|
||||
|
||||
pub mod qoi;
|
||||
pub mod rect;
|
||||
mod windows;
|
||||
91
crates/algorithms/src/qoi.rs
Normal file
91
crates/algorithms/src/qoi.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
pub fn xrgb8888_encode_qoi(bytes: &[u8], width: u32, height: u32, stride: u32) -> Vec<u8> {
|
||||
const OP_RGB: u8 = 0b1111_1110;
|
||||
const OP_INDEX: u8 = 0b0000_0000;
|
||||
const OP_DIFF: u8 = 0b0100_0000;
|
||||
const OP_LUMA: u8 = 0b1000_0000;
|
||||
const OP_RUN: u8 = 0b1100_0000;
|
||||
|
||||
let mut res = vec![];
|
||||
let width_bytes_be = width.to_be_bytes();
|
||||
let height_bytes_be = height.to_be_bytes();
|
||||
let header = [
|
||||
b'q',
|
||||
b'o',
|
||||
b'i',
|
||||
b'f',
|
||||
width_bytes_be[0],
|
||||
width_bytes_be[1],
|
||||
width_bytes_be[2],
|
||||
width_bytes_be[3],
|
||||
height_bytes_be[0],
|
||||
height_bytes_be[1],
|
||||
height_bytes_be[2],
|
||||
height_bytes_be[3],
|
||||
3,
|
||||
0,
|
||||
];
|
||||
res.extend_from_slice(&header);
|
||||
let mut prev_pixel = [0, 0, 0, 0xff];
|
||||
let mut array = [[0; 4]; 64];
|
||||
let mut run_length = 0;
|
||||
for line in bytes.chunks_exact(stride as _) {
|
||||
for &pixel in array_chunks::<_, 4>(&line[..(width * 4) as _]) {
|
||||
let pixel = [pixel[2], pixel[1], pixel[0], 0xff];
|
||||
if pixel == prev_pixel {
|
||||
run_length += 1;
|
||||
if run_length == 62 {
|
||||
res.push(OP_RUN | (run_length - 1));
|
||||
run_length = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if run_length > 0 {
|
||||
res.push(OP_RUN | (run_length - 1));
|
||||
run_length = 0;
|
||||
}
|
||||
let prev = prev_pixel;
|
||||
prev_pixel = pixel;
|
||||
let index = {
|
||||
let sum = 0u8
|
||||
.wrapping_add(pixel[0].wrapping_mul(3))
|
||||
.wrapping_add(pixel[1].wrapping_mul(5))
|
||||
.wrapping_add(pixel[2].wrapping_mul(7))
|
||||
.wrapping_add(255u8.wrapping_mul(11));
|
||||
sum & 63
|
||||
};
|
||||
if array[index as usize] == pixel {
|
||||
res.push(OP_INDEX | index);
|
||||
continue;
|
||||
}
|
||||
array[index as usize] = pixel;
|
||||
let dr = pixel[0].wrapping_sub(prev[0]);
|
||||
let dg = pixel[1].wrapping_sub(prev[1]);
|
||||
let db = pixel[2].wrapping_sub(prev[2]);
|
||||
let dr_2 = dr.wrapping_add(2);
|
||||
let dg_2 = dg.wrapping_add(2);
|
||||
let db_2 = db.wrapping_add(2);
|
||||
if dr_2 | dg_2 | db_2 | 3 == 3 {
|
||||
res.push(OP_DIFF | (dr_2 << 4) | (dg_2 << 2) | db_2);
|
||||
continue;
|
||||
}
|
||||
let dr_dg_8 = dr.wrapping_sub(dg).wrapping_add(8);
|
||||
let db_dg_8 = db.wrapping_sub(dg).wrapping_add(8);
|
||||
let dg_32 = dg.wrapping_add(32);
|
||||
if (dg_32 | 63 == 63) && (dr_dg_8 | db_dg_8 | 15 == 15) {
|
||||
res.extend_from_slice(&[OP_LUMA | dg_32, (dr_dg_8 << 4) | db_dg_8]);
|
||||
continue;
|
||||
}
|
||||
res.extend_from_slice(&[OP_RGB, pixel[0], pixel[1], pixel[2]]);
|
||||
}
|
||||
}
|
||||
if run_length > 0 {
|
||||
res.push(OP_RUN | (run_length - 1));
|
||||
}
|
||||
res.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
res
|
||||
}
|
||||
|
||||
fn array_chunks<T, const N: usize>(slice: &[T]) -> &[[T; N]] {
|
||||
let len = slice.len() / N;
|
||||
unsafe { std::slice::from_raw_parts(slice.as_ptr() as _, len) }
|
||||
}
|
||||
76
crates/algorithms/src/rect.rs
Normal file
76
crates/algorithms/src/rect.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
pub mod region;
|
||||
|
||||
use {
|
||||
smallvec::SmallVec,
|
||||
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<T = NoTag>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
pub x1: i32,
|
||||
pub y1: i32,
|
||||
pub x2: i32,
|
||||
pub y2: i32,
|
||||
pub tag: T,
|
||||
}
|
||||
|
||||
impl<T> Debug for RectRaw<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
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));
|
||||
if T::IS_SIGNIFICANT {
|
||||
debug.field("tag", &self.tag);
|
||||
}
|
||||
debug.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RectRaw<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
fn is_empty(&self) -> bool {
|
||||
self.x1 == self.x2 || self.y1 == self.y2
|
||||
}
|
||||
}
|
||||
|
||||
type Container<T = NoTag> = SmallVec<[RectRaw<T>; 1]>;
|
||||
687
crates/algorithms/src/rect/region.rs
Normal file
687
crates/algorithms/src/rect/region.rs
Normal file
|
|
@ -0,0 +1,687 @@
|
|||
use {
|
||||
crate::{
|
||||
rect::{Container, NoTag, RectRaw, Tag},
|
||||
windows::WindowsExt,
|
||||
},
|
||||
std::{cmp::Ordering, collections::BinaryHeap, marker::PhantomData, 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)
|
||||
}
|
||||
|
||||
pub fn intersect(left: &Container, right: &Container) -> Container {
|
||||
op::<_, _, _, Intersect<NoTag>>(left, right)
|
||||
}
|
||||
|
||||
pub fn intersect_tagged(left: &Container<u32>, right: &Container) -> Container<u32> {
|
||||
op::<_, _, _, Intersect<u32>>(left, right)
|
||||
}
|
||||
|
||||
struct Bands<'a, T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
rects: &'a [RectRaw<T>],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Band<'a, T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
rects: &'a [RectRaw<T>],
|
||||
y1: i32,
|
||||
y2: i32,
|
||||
}
|
||||
|
||||
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, a.tag) == (b.x1, b.x2, b.tag))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Bands<'a, T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
type Item = Band<'a, T>;
|
||||
|
||||
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<T>(a: &[RectRaw<T>]) -> RectRaw
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
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);
|
||||
}
|
||||
RectRaw {
|
||||
x1: res.x1,
|
||||
y1: res.y1,
|
||||
x2: res.x2,
|
||||
y2: res.y2,
|
||||
tag: NoTag,
|
||||
}
|
||||
}
|
||||
|
||||
fn op<T1, T2, T3, O: Op<T1, T2, T3>>(a: &[RectRaw<T2>], b: &[RectRaw<T3>]) -> Container<T1>
|
||||
where
|
||||
T1: Tag,
|
||||
T2: Tag,
|
||||
T3: Tag,
|
||||
{
|
||||
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, $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();
|
||||
res.reserve($a.rects.len());
|
||||
for rect in $a.rects {
|
||||
res.push(RectRaw {
|
||||
x1: rect.x1,
|
||||
y1: $a.y1,
|
||||
x2: rect.x2,
|
||||
y2,
|
||||
tag: O::$map(rect.tag),
|
||||
});
|
||||
}
|
||||
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, map_t2_to_t1, a, a_opt, a_bands, b);
|
||||
} else if b.y1 < a.y1 {
|
||||
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();
|
||||
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, $map:ident) => {{
|
||||
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,
|
||||
tag: O::$map(rect.tag),
|
||||
});
|
||||
}
|
||||
fixup_new_band!(a.y1, a.y2);
|
||||
$a_opt = $a_bands.next();
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
if O::APPEND_NON_A {
|
||||
push_trailing!(a_opt, a_bands, map_t2_to_t1);
|
||||
}
|
||||
|
||||
if O::APPEND_NON_B {
|
||||
push_trailing!(b_opt, b_bands, map_t3_to_t1);
|
||||
}
|
||||
|
||||
res.shrink_to_fit();
|
||||
res
|
||||
}
|
||||
|
||||
fn coalesce<T>(new: &mut Container<T>, 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, a.tag) != (b.x1, b.x2, b.tag) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for rect in &mut new[a..b] {
|
||||
rect.y2 = y2;
|
||||
}
|
||||
new.truncate(b);
|
||||
true
|
||||
}
|
||||
|
||||
trait Op<T1, T2, T3>
|
||||
where
|
||||
T1: Tag,
|
||||
T2: Tag,
|
||||
T3: Tag,
|
||||
{
|
||||
const APPEND_NON_A: bool;
|
||||
const APPEND_NON_B: bool;
|
||||
|
||||
fn handle_band(new: &mut Container<T1>, a: &[RectRaw<T2>], b: &[RectRaw<T3>], y1: i32, y2: i32);
|
||||
|
||||
fn map_t2_to_t1(tag: T2) -> T1;
|
||||
|
||||
fn map_t3_to_t1(tag: T3) -> T1;
|
||||
}
|
||||
|
||||
struct Union;
|
||||
|
||||
impl Op<NoTag, NoTag, NoTag> 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,
|
||||
tag: NoTag,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
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!();
|
||||
}
|
||||
|
||||
#[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<NoTag, NoTag, NoTag> 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,
|
||||
tag: NoTag,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
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!();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn map_t2_to_t1(_tag: NoTag) -> NoTag {
|
||||
NoTag
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn map_t3_to_t1(_tag: NoTag) -> NoTag {
|
||||
NoTag
|
||||
}
|
||||
}
|
||||
|
||||
struct Intersect<T>(PhantomData<T>);
|
||||
|
||||
impl<T> Op<T, T, NoTag> for Intersect<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
const APPEND_NON_A: bool = false;
|
||||
const APPEND_NON_B: bool = false;
|
||||
|
||||
fn handle_band(new: &mut Container<T>, a: &[RectRaw<T>], 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<u32>]) -> Container<u32> {
|
||||
rects_to_bands_(rects_tmp)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn rects_to_bands_<T>(rects_tmp: &[RectRaw<T>]) -> Container<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
#[derive(Copy, Clone)]
|
||||
struct W<T>(RectRaw<T>)
|
||||
where
|
||||
T: Tag;
|
||||
impl<T> Eq for W<T> where T: Tag {}
|
||||
impl<T> PartialEq<Self> for W<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
impl<T> PartialOrd<Self> for W<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl<T> Ord for W<T>
|
||||
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<T> Deref for W<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
type Target = RectRaw<T>;
|
||||
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>() {
|
||||
#[expect(clippy::never_loop)]
|
||||
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;
|
||||
let mut tag: T = rect.tag;
|
||||
while let Some(mut rect) = rects.peek().copied() {
|
||||
check_rect!(rect);
|
||||
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 {
|
||||
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,
|
||||
tag: tag.constrain(),
|
||||
});
|
||||
}
|
||||
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
|
||||
}
|
||||
38
crates/algorithms/src/windows.rs
Normal file
38
crates/algorithms/src/windows.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue