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
365
geometry/src/lib.rs
Normal file
365
geometry/src/lib.rs
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
mod region;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use region::{DamageQueue, RegionBuilder};
|
||||
use {
|
||||
jay_algorithms::rect::{NoTag, RectRaw, Tag},
|
||||
smallvec::SmallVec,
|
||||
std::fmt::{Debug, Formatter},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct Rect<T = NoTag>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
raw: RectRaw<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Default)]
|
||||
pub struct Region<T = NoTag>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
rects: SmallVec<[RectRaw<T>; 1]>,
|
||||
extents: Rect,
|
||||
}
|
||||
|
||||
impl Debug for Rect {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(&self.raw, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
|
||||
pub struct RectOverflow {
|
||||
pub left: i32,
|
||||
pub right: i32,
|
||||
pub top: i32,
|
||||
pub bottom: i32,
|
||||
}
|
||||
|
||||
impl RectOverflow {
|
||||
pub fn is_contained(&self) -> bool {
|
||||
self.left <= 0 && self.right <= 0 && self.top <= 0 && self.bottom <= 0
|
||||
}
|
||||
|
||||
pub fn x_overflow(&self) -> bool {
|
||||
self.left > 0 || self.right > 0
|
||||
}
|
||||
|
||||
pub fn y_overflow(&self) -> bool {
|
||||
self.top > 0 || self.bottom > 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Rect<T>
|
||||
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 {
|
||||
raw: RectRaw {
|
||||
x1: x,
|
||||
y1: y,
|
||||
x2: x,
|
||||
y2: y,
|
||||
tag: NoTag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(x1: i32, y1: i32, x2: i32, y2: i32) -> Option<Self> {
|
||||
if x2 < x1 || y2 < y1 {
|
||||
return None;
|
||||
}
|
||||
Some(Self {
|
||||
raw: RectRaw {
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
tag: NoTag,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[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,
|
||||
tag: NoTag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_sized(x1: i32, y1: i32, width: i32, height: i32) -> Option<Self> {
|
||||
if width < 0 || height < 0 {
|
||||
return None;
|
||||
}
|
||||
Self::new(x1, y1, x1 + width, y1 + height)
|
||||
}
|
||||
|
||||
pub fn new_saturating(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
|
||||
Self {
|
||||
raw: RectRaw {
|
||||
x1,
|
||||
y1,
|
||||
x2: x2.max(x1),
|
||||
y2: y2.max(y1),
|
||||
tag: NoTag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_sized_saturating(x1: i32, y1: i32, width: i32, height: i32) -> Self {
|
||||
Self {
|
||||
raw: RectRaw {
|
||||
x1,
|
||||
y1,
|
||||
x2: x1.saturating_add(width.max(0)),
|
||||
y2: y1.saturating_add(height.max(0)),
|
||||
tag: NoTag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(&self, other: Self) -> Self {
|
||||
Self {
|
||||
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),
|
||||
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_saturating(&self, width: i32, height: i32) -> Self {
|
||||
Self::new_sized_saturating(self.raw.x1, self.raw.y1, width, height)
|
||||
}
|
||||
|
||||
pub fn with_tag(&self, tag: u32) -> Rect<u32> {
|
||||
Rect {
|
||||
raw: RectRaw {
|
||||
x1: self.raw.x1,
|
||||
y1: self.raw.y1,
|
||||
x2: self.raw.x2,
|
||||
y2: self.raw.y2,
|
||||
tag,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Rect<T>
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intersects(&self, other: &Self) -> bool {
|
||||
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 contains(&self, x: i32, y: i32) -> bool {
|
||||
self.raw.x1 <= x && self.raw.y1 <= y && self.raw.x2 > x && self.raw.y2 > y
|
||||
}
|
||||
|
||||
pub fn not_contains(&self, x: i32, y: i32) -> bool {
|
||||
!self.contains(x, y)
|
||||
}
|
||||
|
||||
pub fn dist_squared(&self, x: i32, y: i32) -> i128 {
|
||||
let x = x as i64;
|
||||
let y = y as i64;
|
||||
let x1 = self.raw.x1 as i64;
|
||||
let x2 = self.raw.x2 as i64;
|
||||
let y1 = self.raw.y1 as i64;
|
||||
let y2 = self.raw.y2 as i64;
|
||||
let mut dx = 0;
|
||||
if x1 > x {
|
||||
dx = x1 - x;
|
||||
} else if x2 < x {
|
||||
dx = x - x2;
|
||||
}
|
||||
let mut dy = 0;
|
||||
if y1 > y {
|
||||
dy = y1 - y;
|
||||
} else if y2 < y {
|
||||
dy = y - y2;
|
||||
}
|
||||
let dx = dx as i128;
|
||||
let dy = dy as i128;
|
||||
dx * dx + dy * dy
|
||||
}
|
||||
|
||||
pub fn contains_rect<U>(&self, rect: &Rect<U>) -> 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<U>(&self, child: &Rect<U>) -> RectOverflow
|
||||
where
|
||||
U: Tag,
|
||||
{
|
||||
RectOverflow {
|
||||
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.raw.x1 == self.raw.x2 || self.raw.y1 == self.raw.y2
|
||||
}
|
||||
|
||||
pub fn is_not_empty(&self) -> bool {
|
||||
!self.is_empty()
|
||||
}
|
||||
|
||||
pub fn to_origin(&self) -> Self {
|
||||
Self {
|
||||
raw: RectRaw {
|
||||
x1: 0,
|
||||
y1: 0,
|
||||
x2: self.raw.x2 - self.raw.x1,
|
||||
y2: self.raw.y2 - self.raw.y1,
|
||||
tag: self.raw.tag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_(&self, dx: i32, dy: i32) -> Self {
|
||||
Self {
|
||||
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),
|
||||
tag: self.raw.tag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn at_point(&self, x1: i32, y1: i32) -> Self {
|
||||
Self {
|
||||
raw: RectRaw {
|
||||
x1,
|
||||
y1,
|
||||
x2: x1 + self.raw.x2 - self.raw.x1,
|
||||
y2: y1 + self.raw.y2 - self.raw.y1,
|
||||
tag: self.raw.tag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate(&self, x: i32, y: i32) -> (i32, i32) {
|
||||
(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.raw.x1), y.wrapping_add(self.raw.y1))
|
||||
}
|
||||
|
||||
pub fn x1(&self) -> i32 {
|
||||
self.raw.x1
|
||||
}
|
||||
|
||||
pub fn x2(&self) -> i32 {
|
||||
self.raw.x2
|
||||
}
|
||||
|
||||
pub fn y1(&self) -> i32 {
|
||||
self.raw.y1
|
||||
}
|
||||
|
||||
pub fn y2(&self) -> i32 {
|
||||
self.raw.y2
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
self.raw.x2 - self.raw.x1
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
self.raw.y2 - self.raw.y1
|
||||
}
|
||||
|
||||
pub fn position(&self) -> (i32, i32) {
|
||||
(self.raw.x1, self.raw.y1)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> (i32, i32) {
|
||||
(self.width(), self.height())
|
||||
}
|
||||
|
||||
pub fn center(&self) -> (i32, i32) {
|
||||
(
|
||||
self.raw.x1 + self.width() / 2,
|
||||
self.raw.y1 + self.height() / 2,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> T {
|
||||
self.raw.tag
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
715
geometry/src/tests.rs
Normal file
715
geometry/src/tests.rs
Normal file
|
|
@ -0,0 +1,715 @@
|
|||
use {
|
||||
crate::{Rect, Region},
|
||||
jay_algorithms::rect::{NoTag, RectRaw},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn union1() {
|
||||
let r1 = Region::new(Rect::new(0, 0, 10, 10).unwrap());
|
||||
let r2_ = Region::new(Rect::new(5, 5, 15, 15).unwrap());
|
||||
let r2 = Region::new(Rect::new(10, 10, 20, 20).unwrap());
|
||||
let r3 = r1.union_cow(&r2);
|
||||
let r3 = r3.union_cow(&r2_);
|
||||
assert_eq!(r3.extents, Rect::new(0, 0, 20, 20).unwrap());
|
||||
assert_eq!(
|
||||
&r3.rects[..],
|
||||
&[
|
||||
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,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union2() {
|
||||
let r1 = Region::new(Rect::new(0, 0, 10, 10).unwrap());
|
||||
let r2 = Region::new(Rect::new(0, 10, 10, 20).unwrap());
|
||||
let r3 = r1.union_cow(&r2);
|
||||
assert_eq!(r3.extents, Rect::new(0, 0, 10, 20).unwrap());
|
||||
assert_eq!(&r3.rects[..], &[Rect::new(0, 0, 10, 20).unwrap().raw,]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtract1() {
|
||||
let r1 = Region::new(Rect::new(0, 0, 20, 20).unwrap());
|
||||
let r2 = Region::new(Rect::new(5, 5, 15, 15).unwrap());
|
||||
let r3 = r1.subtract_cow(&r2);
|
||||
assert_eq!(r3.extents, Rect::new(0, 0, 20, 20).unwrap());
|
||||
assert_eq!(
|
||||
&r3.rects[..],
|
||||
&[
|
||||
RectRaw {
|
||||
x1: 0,
|
||||
y1: 0,
|
||||
x2: 20,
|
||||
y2: 5,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 0,
|
||||
y1: 5,
|
||||
x2: 5,
|
||||
y2: 15,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 15,
|
||||
y1: 5,
|
||||
x2: 20,
|
||||
y2: 15,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 0,
|
||||
y1: 15,
|
||||
x2: 20,
|
||||
y2: 20,
|
||||
tag: NoTag,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rects_to_bands() {
|
||||
let rects = [
|
||||
Rect::new_unchecked_danger(0, 0, 10, 10),
|
||||
Rect::new_unchecked_danger(5, 0, 30, 10),
|
||||
Rect::new_unchecked_danger(30, 5, 50, 15),
|
||||
];
|
||||
let r = Region::from_rects(&rects[..]);
|
||||
// println!("{:#?}", r.rects);
|
||||
assert_eq!(
|
||||
&r.rects[..],
|
||||
&[
|
||||
RectRaw {
|
||||
x1: 0,
|
||||
y1: 0,
|
||||
x2: 30,
|
||||
y2: 5,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 0,
|
||||
y1: 5,
|
||||
x2: 50,
|
||||
y2: 10,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 30,
|
||||
y1: 10,
|
||||
x2: 50,
|
||||
y2: 15,
|
||||
tag: NoTag,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rects_to_bands2() {
|
||||
let rects = [
|
||||
Rect::new_unchecked_danger(0, 0, 10, 10),
|
||||
Rect::new_unchecked_danger(0, 10, 10, 20),
|
||||
];
|
||||
let r = Region::from_rects(&rects[..]);
|
||||
// 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),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_saturating() {
|
||||
let r1 = Rect::new_sized_saturating(10, 10, -5, -5);
|
||||
assert!(r1.is_empty());
|
||||
assert_eq!(r1.x1(), 10);
|
||||
assert_eq!(r1.x2(), 10);
|
||||
|
||||
let r2 = Rect::new_saturating(100, 100, 50, 50);
|
||||
assert!(r2.is_empty());
|
||||
assert_eq!(r2.x1(), 100);
|
||||
assert_eq!(r2.x2(), 100);
|
||||
|
||||
let r3 = Rect::new_sized_saturating(i32::MAX - 10, 0, 100, 10);
|
||||
assert_eq!(r3.x2(), i32::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dist_squared() {
|
||||
let r1 = Rect::new_sized_saturating(i32::MIN, i32::MIN, 0, 0);
|
||||
assert_eq!(
|
||||
r1.dist_squared(i32::MAX, i32::MAX),
|
||||
(1 << 65) + 2 - (1 << 34),
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue