Skip to content

Commit 00a3005

Browse files
bors[bot]Bromeon
andauthored
Merge #314
314: `ApproxEq` trait + refactorings r=Bromeon a=Bromeon Changes: ### Add `ApproxEq` trait and implement it for many builtin types. * This standardizes checking for approximate equality across types. * Gets rid of the boilerplate 3rd argument `assert_eq_approx!(a, b, is_equal_approx)`. * That argument can still be specified if desired, using `assert_eq_approx!(a, b, fn = is_equal_approx)`. * In 95% of cases, it's not needed though. ### Refactor modules * `godot::builtin::math` is now a public module, which contains: * Godot ported functions such as `lerp`, `sign` * `ApproxEq` trait + `assert_eq_approx!`, `assert_ne_approx!` macros * glam utilities: `IVec4`, `GlamConv`, etc (internal) * The above symbols are no longer in `godot::builtin`. If this turns out to be an issue, we can also revert this in the future; but it might help some discoverability. Some symbols could also be considered to be part of the prelude. * Move `godot::native_structure` -> `godot::engine::native` * Added in #272 * Native structures are now less prominent because rarely used. * Move `real`-related symbols into their own `real.rs` file (internal; API unaffected). ### Refactor import statements (just around geometric types, not whole codebase) * Remove wildcard imports * Remove nested import statements * Consistent order Co-authored-by: Jan Haller <[email protected]>
2 parents 6608393 + 31e189e commit 00a3005

34 files changed

+658
-715
lines changed

.github/workflows/full-ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ on:
1212
branches:
1313
- staging
1414
- trying
15+
1516
env:
1617
GDEXT_FEATURES: ''
1718

