use std::fmt;
use crate::base::{error::StateError, state::State};
#[derive(Clone, Debug, PartialEq, Default)]
pub struct SO3State {
pub x: f64,
pub y: f64,
pub z: f64,
pub w: f64,
}
impl SO3State {
pub fn new(x: f64, y: f64, z: f64, w: f64) -> Self {
SO3State { x, y, z, w }
}
pub fn normalise(&mut self) -> Result<Self, StateError> {
let norm = (self.x.powi(2) + self.y.powi(2) + self.z.powi(2) + self.w.powi(2)).sqrt();
if norm < 1e-9 {
Err(StateError::ZeroMagnitude)
} else {
Ok(SO3State {
x: self.x / norm,
y: self.y / norm,
z: self.z / norm,
w: self.w / norm,
})
}
}
pub fn identity() -> Self {
Self {
x: 0.,
y: 0.,
z: 0.,
w: 1.,
}
}
}
impl State for SO3State {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl fmt::Display for SO3State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Quaternion: [x: {}, y: {}, z: {}, w: {}]",
self.x, self.y, self.z, self.w
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_so3_state_new() {
let state = SO3State::new(1.0, 2.0, 3.0, 4.0);
assert_eq!(state.x, 1.0);
assert_eq!(state.y, 2.0);
assert_eq!(state.z, 3.0);
assert_eq!(state.w, 4.0);
}
#[test]
fn test_so3_state_clone() {
let state1 = SO3State::new(1.0, 2.0, 3.0, 4.0);
let state2 = state1.clone();
assert_eq!(state1, state2);
}
#[test]
fn test_so3_state_identity() {
let identity = SO3State::identity();
assert_eq!(identity.x, 0.0);
assert_eq!(identity.y, 0.0);
assert_eq!(identity.z, 0.0);
assert_eq!(identity.w, 1.0);
}
#[test]
fn test_so3_state_normalise_ok() {
let mut state = SO3State::new(1.0, 2.0, 3.0, 4.0);
let norm = 30.0f64.sqrt();
let normalised_state = state.normalise().unwrap();
assert!((normalised_state.x - (1.0 / norm)).abs() < 1e-9);
assert!((normalised_state.y - (2.0 / norm)).abs() < 1e-9);
assert!((normalised_state.z - (3.0 / norm)).abs() < 1e-9);
assert!((normalised_state.w - (4.0 / norm)).abs() < 1e-9);
}
#[test]
fn test_so3_state_normalise_err_on_zero_magnitude() {
let mut zero_state = SO3State::new(0.0, 0.0, 0.0, 0.0);
let result = zero_state.normalise();
assert!(result.is_err());
match result.err().unwrap() {
StateError::ZeroMagnitude => (),
}
}
}