///|
pub fn Rect2::new(min~ : Point2, max~ : Point2) -> Rect2 raise GeometryError {
  if max.x < min.x || max.y < min.y {
    raise GeometryError::DegenerateInput(
      "Rect2 max corner must not be smaller than min corner",
    )
  }
  { min, max }
}

///|
pub fn Rect2::from_center_size(
  center~ : Point2,
  width~ : Double,
  height~ : Double,
) -> Rect2 raise GeometryError {
  if width < 0.0 || height < 0.0 {
    raise GeometryError::DegenerateInput("Rect2 size must be non-negative")
  }
  let half_w = width / 2.0
  let half_h = height / 2.0
  Rect2::new(
    min=Point2::new(x=center.x - half_w, y=center.y - half_h),
    max=Point2::new(x=center.x + half_w, y=center.y + half_h),
  )
}

///|
pub fn rect2_from_points(
  points : ArrayView[Point2],
) -> Rect2 raise GeometryError {
  if points.length() == 0 {
    raise GeometryError::NotEnoughPoints("Rect2 needs at least one point")
  }
  let first = points[0]
  let mut min_x = first.x
  let mut min_y = first.y
  let mut max_x = first.x
  let mut max_y = first.y
  for i in 1.. max_x {
      max_x = p.x
    }
    if p.y > max_y {
      max_y = p.y
    }
  }
  Rect2::new(
    min=Point2::new(x=min_x, y=min_y),
    max=Point2::new(x=max_x, y=max_y),
  )
}

///|
pub fn Rect2::width(rect : Rect2) -> Double {
  rect.max.x - rect.min.x
}

///|
pub fn Rect2::height(rect : Rect2) -> Double {
  rect.max.y - rect.min.y
}

///|
pub fn Rect2::area(rect : Rect2) -> Double {
  rect.width() * rect.height()
}

///|
pub fn Rect2::center(rect : Rect2) -> Point2 {
  Point2::new(
    x=(rect.min.x + rect.max.x) / 2.0,
    y=(rect.min.y + rect.max.y) / 2.0,
  )
}

///|
pub fn Rect2::contains(rect : Rect2, p : Point2) -> Bool {
  p.x >= rect.min.x &&
  p.x <= rect.max.x &&
  p.y >= rect.min.y &&
  p.y <= rect.max.y
}

///|
pub fn Rect2::expand(
  rect : Rect2,
  margin : Double,
) -> Rect2 raise GeometryError {
  Rect2::new(
    min=Point2::new(x=rect.min.x - margin, y=rect.min.y - margin),
    max=Point2::new(x=rect.max.x + margin, y=rect.max.y + margin),
  )
}

///|
pub fn Rect2::union(a : Rect2, b : Rect2) -> Rect2 {
  Rect2::{
    min: Point2::new(
      x=if a.min.x < b.min.x { a.min.x } else { b.min.x },
      y=if a.min.y < b.min.y { a.min.y } else { b.min.y },
    ),
    max: Point2::new(
      x=if a.max.x > b.max.x { a.max.x } else { b.max.x },
      y=if a.max.y > b.max.y { a.max.y } else { b.max.y },
    ),
  }
}

///|
pub fn Rect2::intersection(a : Rect2, b : Rect2) -> Rect2? {
  let min_x = if a.min.x > b.min.x { a.min.x } else { b.min.x }
  let min_y = if a.min.y > b.min.y { a.min.y } else { b.min.y }
  let max_x = if a.max.x < b.max.x { a.max.x } else { b.max.x }
  let max_y = if a.max.y < b.max.y { a.max.y } else { b.max.y }
  if max_x < min_x || max_y < min_y {
    None
  } else {
    Some(Rect2::{
      min: Point2::new(x=min_x, y=min_y),
      max: Point2::new(x=max_x, y=max_y),
    })
  }
}