///|
/// 2D vector. Core math type for all geometry in MoonCollider.
pub(all) struct Vec2 {
  x : Double
  y : Double
} derive(Eq, Debug)

///|
/// Construct a vector.
pub fn Vec2::new(x : Double, y : Double) -> Vec2 {
  { x, y }
}

///|
/// Zero vector.
pub fn Vec2::zero() -> Vec2 {
  { x: 0.0, y: 0.0 }
}

///|
/// Unit vector along +x.
pub fn Vec2::unit_x() -> Vec2 {
  { x: 1.0, y: 0.0 }
}

///|
/// Unit vector along +y.
pub fn Vec2::unit_y() -> Vec2 {
  { x: 0.0, y: 1.0 }
}

///|
/// Vector addition.
pub fn Vec2::add(self : Vec2, other : Vec2) -> Vec2 {
  { x: self.x + other.x, y: self.y + other.y }
}

///|
/// Vector subtraction.
pub fn Vec2::sub(self : Vec2, other : Vec2) -> Vec2 {
  { x: self.x - other.x, y: self.y - other.y }
}

///|
/// Scale by a scalar.
pub fn Vec2::scale(self : Vec2, s : Double) -> Vec2 {
  { x: self.x * s, y: self.y * s }
}

///|
/// Negate.
pub fn Vec2::neg(self : Vec2) -> Vec2 {
  { x: -self.x, y: -self.y }
}

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

///|
/// 2D cross product (scalar): self.x * other.y - self.y * other.x.
pub fn Vec2::cross(self : Vec2, other : Vec2) -> Double {
  self.x * other.y - self.y * other.x
}

///|
/// Squared length (cheap, no sqrt).
pub fn Vec2::length_sq(self : Vec2) -> Double {
  self.x * self.x + self.y * self.y
}

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

///|
/// Normalize to unit length. Returns zero vector if length is zero.
pub fn Vec2::normalize(self : Vec2) -> Vec2 {
  let l = self.length()
  if l == 0.0 {
    Vec2::zero()
  } else {
    { x: self.x / l, y: self.y / l }
  }
}

///|
/// Perpendicular vector (rotated +90 degrees).
pub fn Vec2::perp(self : Vec2) -> Vec2 {
  { x: -self.y, y: self.x }
}

///|
/// Rotate by `angle` radians (counter-clockwise).
pub fn Vec2::rotate(self : Vec2, angle : Double) -> Vec2 {
  let c = @math.cos(angle)
  let s = @math.sin(angle)
  { x: self.x * c - self.y * s, y: self.x * s + self.y * c }
}

///|
/// Approximate equality within `eps`.
pub fn Vec2::approx_eq(self : Vec2, other : Vec2, eps : Double) -> Bool {
  (self.x - other.x).abs() < eps && (self.y - other.y).abs() < eps
}