godot-codegen/src/class_generator.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
333333
use godot_ffi as sys;
334334
use crate::engine::notify::*;
335335
use crate::builtin::*;
336-
use crate::native_structure::*;
336+
use crate::engine::native::*;
337337
use crate::obj::{AsArg, Gd};
338338
use sys::GodotFfi as _;
339339
use std::ffi::c_void;
@@ -564,7 +564,7 @@ fn make_builtin_class(
564564
let tokens = quote! {
565565
use godot_ffi as sys;
566566
use crate::builtin::*;
567-
use crate::native_structure::*;
567+
use crate::engine::native::*;
568568
use crate::obj::{AsArg, Gd};
569569
use crate::sys::GodotFfi as _;
570570
use crate::engine::Object;
@@ -605,7 +605,7 @@ fn make_native_structure(
605605
let tokens = quote! {
606606
use godot_ffi as sys;
607607
use crate::builtin::*;
608-
use crate::native_structure::*;
608+
use crate::engine::native::*;
609609
use crate::obj::{AsArg, Gd};
610610
use crate::sys::GodotFfi as _;
611611
use crate::engine::Object;

godot-core/src/builtin/aabb.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
use godot_ffi as sys;
88
use sys::{ffi_methods, GodotFfi};
99

10-
use super::{real, Plane, Vector3, Vector3Axis};
10+
use crate::builtin::math::ApproxEq;
11+
use crate::builtin::{real, Plane, Vector3, Vector3Axis};
1112

1213
/// Axis-aligned bounding box in 3D space.
1314
///
@@ -185,13 +186,6 @@ impl Aabb {
185186
self.size = end - self.position
186187
}
187188

188-
/// Returns `true` if the two `Aabb`s are approximately equal, by calling `is_equal_approx` on
189-
/// `position` and `size`.
190-
#[inline]
191-
pub fn is_equal_approx(&self, other: &Self) -> bool {
192-
self.position.is_equal_approx(other.position) && self.size.is_equal_approx(other.size)
193-
}
194-
195189
/// Returns the normalized longest axis of the AABB.
196190
pub fn longest_axis(&self) -> Vector3 {
197191
match self.longest_axis_index() {
@@ -388,6 +382,16 @@ unsafe impl GodotFfi for Aabb {
388382
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
389383
}
390384

385+
impl ApproxEq for Aabb {
386+
/// Returns `true` if the two `Aabb`s are approximately equal, by calling `is_equal_approx` on
387+
/// `position` and `size`.
388+
#[inline]
389+
fn approx_eq(&self, other: &Self) -> bool {
390+
Vector3::approx_eq(&self.position, &other.position)
391+
&& Vector3::approx_eq(&self.size, &other.size)
392+
}
393+
}
394+
391395
#[cfg(test)]
392396
mod test {
393397
use super::*;

godot-core/src/builtin/basis.rs

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
use std::cmp::Ordering;
21
/*
32
* This Source Code Form is subject to the terms of the Mozilla Public
43
* License, v. 2.0. If a copy of the MPL was not distributed with this
54
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
65
*/
7-
use std::{fmt::Display, ops::*};
86

97
use godot_ffi as sys;
108
use sys::{ffi_methods, GodotFfi};
119

12-
use super::glam_helpers::{GlamConv, GlamType};
13-
use super::real_consts::FRAC_PI_2;
14-
use super::{math::*, Quaternion, Vector3};
15-
use super::{real, RMat3, RQuat, RVec2, RVec3};
10+
use crate::builtin::math::{is_equal_approx, lerp, ApproxEq, GlamConv, GlamType, CMP_EPSILON};
11+
use crate::builtin::real_consts::FRAC_PI_2;
12+
use crate::builtin::{real, Quaternion, RMat3, RQuat, RVec2, RVec3, Vector3};
13+
14+
use std::cmp::Ordering;
15+
use std::fmt::Display;
16+
use std::ops::{Mul, MulAssign};
1617

1718
/// A 3x3 matrix, typically used as an orthogonal basis for [`Transform3D`](crate::builtin::Transform3D).
1819
///
@@ -448,16 +449,6 @@ impl Basis {
448449
self.rows[0].is_finite() && self.rows[1].is_finite() && self.rows[2].is_finite()
449450
}
450451

451-
/// Returns `true` if this basis and `other` are approximately equal,
452-
/// by calling `is_equal_approx` on each row.
453-
///
454-
/// _Godot equivalent: `Basis.is_equal_approx(Basis b)`_
455-
pub fn is_equal_approx(&self, other: &Self) -> bool {
456-
self.rows[0].is_equal_approx(other.rows[0])
457-
&& self.rows[1].is_equal_approx(other.rows[1])
458-
&& self.rows[2].is_equal_approx(other.rows[2])
459-
}
460-
461452
/// Returns the first column of the matrix,
462453
///
463454
/// _Godot equivalent: `Basis.x`_
@@ -518,6 +509,15 @@ impl Display for Basis {
518509
}
519510
}
520511

512+
impl ApproxEq for Basis {
513+
/// Returns if this basis and `other` are approximately equal, by calling `is_equal_approx` on each row.
514+
fn approx_eq(&self, other: &Self) -> bool {
515+
Vector3::approx_eq(&self.rows[0], &other.rows[0])
516+
&& Vector3::approx_eq(&self.rows[1], &other.rows[1])
517+
&& Vector3::approx_eq(&self.rows[2], &other.rows[2])
518+
}
519+
}
520+
521521
impl GlamConv for Basis {
522522
type Glam = RMat3;
523523
}
@@ -693,45 +693,29 @@ mod test {
693693
fn consts_behavior_correct() {
694694
let v = Vector3::new(1.0, 2.0, 3.0);
695695

696-
assert_eq_approx!(Basis::IDENTITY * v, v, Vector3::is_equal_approx);
697-
assert_eq_approx!(
698-
Basis::FLIP_X * v,
699-
Vector3::new(-v.x, v.y, v.z),
700-
Vector3::is_equal_approx
701-
);
702-
assert_eq_approx!(
703-
Basis::FLIP_Y * v,
704-
Vector3::new(v.x, -v.y, v.z),
705-
Vector3::is_equal_approx
706-
);
707-
assert_eq_approx!(
708-
Basis::FLIP_Z * v,
709-
Vector3::new(v.x, v.y, -v.z),
710-
Vector3::is_equal_approx
711-
);
696+
assert_eq_approx!(Basis::IDENTITY * v, v);
697+
assert_eq_approx!(Basis::FLIP_X * v, Vector3::new(-v.x, v.y, v.z),);
698+
assert_eq_approx!(Basis::FLIP_Y * v, Vector3::new(v.x, -v.y, v.z),);
699+
assert_eq_approx!(Basis::FLIP_Z * v, Vector3::new(v.x, v.y, -v.z),);
712700
}
713701

714702
#[test]
715703
fn basic_rotation_correct() {
716704
assert_eq_approx!(
717705
Basis::from_axis_angle(Vector3::FORWARD, 0.0) * Vector3::RIGHT,
718706
Vector3::RIGHT,
719-
Vector3::is_equal_approx,
720707
);
721708
assert_eq_approx!(
722709
Basis::from_axis_angle(Vector3::FORWARD, FRAC_PI_2) * Vector3::RIGHT,
723710
Vector3::DOWN,
724-
Vector3::is_equal_approx,
725711
);
726712
assert_eq_approx!(
727713
Basis::from_axis_angle(Vector3::FORWARD, PI) * Vector3::RIGHT,
728714
Vector3::LEFT,
729-
Vector3::is_equal_approx,
730715
);
731716
assert_eq_approx!(
732717
Basis::from_axis_angle(Vector3::FORWARD, PI + FRAC_PI_2) * Vector3::RIGHT,
733718
Vector3::UP,
734-
Vector3::is_equal_approx,
735719
);
736720
}
737721

godot-core/src/builtin/color.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
*/
66

77
use crate::builtin::inner::InnerColor;
8+
use crate::builtin::math::ApproxEq;
89
use crate::builtin::GodotString;
10+
911
use godot_ffi as sys;
10-
use std::ops;
1112
use sys::{ffi_methods, GodotFfi};
1213

14+
use std::ops;
15+
1316
/// Color built-in type, in floating-point RGBA format.
1417
///
1518
/// Channel values are _typically_ in the range of 0 to 1, but this is not a requirement, and
@@ -287,12 +290,6 @@ impl Color {
287290
self.as_inner().to_html(false)
288291
}
289292

290-
/// Returns true if `self` and `to` are approximately equal, within the tolerance used by
291-
/// the global `is_equal_approx` function in GDScript.
292-
pub fn is_equal_approx(self, to: Color) -> bool {
293-
self.as_inner().is_equal_approx(to)
294-
}
295-
296293
/// Returns the color converted to a 32-bit integer (each component is 8 bits) with the given
297294
/// `order` of channels (from most to least significant byte).
298295
pub fn to_u32(self, order: ColorChannelOrder) -> u32 {
@@ -321,6 +318,13 @@ unsafe impl GodotFfi for Color {
321318
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
322319
}
323320

321+
impl ApproxEq for Color {
322+
fn approx_eq(&self, other: &Self) -> bool {
323+
// TODO(bromeon): re-implement in Rust
324+
self.as_inner().is_equal_approx(*other)
325+
}
326+
}
327+
324328
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
325329
pub enum ColorChannelOrder {
326330
/// RGBA channel order. Godot's default.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
7+
use crate::builtin::real;
8+
9+
// TODO(bromeon): test this against Godot's own is_equal_approx() implementation for equality-comparable built-in types (excl Callable/Rid/...)
10+
11+
/// Approximate equality-comparison of geometric types.
12+
///
13+
/// The implementation is specific to the type. It's mostly used for gdext-internal tests, but you may use it for your own code.
14+
/// Note that we give no guarantees about precision, and implementation can change at any time.
15+
///
16+
/// We currently also do not guarantee that this gives the same results as Godot's own `is_equal_approx()` function; although this may
17+
/// be the goal in the future.
18+
pub trait ApproxEq: PartialEq {
19+
fn approx_eq(&self, other: &Self) -> bool;
20+
}
21+
22+
impl ApproxEq for real {
23+
fn approx_eq(&self, other: &Self) -> bool {
24+
crate::builtin::math::is_equal_approx(*self, *other)
25+
}
26+
}
27+
28+
/// Asserts that two values are approximately equal
29+
///
30+
/// For comparison, this uses `ApproxEq::approx_eq` by default, or the provided `fn = ...` function.
31+
#[macro_export]
32+
macro_rules! assert_eq_approx {
33+
($actual:expr, $expected:expr, fn = $func:expr $(,)?) => {
34+
match ($actual, $expected) {
35+
(a, b) => assert!(($func)(&a, &b), "\n left: {:?},\n right: {:?}", $actual, $expected)
36+
}
37+
};
38+
($actual:expr, $expected:expr, fn = $func:expr, $($t:tt)+) => {
39+
match ($actual, $expected) {
40+
(a, b) => assert!(($func)(&a, &b), "\n left: {:?},\n right: {:?}{}", $actual, $expected, format_args!($($t)+) )
41+
}
42+
};
43+
($actual:expr, $expected:expr $(,)?) => {
44+
match ($actual, $expected) {
45+
(a, b) => assert!($crate::builtin::math::ApproxEq::approx_eq(&a, &b), "\n left: {:?},\n right: {:?}", $actual, $expected),
46+
// (a, b) => $crate::assert_eq_approx!($actual, $expected, fn = $crate::builtin::ApproxEq::approx_eq),
47+
}
48+
};
49+
($actual:expr, $expected:expr, $($t:tt)+) => {
50+
match ($actual, $expected) {
51+
(a, b) => assert!($crate::builtin::math::ApproxEq::approx_eq(&a, &b), "\n left: {:?},\n right: {:?},\n{}", $actual, $expected, format_args!($($t)+)),
52+
// (a, b) => $crate::assert_eq_approx!($actual, $expected, fn = $crate::builtin::ApproxEq::approx_eq, $($t)+),
53+
}
54+
};
55+
}
56+
57+
/// Asserts that two values are not approximately equal, using the provided
58+
/// `func` for equality checking.
59+
#[macro_export]
60+
macro_rules! assert_ne_approx {
61+
($actual:expr, $expected:expr, fn = $func:expr $(, $($t:tt)* )?) => {
62+
#[allow(clippy::redundant_closure_call)]
63+
{
64+
$crate::assert_eq_approx!($actual, $expected, fn = |a,b| !($func)(a, b) $(, $($t)* )?)
65+
}
66+
};
67+
68+
($actual:expr, $expected:expr $(, $($t:tt)* )?) => {
69+
#[allow(clippy::redundant_closure_call)]
70+
{
71+
$crate::assert_eq_approx!($actual, $expected, fn = |a, b| !$crate::builtin::math::ApproxEq::approx_eq(a, b) $(, $($t)* )?)
72+
}
73+
};
74+
}

godot-core/src/builtin/glam_helpers.rs renamed to godot-core/src/builtin/math/glam_helpers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// self.glam().dot(b.glam())
1616
// GlamType::dot(self.glam(), b.glam())
1717

18-
use super::real;
18+
use crate::builtin::real;
1919

2020
pub(crate) trait GlamConv {
2121
type Glam: GlamType<Mapped = Self>;

0 commit comments

Comments
 (0)