use core::ops::Mul;
use crate::linear_algebra::{Matrix, Vector};
use crate::scalar::Numeric;
#[derive(Debug, Clone, Copy, PartialEq)]
#[allow(clippy::upper_case_acronyms)]
pub struct SO2<T: Numeric> {
c: T,
s: T,
}
impl<T: Numeric> SO2<T> {
#[inline]
pub fn identity() -> Self {
SO2 {
c: T::ONE,
s: T::ZERO,
}
}
#[inline]
pub fn from_angle(theta: T) -> Self {
SO2 {
c: theta.cos(),
s: theta.sin(),
}
}
#[inline]
pub fn cos_sin(self) -> (T, T) {
(self.c, self.s)
}
#[inline]
pub fn compose(self, rhs: Self) -> Self {
SO2 {
c: self.c * rhs.c - self.s * rhs.s,
s: self.c * rhs.s + self.s * rhs.c,
}
}
#[inline]
pub fn inverse(self) -> Self {
SO2 {
c: self.c,
s: -self.s,
}
}
#[inline]
pub fn act(self, p: Vector<2, T>) -> Vector<2, T> {
Vector::new([self.c * p[0] - self.s * p[1], self.s * p[0] + self.c * p[1]])
}
#[inline]
pub fn exp(theta: T) -> Self {
Self::from_angle(theta)
}
#[inline]
pub fn log(self) -> T {
self.s.atan2(self.c)
}
#[inline]
pub fn hat(theta: T) -> Matrix<2, 2, T> {
Matrix::new([[T::ZERO, -theta], [theta, T::ZERO]])
}
#[inline]
pub fn vee(m: Matrix<2, 2, T>) -> T {
m[(1, 0)]
}
#[inline]
pub fn adjoint(self) -> T {
T::ONE
}
#[inline]
pub fn to_matrix(self) -> Matrix<2, 2, T> {
Matrix::new([[self.c, -self.s], [self.s, self.c]])
}
#[inline]
pub fn try_from_matrix(m: Matrix<2, 2, T>) -> Option<Self> {
let (c, s) = (m[(0, 0)], m[(1, 0)]);
let n = (c * c + s * s).sqrt();
if !n.is_finite() || n <= T::EPSILON {
None
} else {
Some(SO2 { c: c / n, s: s / n })
}
}
#[inline]
pub fn interpolate(self, other: Self, t: T) -> Self {
self.compose(Self::exp(self.inverse().compose(other).log() * t))
}
#[inline]
pub fn left_jacobian(_theta: T) -> T {
T::ONE
}
#[inline]
pub fn right_jacobian(_theta: T) -> T {
T::ONE
}
#[inline]
pub fn left_jacobian_inverse(_theta: T) -> T {
T::ONE
}
#[inline]
pub fn right_jacobian_inverse(_theta: T) -> T {
T::ONE
}
}
impl<T: Numeric> Mul for SO2<T> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
self.compose(rhs)
}
}