///|
pub fn Rect::is_empty(self : Rect) -> Bool {
  self.width <= 0 || self.height <= 0
}

///|
pub fn Rect::area(self : Rect) -> Int {
  max_i(0, self.width) * max_i(0, self.height)
}

///|
pub fn Rect::center(self : Rect) -> Point {
  Point::new(x=self.x + self.width / 2, y=self.y + self.height / 2)
}

///|
pub fn Rect::translate(self : Rect, dx : Int, dy : Int) -> Rect {
  Rect::new(x=self.x + dx, y=self.y + dy, width=self.width, height=self.height)
}

///|
pub fn Rect::inflate(self : Rect, amount : Int) -> Rect {
  Rect::new(
    x=self.x - amount,
    y=self.y - amount,
    width=self.width + amount * 2,
    height=self.height + amount * 2,
  )
}

///|
pub fn Rect::intersection(self : Rect, other : Rect) -> Rect {
  let left = max_i(self.x, other.x)
  let top = max_i(self.y, other.y)
  let right = min_i(self.right(), other.right())
  let bottom = min_i(self.bottom(), other.bottom())
  Rect::new(
    x=left,
    y=top,
    width=max_i(0, right - left),
    height=max_i(0, bottom - top),
  )
}

///|
pub fn Rect::union(self : Rect, other : Rect) -> Rect {
  let left = min_i(self.x, other.x)
  let top = min_i(self.y, other.y)
  let right = max_i(self.right(), other.right())
  let bottom = max_i(self.bottom(), other.bottom())
  Rect::new(x=left, y=top, width=right - left, height=bottom - top)
}

///|
pub fn Rect::iou(self : Rect, other : Rect) -> Int {
  let overlap = self.intersection(other).area()
  let total = self.area() + other.area() - overlap
  if total == 0 {
    0
  } else {
    overlap * 1000 / total
  }
}

///|
pub fn Point::distance_l1(self : Point, other : Point) -> Int {
  abs_i(self.x - other.x) + abs_i(self.y - other.y)
}

///|
pub fn Point::distance_linf(self : Point, other : Point) -> Int {
  max_i(abs_i(self.x - other.x), abs_i(self.y - other.y))
}

///|
pub fn Point::translate(self : Point, dx : Int, dy : Int) -> Point {
  Point::new(x=self.x + dx, y=self.y + dy)
}

///|
pub fn clamp_rect(rect : Rect, size : Size) -> Rect {
  let width = min_i(max_i(0, rect.width), max_i(0, size.width))
  let height = min_i(max_i(0, rect.height), max_i(0, size.height))
  let x = clamp(rect.x, low=0, high=max_i(0, size.width - width))
  let y = clamp(rect.y, low=0, high=max_i(0, size.height - height))
  Rect::new(x~, y~, width~, height~)
}

///|
pub fn rect_grid(size : Size, cell : Size) -> Array[Rect] {
  let out : Array[Rect] = []
  let cw = max_i(1, cell.width)
  let ch = max_i(1, cell.height)
  for y in 0.. Array[Rect] {
  let out : Array[Rect] = []
  let n = max_i(1, parts)
  for i in 0.. Array[Rect] {
  let out : Array[Rect] = []
  let n = max_i(1, parts)
  for i in 0.. Mask {
  let mask = Mask::new(width=size.width, height=size.height)
  mask.fill_rect(self, id)
  mask
}

///|
pub fn mask_iou(a : Mask, b : Mask, positive : Int) -> Int {
  let count = min_i(a.values.length(), b.values.length())
  let mut intersection = 0
  let mut union = 0
  for i in 0.. Int {
  let mut total = 0
  for value in self.values {
    if value == id {
      total += 1
    }
  }
  total
}

///|
pub fn Mask::bounding_box(self : Mask, id : Int) -> Rect {
  let mut left = self.width
  let mut top = self.height
  let mut right = 0
  let mut bottom = 0
  for y in 0.. Box2D {
  Box2D::new(
    label=self.label,
    rect=clamp_rect(self.rect, size),
    occluded=self.occluded,
  )
}

///|
pub fn Box2D::iou(self : Box2D, other : Box2D) -> Int {
  self.rect.iou(other.rect)
}

///|
pub fn Box2D::center(self : Box2D) -> Point {
  self.rect.center()
}

///|
pub fn Box2D::is_valid(self : Box2D) -> Bool {
  !self.rect.is_empty() && self.label.id >= 0
}

///|
pub fn keypoints_visible(points : Array[Keypoint]) -> Int {
  let mut total = 0
  for point in points {
    if point.visible {
      total += 1
    }
  }
  total
}

///|
pub fn flow_endpoint(flow : FlowVector) -> Point {
  Point::new(x=flow.x + flow.dx, y=flow.y + flow.dy)
}

///|
pub fn flow_magnitude_l1(flow : FlowVector) -> Int {
  abs_i(flow.dx) + abs_i(flow.dy)
}

///|
pub fn flow_mean(flow : Array[FlowVector]) -> Point {
  if flow.length() == 0 {
    Point::new(x=0, y=0)
  } else {
    let mut dx = 0
    let mut dy = 0
    for item in flow {
      dx += item.dx
      dy += item.dy
    }
    Point::new(x=dx / flow.length(), y=dy / flow.length())
  }
}