use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
use vek::Vec2;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rotation {
cos: f64,
sin: f64,
}
impl Rotation {
#[inline]
pub const fn zero() -> Self {
let (sin, cos) = (0.0, 1.0);
Self { sin, cos }
}
#[inline]
pub fn from_radians(rotation: f64) -> Self {
let (sin, cos) = rotation.sin_cos();
Self { sin, cos }
}
#[inline]
pub fn from_degrees(rotation: f64) -> Self {
Self::from_radians(rotation.to_radians())
}
#[inline]
pub fn from_direction(dir: Vec2<f64>) -> Self {
Self::from_radians(dir.y.atan2(dir.x))
}
#[inline]
pub fn to_radians(self) -> f64 {
self.sin.atan2(self.cos)
}
#[inline]
pub fn to_degrees(self) -> f64 {
self.to_radians().to_degrees()
}
#[inline]
pub fn rotate(&self, point: Vec2<f64>) -> Vec2<f64> {
point.rotated_z(self.to_radians())
}
#[inline]
pub fn sin(&self) -> f64 {
self.sin
}
#[inline]
pub fn cos(&self) -> f64 {
self.cos
}
}
impl Default for Rotation {
fn default() -> Self {
Self { cos: 1.0, sin: 0.0 }
}
}
impl From<f64> for Rotation {
fn from(value: f64) -> Self {
Self::from_radians(value)
}
}
impl AddAssign<f64> for Rotation {
fn add_assign(&mut self, rhs: f64) {
*self = *self + rhs;
}
}
impl AddAssign<Self> for Rotation {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Add<f64> for Rotation {
type Output = Self;
fn add(self, rhs: f64) -> Self::Output {
if rhs.is_sign_positive() {
self + Self::from_radians(rhs)
} else {
self - Self::from_radians(-rhs)
}
}
}
impl Add<Self> for Rotation {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
cos: self.cos * rhs.cos - self.sin * rhs.sin,
sin: self.sin * rhs.cos + self.cos * rhs.sin,
}
}
}
impl SubAssign<Self> for Rotation {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl SubAssign<f64> for Rotation {
fn sub_assign(&mut self, rhs: f64) {
*self = *self - rhs;
}
}
impl Sub<Self> for Rotation {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
self + -rhs
}
}
impl Sub<f64> for Rotation {
type Output = Self;
fn sub(self, rhs: f64) -> Self::Output {
if rhs.is_sign_positive() {
self - Self::from_radians(rhs)
} else {
self + Self::from_radians(-rhs)
}
}
}
impl Neg for Rotation {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
cos: self.cos,
sin: -self.sin,
}
}
}
#[cfg(test)]
mod tests {
use super::Rotation;
#[test]
fn test_ops() {
let mut a = Rotation::from_degrees(90.0);
let b = Rotation::from_degrees(45.0);
assert_eq!((-a).to_degrees().round() as i16, -90);
assert_eq!((a + b).to_degrees().round() as i16, 135);
assert_eq!((a - b).to_degrees().round() as i16, 45);
assert_eq!((a + 45f64.to_radians()).to_degrees().round() as i16, 135);
assert_eq!((a + 180f64.to_radians()).to_degrees().round() as i16, -90);
assert_eq!((a - 180f64.to_radians()).to_degrees().round() as i16, -90);
assert_eq!((a - 90f64.to_radians()).to_degrees().round() as i16, 0);
assert_eq!(a - 1.0, a + -1.0);
a -= 10f64.to_radians();
assert_eq!(a.to_degrees().round() as i16, 80);
a += 10f64.to_radians();
assert_eq!(a.to_degrees().round() as i16, 90);
}
}