///|
pub fn Rect::translate(self : Rect, dx : Int, dy : Int) -> Rect {
{ ..self, x: self.x + dx, y: self.y + dy }
}
///|
pub fn Rect::inset(self : Rect, amount : Int) -> Rect {
let edge = amount * 2
{ x: self.x + amount, y: self.y + amount, w: self.w - edge, h: self.h - edge }
}
///|
pub fn Rect::expand(self : Rect, amount : Int) -> Rect {
let edge = amount * 2
{ x: self.x - amount, y: self.y - amount, w: self.w + edge, h: self.h + edge }
}
///|
pub fn Rect::intersection(self : Rect, other : Rect) -> Rect? {
let x = if self.x > other.x { self.x } else { other.x }
let y = if self.y > other.y { self.y } else { other.y }
let right = if self.right() < other.right() {
self.right()
} else {
other.right()
}
let bottom = if self.bottom() < other.bottom() {
self.bottom()
} else {
other.bottom()
}
let result = { x, y, w: right - x, h: bottom - y }
if result.is_empty() {
None
} else {
Some(result)
}
}
///|
pub fn Rect::union(self : Rect, other : Rect) -> Rect {
let x = if self.x < other.x { self.x } else { other.x }
let y = if self.y < other.y { self.y } else { other.y }
let right = if self.right() > other.right() {
self.right()
} else {
other.right()
}
let bottom = if self.bottom() > other.bottom() {
self.bottom()
} else {
other.bottom()
}
{ x, y, w: right - x, h: bottom - y }
}
///|
pub fn Rect::overlaps(self : Rect, other : Rect) -> Bool {
match self.intersection(other) {
Some(_) => true
None => false
}
}
///|
pub fn Rect::touches(self : Rect, other : Rect) -> Bool {
let horizontal = self.x <= other.right() && self.right() >= other.x
let vertical = self.y <= other.bottom() && self.bottom() >= other.y
horizontal && vertical
}
///|
pub fn Rect::clamp_to(self : Rect, bounds : Rect) -> Rect {
let w = if self.w > bounds.w { bounds.w } else { self.w }
let h = if self.h > bounds.h { bounds.h } else { self.h }
let max_x = bounds.right() - w
let max_y = bounds.bottom() - h
let x0 = if self.x < bounds.x { bounds.x } else { self.x }
let y0 = if self.y < bounds.y { bounds.y } else { self.y }
let x = if x0 > max_x { max_x } else { x0 }
let y = if y0 > max_y { max_y } else { y0 }
{ x, y, w, h }
}
///|
pub fn Rect::center(self : Rect) -> Point {
{ x: self.x + self.w / 2, y: self.y + self.h / 2 }
}
///|
pub fn Rect::is_inside(self : Rect, bounds : Rect) -> Bool {
bounds.contains_rect(self)
}
///|
pub fn Rect::distance_to(self : Rect, other : Rect) -> Int {
let dx = if self.right() < other.x {
other.x - self.right()
} else if other.right() < self.x {
self.x - other.right()
} else {
0
}
let dy = if self.bottom() < other.y {
other.y - self.bottom()
} else if other.bottom() < self.y {
self.y - other.bottom()
} else {
0
}
dx + dy
}
///|
pub fn rect_area_sum(rects : ArrayView[Rect]) -> Int {
rects.fold(init=0, (total, rect) => total + rect.area())
}
///|
pub fn rect_bounds(rects : ArrayView[Rect]) -> Rect? {
match rects.get(0) {
None => None
Some(first) => {
let mut result = first
for rect in rects[1:] {
result = result.union(rect)
}
Some(result)
}
}
}
///|
pub fn rects_overlap_any(rects : ArrayView[Rect]) -> Bool {
for i in 0.. Rect {
let x = if rect.w < 0 { rect.x + rect.w } else { rect.x }
let y = if rect.h < 0 { rect.y + rect.h } else { rect.y }
{
x,
y,
w: if rect.w < 0 {
-rect.w
} else {
rect.w
},
h: if rect.h < 0 {
-rect.h
} else {
rect.h
},
}
}
///|
pub fn snap_rect(rect : Rect, grid : Int) -> Rect {
if grid <= 1 {
return rect
}
let snap = fn(value : Int) { value / grid * grid }
let right = (rect.right() + grid - 1) / grid * grid
let bottom = (rect.bottom() + grid - 1) / grid * grid
let x = snap(rect.x)
let y = snap(rect.y)
{ x, y, w: right - x, h: bottom - y }
}
///|
pub fn Size::max(self : Size, other : Size) -> Size {
{
w: if self.w > other.w {
self.w
} else {
other.w
},
h: if self.h > other.h {
self.h
} else {
other.h
},
}
}
///|
pub fn Size::scale(self : Size, factor : Int) -> Size {
{ w: self.w * factor, h: self.h * factor }
}