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
15impl 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
34impl<'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;