// Pose / TRS types.

///|
/// Translation * Rotation * Scale. `to_mat4` gives the model matrix.
/// A negative scale component represents mirroring.
#valtype
pub(all) struct Transform {
  translation : Vec3
  rotation : Quat
  scale : Vec3
} derive(Eq, Debug)

///|
pub fn Transform::identity() -> Transform {
  {
    translation: Vec3::zero(),
    rotation: Quat::identity(),
    scale: Vec3::splat(1.0),
  }
}

///|
pub fn Transform::new(t : Vec3, r : Quat, s : Vec3) -> Transform {
  { translation: t, rotation: r, scale: s, }
}

///|
pub fn Transform::to_mat4(self : Transform) -> Mat4 {
  Mat4::from_trs(self.translation, self.rotation, self.scale)
}

///|
/// local -> parent
pub fn Transform::mul_point(self : Transform, p : Vec3) -> Vec3 {
  self.rotation.rotate(p * self.scale) + self.translation
}

///|
pub fn Transform::mul_dir(self : Transform, d : Vec3) -> Vec3 {
  self.rotation.rotate(d * self.scale)
}

///|
/// Rigid body pose: rotation + translation only (no scale). Forms a group: composition and
/// inverse are exact. Use for camera poses, bones, physics bodies, frames.
#valtype
pub(all) struct Rigid {
  rotation : Quat
  translation : Vec3
} derive(Eq, Debug)

///|
pub fn Rigid::identity() -> Rigid {
  { rotation: Quat::identity(), translation: Vec3::zero(), }
}

///|
pub fn Rigid::new(rotation : Quat, translation : Vec3) -> Rigid {
  { rotation, translation, }
}

///|
/// `a * b` = apply `b` first, then `a` (same order as matrices).
pub impl Mul for Rigid with fn mul(a, b) {
  {
    rotation: (a.rotation * b.rotation).normalize(),
    translation: a.rotation.rotate(b.translation) + a.translation,
  }
}

///|
pub fn Rigid::inverse(self : Rigid) -> Rigid {
  let ri = self.rotation.conjugate()
  { rotation: ri, translation: -ri.rotate(self.translation), }
}

///|
pub fn Rigid::mul_point(self : Rigid, p : Vec3) -> Vec3 {
  self.rotation.rotate(p) + self.translation
}

///|
pub fn Rigid::mul_dir(self : Rigid, d : Vec3) -> Vec3 {
  self.rotation.rotate(d)
}

///|
pub fn Rigid::to_mat4(self : Rigid) -> Mat4 {
  Mat4::from_trs(self.translation, self.rotation, Vec3::splat(1.0))
}

///|
/// Extract from a rigid matrix (orthonormal 3x3, affine). Not validated.
pub fn Rigid::from_mat4(m : Mat4) -> Rigid {
  { rotation: Quat::from_mat3(m.to_mat3()), translation: m.translation_part(), }
}

///|
/// Pose (local -> world) of an object at `eye` whose local -Z looks at `target` with +Y
/// toward `up`. For a camera this is the camera-to-world transform.
pub fn Rigid::look_at(eye : Vec3, target : Vec3, up : Vec3) -> Rigid {
  { rotation: Quat::look_rotation(target - eye, up), translation: eye, }
}

///|
pub fn Rigid::lerp(self : Rigid, o : Rigid, t : Double) -> Rigid {
  {
    rotation: self.rotation.slerp(o.rotation, t),
    translation: self.translation.lerp(o.translation, t),
  }
}