1use super::geom::Basis;
2use super::IsEqualApprox;
3use glam::Vec3A;
4use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5
6#[derive(Copy, Clone, Debug, PartialEq, Default)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[repr(C)]
12pub struct Vector3 {
13 pub x: f32,
14 pub y: f32,
15 pub z: f32,
16}
17
18#[allow(clippy::unnecessary_cast)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
20#[repr(u32)]
21pub enum Axis {
22 X = sys::godot_vector3_axis_GODOT_VECTOR3_AXIS_X as u32,
23 Y = sys::godot_vector3_axis_GODOT_VECTOR3_AXIS_Y as u32,
24 Z = sys::godot_vector3_axis_GODOT_VECTOR3_AXIS_Z as u32,
25}
26
27impl Axis {
28 #[inline]
30 pub fn to_unit_vector(self) -> Vector3 {
31 match self {
32 Axis::X => Vector3::RIGHT,
33 Axis::Y => Vector3::UP,
34 Axis::Z => Vector3::BACK,
35 }
36 }
37}
38
39impl Vector3 {
43 pub const ZERO: Self = Self::new(0.0, 0.0, 0.0);
45
46 pub const ONE: Self = Self::new(1.0, 1.0, 1.0);
48
49 pub const INF: Self = Self::new(f32::INFINITY, f32::INFINITY, f32::INFINITY);
51
52 pub const LEFT: Self = Self::new(-1.0, 0.0, 0.0);
54
55 pub const RIGHT: Self = Self::new(1.0, 0.0, 0.0);
57
58 pub const UP: Self = Self::new(0.0, 1.0, 0.0);
60
61 pub const DOWN: Self = Self::new(0.0, -1.0, 0.0);
63
64 pub const FORWARD: Self = Self::new(0.0, 0.0, -1.0);
66
67 pub const BACK: Self = Self::new(0.0, 0.0, 1.0);
69
70 #[inline]
72 pub const fn new(x: f32, y: f32, z: f32) -> Self {
73 Self { x, y, z }
74 }
75
76 #[inline]
78 pub fn abs(self) -> Self {
79 Self::gd(self.glam().abs())
80 }
81
82 #[inline]
84 pub fn angle_to(self, to: Self) -> f32 {
85 self.glam().angle_between(to.glam())
86 }
87
88 #[inline]
90 pub fn bounce(self, n: Self) -> Self {
91 -self.reflect(n)
92 }
93
94 #[inline]
96 pub fn ceil(self) -> Self {
97 Self::gd(self.glam().ceil())
98 }
99
100 #[inline]
102 pub fn cross(self, b: Self) -> Self {
103 Self::gd(self.glam().cross(b.glam()))
104 }
105
106 #[inline]
109 pub fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, t: f32) -> Self {
110 let mut p = (pre_a, self, b, post_b);
111
112 {
113 let ab = p.0.distance_to(p.1);
114 let bc = p.1.distance_to(p.2);
115 let cd = p.2.distance_to(p.3);
116
117 if ab > 0.0 {
118 p.0 = p.1 + (p.0 - p.1) * (bc / ab);
119 }
120 if cd > 0.0 {
121 p.3 = p.2 + (p.3 - p.2) * (bc / cd);
122 }
123 }
124
125 let t = (t, t * t, t * t * t);
126
127 0.5 * ((p.1 * 2.0)
128 + (-p.0 + p.2) * t.0
129 + (2.0 * p.0 - 5.0 * p.1 + 4.0 * p.2 - p.3) * t.1
130 + (-p.0 + 3.0 * p.1 - 3.0 * p.2 + p.3) * t.2)
131 }
132
133 #[inline]
135 pub fn direction_to(self, other: Vector3) -> Vector3 {
136 Self::gd((other.glam() - self.glam()).normalize())
137 }
138
139 #[inline]
144 pub fn distance_squared_to(self, other: Vector3) -> f32 {
145 other.glam().distance_squared(self.glam())
146 }
147
148 #[inline]
150 pub fn distance_to(self, other: Vector3) -> f32 {
151 other.glam().distance(self.glam())
152 }
153
154 #[inline]
166 pub fn dot(self, other: Self) -> f32 {
167 self.glam().dot(other.glam())
168 }
169
170 #[inline]
172 pub fn floor(self) -> Self {
173 Self::gd(self.glam().floor())
174 }
175
176 #[inline]
179 pub fn inverse(self) -> Self {
180 Self::new(1.0 / self.x, 1.0 / self.y, 1.0 / self.z)
181 }
182
183 #[inline]
186 pub fn is_equal_approx(self, v: Self) -> bool {
187 self.x.is_equal_approx(v.x) && self.y.is_equal_approx(v.y) && self.z.is_equal_approx(v.z)
188 }
189
190 #[inline]
192 pub fn is_normalized(self) -> bool {
193 self.glam().is_normalized()
194 }
195
196 #[inline]
198 pub fn length(self) -> f32 {
199 self.glam().length()
200 }
201
202 #[inline]
207 pub fn length_squared(self) -> f32 {
208 self.glam().length_squared()
209 }
210
211 #[inline]
214 pub fn linear_interpolate(self, b: Self, t: f32) -> Self {
215 Self::gd(self.glam().lerp(b.glam(), t))
216 }
217
218 #[inline]
222 #[allow(clippy::collapsible_else_if)]
223 pub fn max_axis(self) -> Axis {
224 if self.z > self.y {
225 if self.z > self.x {
226 Axis::Z
227 } else {
228 Axis::X
229 }
230 } else {
231 if self.y > self.x {
232 Axis::Y
233 } else {
234 Axis::X
235 }
236 }
237 }
238
239 #[inline]
243 #[allow(clippy::collapsible_else_if)]
244 pub fn min_axis(self) -> Axis {
245 if self.x < self.y {
246 if self.x < self.z {
247 Axis::X
248 } else {
249 Axis::Z
250 }
251 } else {
252 if self.y < self.z {
253 Axis::Y
254 } else {
255 Axis::Z
256 }
257 }
258 }
259
260 #[inline]
262 pub fn move_toward(self, to: Self, delta: f32) -> Self {
263 let vd = to - self;
264 let len = vd.length();
265 if len <= delta || approx::abs_diff_eq!(0.0, len) {
266 to
267 } else {
268 self.linear_interpolate(to, delta / len)
269 }
270 }
271
272 #[inline]
274 pub fn normalized(self) -> Self {
275 Self::gd(self.glam().normalize())
276 }
277
278 #[inline]
280 pub fn outer(self, b: Self) -> Basis {
281 Basis::from_rows(b * self.x, b * self.y, b * self.z)
282 }
283
284 #[inline]
286 pub fn posmod(self, rem: f32) -> Self {
287 self.posmodv(Self::new(rem, rem, rem))
288 }
289
290 #[inline]
293 pub fn posmodv(self, remv: Self) -> Self {
294 Self::new(
295 self.x.rem_euclid(remv.x),
296 self.y.rem_euclid(remv.y),
297 self.z.rem_euclid(remv.z),
298 )
299 }
300
301 #[inline]
303 pub fn project(self, b: Self) -> Self {
304 b * (self.dot(b) / b.length_squared())
305 }
306
307 #[inline]
309 pub fn reflect(self, n: Self) -> Self {
310 n * self.dot(n) * 2.0 - self
311 }
312
313 #[inline]
316 pub fn rotated(self, axis: Self, phi: f32) -> Self {
317 Basis::from_axis_angle(axis, phi) * self
318 }
319
320 #[inline]
323 pub fn round(self) -> Self {
324 Self::gd(self.glam().round())
325 }
326
327 #[inline]
331 pub fn sign(self) -> Self {
332 Self::gd(self.glam().signum())
333 }
334
335 #[inline]
340 pub fn slerp(self, b: Self, t: f32) -> Self {
341 let theta = self.angle_to(b);
342 self.rotated(self.cross(b).normalized(), theta * t)
343 }
344
345 #[inline]
347 pub fn slide(self, n: Self) -> Self {
348 self - n * self.dot(n)
349 }
350
351 #[inline]
354 pub fn snapped(self, by: Self) -> Self {
355 let stepify = |v: f32, s: f32| {
356 if by.x != 0.0 {
357 (v / s + 0.5).floor() * s
358 } else {
359 v
360 }
361 };
362 Self::new(
363 stepify(self.x, by.x),
364 stepify(self.y, by.y),
365 stepify(self.z, by.z),
366 )
367 }
368
369 #[inline]
374 pub fn to_diagonal_matrix(self) -> Basis {
375 Basis::from_diagonal(self)
376 }
377
378 #[doc(hidden)]
379 #[allow(clippy::wrong_self_convention)]
380 #[inline]
381 pub fn to_sys(self) -> sys::godot_vector3 {
382 unsafe { std::mem::transmute(self) }
383 }
384
385 #[doc(hidden)]
388 #[inline]
389 pub fn sys(&self) -> *const sys::godot_vector3 {
390 self as *const _ as *const _
391 }
392
393 #[doc(hidden)]
396 #[inline]
397 pub fn from_sys(v: sys::godot_vector3) -> Self {
398 unsafe { std::mem::transmute(v) }
399 }
400
401 #[inline]
402 pub(super) fn glam(self) -> Vec3A {
403 Vec3A::new(self.x, self.y, self.z)
404 }
405
406 #[inline]
407 pub(super) fn gd(from: Vec3A) -> Self {
408 Self::new(from.x, from.y, from.z)
409 }
410}
411
412impl AsRef<[f32; 3]> for Vector3 {
413 #[inline]
414 fn as_ref(&self) -> &[f32; 3] {
415 unsafe { &*(self as *const Vector3 as *const [f32; 3]) }
417 }
418}
419
420macro_rules! derive_op_impl {
421 ($trait:ident, $func:ident) => {
422 impl $trait for Vector3 {
423 type Output = Self;
424
425 #[inline]
426 fn $func(self, with: Self) -> Self {
427 Self::gd(self.glam().$func(with.glam()))
428 }
429 }
430 };
431 ($trait:ident, $func:ident, $in_type:ty) => {
432 impl $trait<$in_type> for Vector3 {
433 type Output = Self;
434
435 #[inline]
436 fn $func(self, with: $in_type) -> Self {
437 Self::gd(self.glam().$func(with))
438 }
439 }
440 };
441}
442
443macro_rules! derive_op_impl_rev {
444 ($trait:ident, $func:ident, $in_type:ty) => {
445 impl $trait<Vector3> for $in_type {
446 type Output = Vector3;
447
448 #[inline]
449 fn $func(self, with: Self::Output) -> Self::Output {
450 $trait::$func(with, self)
451 }
452 }
453 };
454}
455
456macro_rules! derive_assign_op_impl {
457 ($trait:ident, $func:ident, $op_func:ident) => {
458 impl $trait for Vector3 {
459 #[inline]
460 fn $func(&mut self, with: Self) {
461 *self = self.$op_func(with);
462 }
463 }
464 };
465 ($trait:ident, $func:ident, $op_func:ident, $in_type:ty) => {
466 impl $trait<$in_type> for Vector3 {
467 #[inline]
468 fn $func(&mut self, with: $in_type) {
469 *self = self.$op_func(with);
470 }
471 }
472 };
473}
474
475derive_op_impl!(Add, add);
476derive_op_impl!(Sub, sub);
477derive_op_impl!(Mul, mul);
478derive_op_impl!(Div, div);
479derive_op_impl!(Mul, mul, f32);
480derive_op_impl!(Div, div, f32);
481derive_op_impl_rev!(Mul, mul, f32);
482derive_assign_op_impl!(AddAssign, add_assign, add);
483derive_assign_op_impl!(SubAssign, sub_assign, sub);
484derive_assign_op_impl!(MulAssign, mul_assign, mul);
485derive_assign_op_impl!(DivAssign, div_assign, div);
486derive_assign_op_impl!(MulAssign, mul_assign, mul, f32);
487derive_assign_op_impl!(DivAssign, div_assign, div, f32);
488
489impl Neg for Vector3 {
490 type Output = Self;
491
492 #[inline]
493 fn neg(self) -> Self {
494 Self::gd(-self.glam())
495 }
496}
497
498godot_test!(
499 test_vector3_variants {
500 use crate::core_types::{FromVariant, ToVariant, Vector3};
501
502 fn test(vector: Vector3, set_to: Vector3) {
503 let api = crate::private::get_api();
504
505 let copied = vector;
506 unsafe {
507 assert_relative_eq!(vector.x, (api.godot_vector3_get_axis)(
508 &copied as *const _ as *const sys::godot_vector3,
509 Axis::X as u32 as sys::godot_vector3_axis
510 ));
511 assert_relative_eq!(vector.y, (api.godot_vector3_get_axis)(
512 &copied as *const _ as *const sys::godot_vector3,
513 Axis::Y as u32 as sys::godot_vector3_axis
514 ));
515 assert_relative_eq!(vector.z, (api.godot_vector3_get_axis)(
516 &copied as *const _ as *const sys::godot_vector3,
517 Axis::Z as u32 as sys::godot_vector3_axis
518 ));
519 }
520 assert_eq!(vector, copied);
521
522 let mut copied = vector;
523 unsafe {
524 (api.godot_vector3_set_axis)(
525 &mut copied as *mut _ as *mut sys::godot_vector3,
526 Axis::X as u32 as sys::godot_vector3_axis,
527 set_to.x
528 );
529 (api.godot_vector3_set_axis)(
530 &mut copied as *mut _ as *mut sys::godot_vector3,
531 Axis::Y as u32 as sys::godot_vector3_axis,
532 set_to.y
533 );
534 (api.godot_vector3_set_axis)(
535 &mut copied as *mut _ as *mut sys::godot_vector3,
536 Axis::Z as u32 as sys::godot_vector3_axis,
537 set_to.z
538 );
539 }
540 assert_eq!(set_to, copied);
541
542 let variant = vector.to_variant();
543 let vector_from_variant = Vector3::from_variant(&variant).unwrap();
544 assert_eq!(vector, vector_from_variant);
545 }
546
547 test(Vector3::new(1.0, 2.0, 3.0), Vector3::new(4.0, 5.0, 6.0));
548 test(Vector3::new(4.0, 5.0, 6.0), Vector3::new(7.0, 8.0, 9.0));
549 }
550);
551
552#[cfg(test)]
553mod tests {
554 use crate::core_types::Vector3;
555
556 #[test]
566 fn rotated() {
567 let v = Vector3::new(37.51756, 20.39467, 49.96816);
568 let phi = -0.4927880786382844;
569 let r = v.rotated(Vector3::UP, phi);
570 assert!(r.is_equal_approx(Vector3::new(9.414476, 20.39467, 61.77177)));
571 }
572
573 #[test]
574 fn it_is_copy() {
575 fn copy<T: Copy>() {}
576 copy::<Vector3>();
577 }
578
579 #[test]
580 fn it_has_the_same_size() {
581 use std::mem::size_of;
582 assert_eq!(size_of::<sys::godot_vector3>(), size_of::<Vector3>());
583 }
584
585 #[test]
586 fn it_supports_equality() {
587 assert_eq!(Vector3::new(1.0, 2.0, 3.0), Vector3::new(1.0, 2.0, 3.0));
588 }
589
590 #[test]
591 fn it_supports_inequality() {
592 assert_ne!(Vector3::new(1.0, 10.0, 100.0), Vector3::new(1.0, 2.0, 3.0));
593 }
594}