Skip to content

Commit 7641696

Browse files
authored
Merge pull request #768 from joriskleiber/snapped-int-vectors
Add `snapped` to integer vectors
2 parents 88716c7 + 26c5e10 commit 7641696

File tree

7 files changed

+90
-25
lines changed

7 files changed

+90
-25
lines changed

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

+2
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

+2
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

+2
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

+54-1
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,60 @@ 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 != -1,
463+
"snapped() called on vector component i32::MIN with step component -1"
464+
);
465+
466+
if step != 0 {
467+
// Can overflow if step / 2 + value is not in range of i32.
468+
let a = (step / 2).checked_add(value).expect(
469+
"snapped() overflowed, this happened because step / 2 + component is not in range of i32",
470+
);
471+
472+
// Manually implement `a.div_floor(step)` since Rust's native method is still unstable, as of 1.79.0.
473+
474+
// Can overflow with a == i32::MIN and step == -1 when value == i32::MIN.
475+
let mut d = a / step;
476+
// Can't overflow because if a == i32::MIN and step == -1, value == -2147483647.5 which is impossible.
477+
let r = a % step;
478+
if (r > 0 && step < 0) || (r < 0 && step > 0) {
479+
// Can't overflow because if d == i32::MIN than a == i32::MIN and step == 1 and value == -2147483648.5 which is impossible.
480+
d -= 1;
481+
}
482+
483+
value = step * d;
484+
}
485+
486+
value
487+
}
488+
489+
/// Implements functions that are present only on integer vectors.
490+
macro_rules! impl_integer_vector_fns {
491+
(
492+
// Names of the components, for example `x, y`.
493+
$($comp:ident),*
494+
) => {
495+
/// A new vector with each component snapped to the closest multiple of the corresponding
496+
/// component in `step`.
497+
///
498+
/// # Panics
499+
/// On under- or overflow:
500+
/// - If any component of `self` is [`i32::MIN`] while the same component on `step` is `-1`.
501+
/// - If any component of `self` plus half of the same component of `step` is not in range on [`i32`].
502+
pub fn snapped(self, step: Self) -> Self {
503+
use crate::builtin::vectors::vector_macros::snap_one;
504+
505+
Self::new(
506+
$(
507+
snap_one(self.$comp, step.$comp)
508+
),*
509+
)
510+
}
511+
};
512+
}
513+
460514
/// Implements functions that are present only on floating-point vectors.
461515
macro_rules! impl_float_vector_fns {
462516
(
@@ -642,7 +696,6 @@ macro_rules! impl_float_vector_fns {
642696

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

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

+10-8
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

+10-8
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

+10-8
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)