///|
/// A two-dimensional point or vector.
pub(all) struct Point {
  x : Double
  y : Double
} derive(Debug)

///|
/// Construct a point.
pub fn Point::new(x~ : Double, y~ : Double) -> Point {
  { x, y }
}

///|
/// Add two points as vectors.
pub fn Point::add(self : Point, other : Point) -> Point {
  { x: self.x + other.x, y: self.y + other.y }
}

///|
/// Subtract two points as vectors.
pub fn Point::sub(self : Point, other : Point) -> Point {
  { x: self.x - other.x, y: self.y - other.y }
}

///|
/// Scale a vector.
pub fn Point::scale(self : Point, factor : Double) -> Point {
  { x: self.x * factor, y: self.y * factor }
}

///|
/// Dot product.
pub fn Point::dot(self : Point, other : Point) -> Double {
  self.x * other.x + self.y * other.y
}

///|
/// Euclidean length.
pub fn Point::length(self : Point) -> Double {
  (self.x * self.x + self.y * self.y).sqrt()
}

///|
/// Distance between two points.
pub fn Point::distance(self : Point, other : Point) -> Double {
  self.sub(other).length()
}

///|
/// Axis-aligned rectangular bounds.
pub(all) struct Aabb {
  min : Point
  max : Point
} derive(Debug)

///|
/// Construct normalized bounds even when corners are supplied in reverse.
pub fn Aabb::new(min~ : Point, max~ : Point) -> Aabb {
  {
    min: { x: min.x.min(max.x), y: min.y.min(max.y) },
    max: { x: min.x.max(max.x), y: min.y.max(max.y) },
  }
}

///|
/// True when a point lies inside or on the bounds.
pub fn Aabb::contains(self : Aabb, point : Point) -> Bool {
  point.x >= self.min.x &&
  point.x <= self.max.x &&
  point.y >= self.min.y &&
  point.y <= self.max.y
}

///|
/// Circle geometry primitive.
pub(all) struct Circle {
  center : Point
  radius : Double
} derive(Debug)

///|
/// Construct a circle.
pub fn Circle::new(center~ : Point, radius~ : Double) -> Circle {
  { center, radius }
}

///|
/// Signed distance from a circle boundary; negative means inside.
pub fn Circle::signed_distance(self : Circle, point : Point) -> Double {
  point.distance(self.center) - self.radius
}

///|
/// A shape that can be rasterized into a lattice mask.
pub(all) enum Shape {
  CircleShape(Circle)
  RectangleShape(Aabb)
} derive(Debug)

///|
/// Build a circle shape.
pub fn Shape::circle(center~ : Point, radius~ : Double) -> Shape {
  CircleShape(Circle::new(center~, radius~))
}

///|
/// Build a rectangle shape.
pub fn Shape::rectangle(bounds~ : Aabb) -> Shape {
  RectangleShape(bounds)
}

///|
/// Test a shape using lattice-cell-center coordinates.
pub fn Shape::contains(self : Shape, point : Point) -> Bool {
  match self {
    CircleShape(circle) =>
      circle.signed_distance(point) <= 0.0 && circle.radius >= 0.0
    RectangleShape(bounds) => bounds.contains(point)
  }
}

///|
/// Integer coordinate used by connected-component reports.
pub(all) struct GridPoint {
  x : Int
  y : Int
} derive(Debug, Eq)

///|
/// A connected solid region in a domain mask.
pub(all) struct ConnectedComponent {
  size : Int
  cells : Array[GridPoint]
} derive(Debug)