session_rust/
quaternion.rs

1use crate::Vector;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::ops::Mul;
4use uuid::Uuid;
5
6#[derive(Debug, Clone, PartialEq)]
7pub struct Quaternion {
8    pub typ: String,
9    pub guid: String,
10    pub name: String,
11    pub s: f32,
12    pub v: Vector,
13}
14
15// Custom serialization to flatten vector as x, y, z only
16impl Serialize for Quaternion {
17    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
18    where
19        S: Serializer,
20    {
21        use serde::ser::SerializeStruct;
22        let mut state = serializer.serialize_struct("Quaternion", 6)?;
23        state.serialize_field("type", &self.typ)?;
24        state.serialize_field("guid", &self.guid)?;
25        state.serialize_field("name", &self.name)?;
26        state.serialize_field("s", &self.s)?;
27        state.serialize_field("x", &self.v.x())?;
28        state.serialize_field("y", &self.v.y())?;
29        state.serialize_field("z", &self.v.z())?;
30        state.end()
31    }
32}
33
34// Custom deserialization
35impl<'de> Deserialize<'de> for Quaternion {
36    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37    where
38        D: Deserializer<'de>,
39    {
40        #[derive(Deserialize)]
41        struct QuaternionHelper {
42            #[serde(rename = "type")]
43            typ: String,
44            guid: String,
45            name: String,
46            s: f32,
47            x: f32,
48            y: f32,
49            z: f32,
50        }
51
52        let helper = QuaternionHelper::deserialize(deserializer)?;
53        Ok(Quaternion {
54            typ: helper.typ,
55            guid: helper.guid,
56            name: helper.name,
57            s: helper.s,
58            v: Vector::new(helper.x, helper.y, helper.z),
59        })
60    }
61}
62
63impl Quaternion {
64    pub fn new(s: f32, v: Vector) -> Self {
65        Quaternion {
66            typ: "Quaternion".to_string(),
67            guid: Uuid::new_v4().to_string(),
68            name: "my_quaternion".to_string(),
69            s,
70            v,
71        }
72    }
73
74    pub fn from_sv(s: f32, x: f32, y: f32, z: f32) -> Self {
75        Quaternion {
76            typ: "Quaternion".to_string(),
77            guid: Uuid::new_v4().to_string(),
78            name: "my_quaternion".to_string(),
79            s,
80            v: Vector::new(x, y, z),
81        }
82    }
83
84    pub fn identity() -> Self {
85        Quaternion {
86            typ: "Quaternion".to_string(),
87            guid: Uuid::new_v4().to_string(),
88            name: "my_quaternion".to_string(),
89            s: 1.0,
90            v: Vector::new(0.0, 0.0, 0.0),
91        }
92    }
93
94    pub fn from_axis_angle(axis: Vector, angle: f32) -> Self {
95        let axis = axis.normalize();
96        let half_angle = angle * 0.5;
97        let s = half_angle.cos();
98        let v = axis * half_angle.sin();
99        Quaternion {
100            typ: "Quaternion".to_string(),
101            guid: Uuid::new_v4().to_string(),
102            name: "my_quaternion".to_string(),
103            s,
104            v,
105        }
106    }
107
108    pub fn rotate_vector(&self, v: Vector) -> Vector {
109        let qv = self.v.clone();
110        let uv = qv.cross(&v);
111        let uuv = qv.cross(&uv);
112        v + (uv * self.s + uuv) * 2.0
113    }
114
115    pub fn magnitude(&self) -> f32 {
116        (self.s * self.s
117            + self.v.x() * self.v.x()
118            + self.v.y() * self.v.y()
119            + self.v.z() * self.v.z())
120        .sqrt()
121    }
122
123    pub fn normalize(&self) -> Self {
124        let mag = self.magnitude();
125        if mag > 1e-10 {
126            Quaternion {
127                typ: self.typ.clone(),
128                guid: self.guid.clone(),
129                name: self.name.clone(),
130                s: self.s / mag,
131                v: self.v.clone() / mag,
132            }
133        } else {
134            Self::identity()
135        }
136    }
137
138    pub fn conjugate(&self) -> Self {
139        Quaternion {
140            typ: self.typ.clone(),
141            guid: self.guid.clone(),
142            name: self.name.clone(),
143            s: self.s,
144            v: self.v.clone() * -1.0,
145        }
146    }
147
148    pub fn jsondump(&self) -> Result<String, Box<dyn std::error::Error>> {
149        Ok(serde_json::to_string_pretty(self)?)
150    }
151
152    pub fn jsonload(json_data: &str) -> Result<Self, Box<dyn std::error::Error>> {
153        Ok(serde_json::from_str(json_data)?)
154    }
155
156    pub fn to_json(&self, filepath: &str) -> Result<(), Box<dyn std::error::Error>> {
157        let json = self.jsondump()?;
158        std::fs::write(filepath, json)?;
159        Ok(())
160    }
161
162    pub fn from_json(filepath: &str) -> Result<Self, Box<dyn std::error::Error>> {
163        let json_data = std::fs::read_to_string(filepath)?;
164        Self::jsonload(&json_data)
165    }
166}
167
168impl Mul<Quaternion> for Quaternion {
169    type Output = Quaternion;
170
171    fn mul(self, rhs: Quaternion) -> Self::Output {
172        let s = self.s * rhs.s - self.v.dot(&rhs.v);
173        let v = rhs.v.clone() * self.s + self.v.clone() * rhs.s + self.v.cross(&rhs.v);
174        Quaternion {
175            typ: "Quaternion".to_string(),
176            guid: Uuid::new_v4().to_string(),
177            name: "my_quaternion".to_string(),
178            s,
179            v,
180        }
181    }
182}
183
184impl Default for Quaternion {
185    fn default() -> Self {
186        Self::identity()
187    }
188}
189
190#[cfg(test)]
191#[path = "quaternion_test.rs"]
192mod tests;