box3d_rust/math_functions/
quat.rs1use super::*;
5
6pub fn is_normalized_quat(q: Quat) -> bool {
8 let qq = q.v.x * q.v.x + q.v.y * q.v.y + q.v.z * q.v.z + q.s * q.s;
9 1.0 - 20.0 * f32::EPSILON < qq && qq < 1.0 + 20.0 * f32::EPSILON
10}
11
12pub fn rotate_vector(q: Quat, v: Vec3) -> Vec3 {
14 let t1 = cross(q.v, v);
17 let t2 = mul_add(t1, q.s, v);
18 let t3 = cross(q.v, t2);
19 mul_add(v, 2.0, t3)
20}
21
22pub fn inv_rotate_vector(q: Quat, v: Vec3) -> Vec3 {
24 let t1 = cross(q.v, v);
27 let t2 = mul_sub(t1, q.s, v);
28 let t3 = cross(q.v, t2);
29 mul_add(v, 2.0, t3)
30}
31
32pub fn dot_quat(a: Quat, b: Quat) -> f32 {
34 a.v.x * b.v.x + a.v.y * b.v.y + a.v.z * b.v.z + a.s * b.s
35}
36
37pub fn mul_quat(q1: Quat, q2: Quat) -> Quat {
39 let t1 = cross(q1.v, q2.v);
40 let t2 = mul_add(t1, q1.s, q2.v);
41 let t3 = mul_add(t2, q2.s, q1.v);
42 Quat {
43 v: t3,
44 s: q1.s * q2.s - dot(q1.v, q2.v),
45 }
46}
47
48pub fn inv_mul_quat(q1: Quat, q2: Quat) -> Quat {
51 let t1 = cross(q2.v, q1.v);
52 let t2 = mul_add(t1, q1.s, q2.v);
53 let t3 = mul_sub(t2, q2.s, q1.v);
54 Quat {
55 v: t3,
56 s: q1.s * q2.s + dot(q1.v, q2.v),
57 }
58}
59
60pub fn conjugate(q: Quat) -> Quat {
62 Quat {
63 v: Vec3 {
64 x: -q.v.x,
65 y: -q.v.y,
66 z: -q.v.z,
67 },
68 s: q.s,
69 }
70}
71
72pub fn negate_quat(q: Quat) -> Quat {
74 Quat {
75 v: Vec3 {
76 x: -q.v.x,
77 y: -q.v.y,
78 z: -q.v.z,
79 },
80 s: -q.s,
81 }
82}
83
84pub fn normalize_quat(q: Quat) -> Quat {
86 let length_sq = dot_quat(q, q);
87 if length_sq > 1000.0 * f32::MIN_POSITIVE {
88 let s = 1.0 / length_sq.sqrt();
89 Quat {
90 v: Vec3 {
91 x: s * q.v.x,
92 y: s * q.v.y,
93 z: s * q.v.z,
94 },
95 s: s * q.s,
96 }
97 } else {
98 QUAT_IDENTITY
99 }
100}
101
102pub fn integrate_rotation(q1: Quat, delta_rotation: Vec3) -> Quat {
107 let mut qd = Quat {
109 v: mul_sv(0.5, delta_rotation),
110 s: 0.0,
111 };
112 qd = mul_quat(qd, q1);
113 let q2 = Quat {
114 v: add(q1.v, qd.v),
115 s: qd.s + q1.s,
116 };
117 normalize_quat(q2)
118}
119
120pub fn make_quat_from_axis_angle(axis: Vec3, radians: f32) -> Quat {
122 debug_assert!(is_normalized(axis));
123 let cs = compute_cos_sin(0.5 * radians);
124 Quat {
125 v: Vec3 {
126 x: cs.sine * axis.x,
127 y: cs.sine * axis.y,
128 z: cs.sine * axis.z,
129 },
130 s: cs.cosine,
131 }
132}
133
134pub fn get_axis_angle(radians: &mut f32, q: Quat) -> Vec3 {
136 let length = (q.v.x * q.v.x + q.v.y * q.v.y + q.v.z * q.v.z).sqrt();
137 *radians = 2.0 * atan2(length, q.s);
138 if length > 0.0 {
139 let inv_length = 1.0 / length;
140 Vec3 {
141 x: inv_length * q.v.x,
142 y: inv_length * q.v.y,
143 z: inv_length * q.v.z,
144 }
145 } else {
146 VEC3_ZERO
147 }
148}
149
150pub fn get_quat_angle(q: Quat) -> f32 {
152 let length = (q.v.x * q.v.x + q.v.y * q.v.y + q.v.z * q.v.z).sqrt();
153 2.0 * atan2(length, q.s)
154}
155
156pub fn make_quat_from_matrix(m: &Matrix3) -> Quat {
158 let c1 = m.cx;
159 let c2 = m.cy;
160 let c3 = m.cz;
161
162 let q: Quat;
163
164 let trace = m.cx.x + m.cy.y + m.cz.z;
165 if trace >= 0.0 {
166 q = Quat {
167 v: Vec3 {
168 x: c2.z - c3.y,
169 y: c3.x - c1.z,
170 z: c1.y - c2.x,
171 },
172 s: trace + 1.0,
173 };
174 } else if c1.x > c2.y && c1.x > c3.z {
175 q = Quat {
176 v: Vec3 {
177 x: c1.x - c2.y - c3.z + 1.0,
178 y: c2.x + c1.y,
179 z: c3.x + c1.z,
180 },
181 s: c2.z - c3.y,
182 };
183 } else if c2.y > c3.z {
184 q = Quat {
185 v: Vec3 {
186 x: c1.y + c2.x,
187 y: c2.y - c3.z - c1.x + 1.0,
188 z: c3.y + c2.z,
189 },
190 s: c3.x - c1.z,
191 };
192 } else {
193 q = Quat {
194 v: Vec3 {
195 x: c1.z + c3.x,
196 y: c2.z + c3.y,
197 z: c3.z - c1.x - c2.y + 1.0,
198 },
199 s: c1.y - c2.x,
200 };
201 }
202
203 normalize_quat(q)
205}
206
207pub fn compute_quat_between_unit_vectors(v1: Vec3, v2: Vec3) -> Quat {
209 debug_assert!(is_normalized(v1));
210 debug_assert!(is_normalized(v2));
211
212 let out: Quat;
213
214 let m = lerp(v1, v2, 0.5);
215 let tolerance = 100.0 * f32::EPSILON;
216 if length_squared(m) > tolerance * tolerance {
217 out = Quat {
218 v: cross(v1, m),
219 s: dot(v1, m),
220 };
221 } else {
222 if abs_float(v1.x) > 0.5 {
224 out = Quat {
225 v: Vec3 {
226 x: v1.y,
227 y: -v1.x,
228 z: 0.0,
229 },
230 s: 0.0,
231 };
232 } else {
233 out = Quat {
234 v: Vec3 {
235 x: 0.0,
236 y: v1.z,
237 z: -v1.y,
238 },
239 s: 0.0,
240 };
241 }
242 }
243
244 normalize_quat(out)
246}
247
248pub fn get_twist_angle(q: Quat) -> f32 {
250 let mut twist = if q.s < 0.0 {
253 atan2(-q.v.z, -q.s)
254 } else {
255 atan2(q.v.z, q.s)
256 };
257 twist *= 2.0;
258 debug_assert!((-PI..=PI).contains(&twist));
259 twist
260}
261
262pub fn delta_quat_to_rotation(q: Quat, target: Quat) -> Vec3 {
265 let mut s = q;
266 if dot_quat(q, target) < 0.0 {
267 s = negate_quat(q);
269 }
270
271 let diff = Quat {
272 v: sub(target.v, s.v),
273 s: target.s - s.s,
274 };
275 let product = mul_quat(diff, conjugate(s));
276 mul_sv(2.0, product.v)
277}
278
279pub fn get_swing_angle(q: Quat) -> f32 {
281 let x = (q.v.z * q.v.z + q.s * q.s).sqrt();
283 let y = (q.v.x * q.v.x + q.v.y * q.v.y).sqrt();
284 let swing = 2.0 * atan2(y, x);
285 debug_assert!((0.0..=PI).contains(&swing));
286 swing
287}
288
289pub fn nlerp(mut q1: Quat, q2: Quat, alpha: f32) -> Quat {
291 debug_assert!((0.0..=1.0).contains(&alpha));
292 if dot_quat(q1, q2) < 0.0 {
293 q1 = Quat {
294 v: Vec3 {
295 x: -q1.v.x,
296 y: -q1.v.y,
297 z: -q1.v.z,
298 },
299 s: -q1.s,
300 };
301 }
302
303 let q = Quat {
304 v: lerp(q1.v, q2.v, alpha),
305 s: (1.0 - alpha) * q1.s + alpha * q2.s,
306 };
307
308 normalize_quat(q)
309}