Skip to content

Commit 56b9e74

Browse files
committed
Add snapped to integer vectors
1 parent 4dc2720 commit 56b9e74

File tree

7 files changed

+91
-25
lines changed

7 files changed

+91
-25
lines changed

godot-core/src/builtin/vectors/vector2i.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ impl_vector_fns!(Vector2i, glam::IVec2, i32, (x, y));
4444
impl_vector2x_fns!(Vector2i, i32);
4545

4646
impl Vector2i {
47+
impl_integer_vector_fns!(x, y);
48+
4749
/// Constructs a new `Vector2i` from a [`Vector2`]. The floating point coordinates will be truncated.
4850
#[inline]
4951
pub const fn from_vector2(v: Vector2) -> Self {

godot-core/src/builtin/vectors/vector3i.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ impl_vector_fns!(Vector3i, glam::IVec3, i32, (x, y, z));
4747
impl_vector3x_fns!(Vector3i, i32);
4848

4949
impl Vector3i {
50+
impl_integer_vector_fns!(x, y, z);
51+
5052
/// Constructs a new `Vector3i` from a [`Vector3`]. The floating point coordinates will be truncated.
5153
#[inline]
5254
pub const fn from_vector3(v: Vector3) -> Self {

godot-core/src/builtin/vectors/vector4i.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ impl_vector_fns!(Vector4i, glam::IVec4, i32, (x, y, z, w));
4848
impl_vector4x_fns!(Vector4i, i32);
4949

5050
impl Vector4i {
51+
impl_integer_vector_fns!(x, y, z, w);
52+
5153
/// Constructs a new `Vector4i` from a [`Vector4`]. The floating point coordinates will be
5254
/// truncated.
5355
#[inline]

godot-core/src/builtin/vectors/vector_macros.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,61 @@ macro_rules! impl_vector_fns {
457457
}
458458
}
459459

460+
pub(super) fn snap_one(mut value: i32, step: i32) -> i32 {
461+
assert!(
462+
value != i32::MIN || step >= 0,
463+
"snapped() called on vector component i32::MIN with step component smaller then 0"
464+
);
465+
assert!(
466+
value != i32::MAX || step <= 1,
467+
"snapped() called on vector component i32::MAX with step component greater then 1"
468+
);
469+
470+
if step != 0 {
471+
// Can overflow if step < -1 and value is i32::MIN or step > 1 and value is i32::MAX.
472+
let a = value + step / 2;
473+
474+
// Manual implement `a.div_floor(step)` since Rust's native method is still unstable, as of 1.79.0.
475+
476+
// Can overflow if value == i32::MIN and step == -1 because then a == i32::MIN.
477+
let mut d = a / step;
478+
// Can't overflow because if a == i32::MIN and step == -1, value == -2147483647.5 which is impossible.
479+
let r = a % step;
480+
if (r > 0 && step < 0) || (r < 0 && step > 0) {
481+
// Can't overflow because if d == i32::MIN then a == i32::MIN and step == 1 and value == -2147483648.5 which is impossible.
482+
d -= 1;
483+
}
484+
485+
value = step * d;
486+
}
487+
488+
value
489+
}
490+
491+
/// Implements functions that are present only on integer vectors.
492+
macro_rules! impl_integer_vector_fns {
493+
(
494+
// Names of the components, for example `x, y`.
495+
$($comp:ident),*
496+
) => {
497+
/// A new vector with each component snapped to the closest multiple of the corresponding
498+
/// component in `step`.
499+
///
500+
/// # Panics
501+
/// If any component of `self` is `i32::MIN` while the same component on `step` smaller then `0` or
502+
/// if any component of `self` is `i32::MAX` while the same component on `step` greater then `1`.
503+
pub fn snapped(self, step: Self) -> Self {
504+
use crate::builtin::vectors::vector_macros::snap_one;
505+
506+
Self::new(
507+
$(
508+
snap_one(self.$comp, step.$comp)
509+
),*
510+
)
511+
}
512+
};
513+
}
514+
460515
/// Implements functions that are present only on floating-point vectors.
461516
macro_rules! impl_float_vector_fns {
462517
(
@@ -642,7 +697,6 @@ macro_rules! impl_float_vector_fns {
642697

643698
/// A new vector with each component snapped to the closest multiple of the corresponding
644699
/// component in `step`.
645-
// TODO: also implement for integer vectors
646700
#[inline]
647701
pub fn snapped(self, step: Self) -> Self {
648702
Self::new(

itest/rust/src/builtin_tests/geometry/vector_test/vector2i_test.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,13 @@ fn sign() {
102102
assert_eq!(b.sign(), b.as_inner().sign());
103103
}
104104

105-
// TODO: implement snapped for integer vectors
106-
// #[itest]
107-
// fn snapped() {
108-
// let a = Vector2i::new(12, 34);
109-
// let b = Vector2i::new(5, -5);
110-
111-
// assert_eq!(a.snapped(b), a.as_inner().snapped(b));
112-
// }
105+
#[itest]
106+
fn snapped() {
107+
let a = Vector2i::new(12, 34);
108+
let b = Vector2i::new(5, -5);
109+
let c = Vector2i::new(0, 0);
110+
let d = Vector2i::new(3, 0);
111+
112+
assert_eq!(a.snapped(b), a.as_inner().snapped(b));
113+
assert_eq!(c.snapped(d), c.as_inner().snapped(d));
114+
}

itest/rust/src/builtin_tests/geometry/vector_test/vector3i_test.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,13 @@ fn sign() {
9999
assert_eq!(b.sign(), b.as_inner().sign());
100100
}
101101

102-
// TODO: implement snapped for integer vectors
103-
// #[itest]
104-
// fn snapped() {
105-
// let a = Vector3i::new(12, 34, 56);
106-
// let b = Vector3i::new(5, -5, 6);
107-
108-
// assert_eq!(a.snapped(b), a.as_inner().snapped(b));
109-
// }
102+
#[itest]
103+
fn snapped() {
104+
let a = Vector3i::new(12, 34, -56);
105+
let b = Vector3i::new(5, -5, 6);
106+
let c = Vector3i::new(0, 3, 0);
107+
let d = Vector3i::new(3, 0, 0);
108+
109+
assert_eq!(a.snapped(b), a.as_inner().snapped(b));
110+
assert_eq!(c.snapped(d), c.as_inner().snapped(d));
111+
}

itest/rust/src/builtin_tests/geometry/vector_test/vector4i_test.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@ fn sign() {
103103
assert_eq!(b.sign(), b.as_inner().sign());
104104
}
105105

106-
// TODO: implement snapped for integer vectors
107-
// #[itest]
108-
// fn snapped() {
109-
// let a = Vector4i::new(12, 34, 56, 78);
110-
// let b = Vector4i::new(5, -5, 6, -6);
111-
112-
// assert_eq!(a.snapped(b), a.as_inner().snapped(b));
113-
// }
106+
#[itest]
107+
fn snapped() {
108+
let a = Vector4i::new(12, 34, 56, -78);
109+
let b = Vector4i::new(5, -5, 6, 6);
110+
let c = Vector4i::new(0, 3, 0, 0);
111+
let d = Vector4i::new(3, 0, -3, 0);
112+
113+
assert_eq!(a.snapped(b), a.as_inner().snapped(b));
114+
assert_eq!(c.snapped(d), c.as_inner().snapped(d));
115+
}

0 commit comments

Comments
 (0)