all: split reusable components into workspace crates
This commit is contained in:
parent
2a079ed800
commit
657e7ce2f7
225 changed files with 7422 additions and 17602 deletions
339
geometry/src/region.rs
Normal file
339
geometry/src/region.rs
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
use {
|
||||
crate::{Rect, Region},
|
||||
jay_algorithms::rect::{
|
||||
RectRaw, Tag,
|
||||
region::{
|
||||
extents, intersect, intersect_tagged, rects_to_bands, rects_to_bands_tagged, subtract,
|
||||
union,
|
||||
},
|
||||
},
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
array,
|
||||
borrow::Cow,
|
||||
cell::UnsafeCell,
|
||||
fmt::{Debug, Formatter},
|
||||
mem,
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
static EMPTY: Rc<Region> =
|
||||
Rc::new(Region {
|
||||
rects: Default::default(),
|
||||
extents: Default::default(),
|
||||
});
|
||||
}
|
||||
|
||||
impl Region {
|
||||
pub fn empty() -> Rc<Self> {
|
||||
EMPTY.with(|e| e.clone())
|
||||
}
|
||||
|
||||
pub fn from_rects(rects: &[Rect]) -> Rc<Self> {
|
||||
if rects.is_empty() {
|
||||
return Self::empty();
|
||||
}
|
||||
Rc::new(Self::from_rects2(rects))
|
||||
}
|
||||
|
||||
pub fn from_rects2(rects: &[Rect]) -> Self {
|
||||
if rects.is_empty() {
|
||||
return Self::default();
|
||||
}
|
||||
if rects.len() == 1 {
|
||||
return Self::new(rects[0]);
|
||||
}
|
||||
let rects = rects_to_bands(unsafe { mem::transmute::<&[Rect], &[RectRaw]>(rects) });
|
||||
Self {
|
||||
extents: Rect {
|
||||
raw: extents(&rects),
|
||||
},
|
||||
rects,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(self: &Rc<Self>, other: &Rc<Self>) -> Rc<Self> {
|
||||
if self.extents.is_empty() {
|
||||
return other.clone();
|
||||
}
|
||||
if other.extents.is_empty() {
|
||||
return self.clone();
|
||||
}
|
||||
let rects = union(&self.rects, &other.rects);
|
||||
Rc::new(Self {
|
||||
rects,
|
||||
extents: self.extents.union(other.extents),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn union_cow<'a>(&'a self, other: &'a Self) -> Cow<'a, Region> {
|
||||
if self.extents.is_empty() {
|
||||
return Cow::Borrowed(other);
|
||||
}
|
||||
if other.extents.is_empty() {
|
||||
return Cow::Borrowed(self);
|
||||
}
|
||||
let rects = union(&self.rects, &other.rects);
|
||||
Cow::Owned(Self {
|
||||
rects,
|
||||
extents: self.extents.union(other.extents),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn subtract(self: &Rc<Self>, other: &Rc<Self>) -> Rc<Self> {
|
||||
if self.extents.is_empty() || other.extents.is_empty() {
|
||||
return self.clone();
|
||||
}
|
||||
let rects = subtract(&self.rects, &other.rects);
|
||||
Rc::new(Self {
|
||||
extents: Rect {
|
||||
raw: extents(&rects),
|
||||
},
|
||||
rects,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn subtract_cow<'a>(&'a self, other: &Self) -> Cow<'a, Self> {
|
||||
if self.extents.is_empty() || other.extents.is_empty() {
|
||||
return Cow::Borrowed(self);
|
||||
}
|
||||
let rects = subtract(&self.rects, &other.rects);
|
||||
Cow::Owned(Self {
|
||||
extents: Rect {
|
||||
raw: extents(&rects),
|
||||
},
|
||||
rects,
|
||||
})
|
||||
}
|
||||
|
||||
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<u32> {
|
||||
pub fn from_rects_tagged(rects: &[Rect<u32>]) -> 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::new(rect);
|
||||
}
|
||||
let rects = rects_to_bands_tagged(unsafe {
|
||||
mem::transmute::<&[Rect<u32>], &[RectRaw<u32>]>(rects)
|
||||
});
|
||||
Self {
|
||||
extents: Rect {
|
||||
raw: extents(&rects),
|
||||
},
|
||||
rects,
|
||||
}
|
||||
}
|
||||
|
||||
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<T> Region<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
pub fn new(rect: Rect<T>) -> Self {
|
||||
let mut rects = SmallVec::new();
|
||||
rects.push(rect.raw);
|
||||
Self {
|
||||
rects,
|
||||
extents: rect.untag(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extents(&self) -> Rect {
|
||||
self.extents
|
||||
}
|
||||
|
||||
pub fn rects(&self) -> &[Rect<T>] {
|
||||
unsafe { mem::transmute::<&[RectRaw<T>], &[Rect<T>]>(&self.rects[..]) }
|
||||
}
|
||||
|
||||
pub fn contains(&self, x: i32, y: i32) -> bool {
|
||||
if !self.extents.contains(x, y) {
|
||||
return false;
|
||||
}
|
||||
for r in self.deref() {
|
||||
if r.contains(x, y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn contains_rect(&self, rect: &Rect) -> bool {
|
||||
self.contains_rect2(rect, |r| *r)
|
||||
}
|
||||
|
||||
pub fn contains_rect2(&self, rect: &Rect, map: impl Fn(&Rect<T>) -> Rect<T>) -> bool {
|
||||
if rect.is_empty() {
|
||||
return true;
|
||||
}
|
||||
let mut y1 = rect.y1();
|
||||
for r in self.rects() {
|
||||
let r = map(r);
|
||||
if r.y2() <= y1 || r.x2() <= rect.x1() {
|
||||
continue;
|
||||
}
|
||||
if r.y1() > y1 || r.x1() > rect.x1() || r.x2() < rect.x2() {
|
||||
return false;
|
||||
}
|
||||
y1 = r.y2();
|
||||
if y1 >= rect.y2() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Region<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
type Target = [Rect<T>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { mem::transmute::<&[RectRaw<T>], _>(&self.rects) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
|
||||
enum BuilderOp {
|
||||
#[default]
|
||||
Add,
|
||||
Sub,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RegionBuilder {
|
||||
base: Rc<Region>,
|
||||
op: BuilderOp,
|
||||
pending: Vec<Rect>,
|
||||
}
|
||||
|
||||
impl Default for RegionBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
base: Region::empty(),
|
||||
op: Default::default(),
|
||||
pending: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<Region> {
|
||||
self.flush();
|
||||
self.base.clone()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.pending.clear();
|
||||
self.base = Region::empty();
|
||||
}
|
||||
|
||||
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),
|
||||
};
|
||||
self.pending.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DamageQueue {
|
||||
this: usize,
|
||||
datas: Rc<UnsafeCell<Vec<Vec<Rect>>>>,
|
||||
}
|
||||
|
||||
impl Debug for DamageQueue {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("DamageQueue").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl DamageQueue {
|
||||
pub fn new<const N: usize>() -> [DamageQueue; N] {
|
||||
let datas = Rc::new(UnsafeCell::new(vec![vec!(); N]));
|
||||
array::from_fn(|this| DamageQueue {
|
||||
this,
|
||||
datas: datas.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn damage(&self, rects: &[Rect]) {
|
||||
let datas = unsafe { &mut *self.datas.get() };
|
||||
for data in datas {
|
||||
data.extend(rects);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
let data = unsafe { &mut (&mut *self.datas.get())[self.this] };
|
||||
data.clear();
|
||||
}
|
||||
|
||||
pub fn clear_all(&self) {
|
||||
let datas = unsafe { &mut *self.datas.get() };
|
||||
for data in datas {
|
||||
data.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Region {
|
||||
let data = unsafe { &(&*self.datas.get())[self.this] };
|
||||
Region::from_rects2(data)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue