use core::ops::Mul;
use crate::linear_algebra::{Matrix, Matrix2D, Vector, Vector2D};
use crate::scalar::Numeric;
#[derive(Debug, Clone, Copy, PartialEq)]
#[allow(clippy::upper_case_acronyms)]
pub struct SO2<T: Numeric = f64> {
c: T,
s: T,
}
impl<T: Numeric> SO2<T> {
#[inline]
#[must_use]
pub fn identity() -> Self {
SO2 {
c: T::ONE,
s: T::ZERO,
}
}
#[inline]
#[must_use]
pub fn from_angle(theta: T) -> Self {
SO2 {
c: theta.cos(),
s: theta.sin(),
}
}
#[inline]
#[must_use]
pub fn cos_sin(self) -> (T, T) {
(self.c, self.s)
}
#[inline]
#[must_use]
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]
#[must_use]
pub fn inverse(self) -> Self {
SO2 {
c: self.c,
s: -self.s,
}
}
#[inline]
pub fn act(self, p: Vector2D<T>) -> Vector2D<T> {
let [px, py] = *p.as_array();
Vector::new([self.c * px - self.s * py, self.s * px + self.c * py])
}
#[inline]
#[must_use]
pub fn exp(theta: T) -> Self {
Self::from_angle(theta)
}
#[inline]
#[must_use]
pub fn log(self) -> T {
self.s.atan2(self.c)
}
#[inline]
pub fn hat(theta: T) -> Matrix2D<T> {
Matrix::new([[T::ZERO, -theta], [theta, T::ZERO]])
}
#[inline]
#[must_use]
pub fn vee(m: Matrix2D<T>) -> T {
let [[_, _], [m10, _]] = m.into_array();
m10
}
#[inline]
#[must_use]
pub fn adjoint(self) -> T {
T::ONE
}
#[inline]
pub fn to_matrix(self) -> Matrix2D<T> {
Matrix::new([[self.c, -self.s], [self.s, self.c]])
}
#[inline]
#[must_use]
pub fn try_from_matrix(m: Matrix2D<T>) -> Option<Self> {
let [[c, m01], [s, m11]] = m.into_array();
let n = c.hypot(s);
if !n.is_finite() || n <= T::EPSILON || (n - T::ONE).abs() > T::EPSILON_X30 {
return None;
}
let c = c / n;
let s = s / n;
let second_column_error = (m01 + s).hypot(m11 - c);
if !second_column_error.is_finite() || second_column_error > T::EPSILON_X30 {
return None;
}
Some(SO2 { c, s })
}
#[inline]
#[must_use]
pub fn interpolate(self, other: Self, t: T) -> Self {
self.compose(Self::exp(self.inverse().compose(other).log() * t))
}
#[inline]
#[must_use]
fn norm_squared(self) -> T {
self.c * self.c + self.s * self.s
}
#[inline]
#[must_use]
pub fn norm(self) -> T {
self.norm_squared().sqrt()
}
#[inline]
#[must_use]
pub fn normalized(self) -> Self {
let scale = self.norm().recip();
SO2 {
c: self.c * scale,
s: self.s * scale,
}
}
#[inline]
#[must_use]
pub fn left_jacobian(_theta: T) -> T {
T::ONE
}
#[inline]
#[must_use]
pub fn right_jacobian(_theta: T) -> T {
T::ONE
}
#[inline]
#[must_use]
pub fn left_jacobian_inverse(_theta: T) -> T {
T::ONE
}
#[inline]
#[must_use]
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)
}
}
impl<T: Numeric> Default for SO2<T> {
fn default() -> Self {
Self::identity()
}
}