Skip to content

Commit e6d7c23

Browse files
committed
Implement XformInv<T> for Transforms.
- Post-CR fixes.
1 parent b21c46d commit e6d7c23

File tree

8 files changed

+186
-144
lines changed

8 files changed

+186
-144
lines changed

godot-core/src/builtin/basis.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,13 @@ impl XformInv<Vector3> for Basis {
633633
/// Inversely transforms given [`Vector3`] by this basis,
634634
/// under the assumption that the basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not).
635635
///
636-
/// `basis.xform_inv(vector)` is equivalent to `basis.transposed() * vector`. See [`Basis::transposed()`].
636+
/// Since given basis is assumed to be orthonormal (i.e. it is both orthogonal – with all axis perpendicular to each other – and all the axis are normalized),
637+
/// `basis.transposed()` (matrix flipped over its diagonal) is equal to `basis.inverse()`
638+
/// (i.e. `basis * basis.transposed() == basis * basis.inverse() == Basis::Identity`),
639+
/// thus `basis.xform_inv(vector)` is equivalent both to `basis.transposed() * vector` and `basis.inverse() * vector`.
640+
///
641+
/// See [`Basis::transposed()`] and [Godot docs (stable) for `Basis`](https://docs.godotengine.org/en/stable/classes/class_basis.html)
642+
/// with following [Matrices and Transform tutorial](https://docs.godotengine.org/en/stable/tutorials/math/matrices_and_transforms.html).
637643
///
638644
/// For transforming by inverse of a non-orthonormal basis (e.g. with scaling) `basis.inverse() * vector` can be used instead. See [`Basis::inverse()`].
639645
///

godot-core/src/builtin/math/xform.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
/// See also: [`Transform2D`](crate::builtin::Transform2D), [`Transform3D`](crate::builtin::Transform3D), [`Basis`](crate::builtin::Basis).
1111
///
1212
/// _Godot equivalent: `rhs * mat`_
13-
pub trait XformInv<T> {
13+
pub trait XformInv<T>: std::ops::Mul<T> {
1414
fn xform_inv(&self, rhs: T) -> T;
1515
}

godot-core/src/builtin/transform2d.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ use std::ops::{Mul, MulAssign};
3939
///
4040
/// # Transform operations
4141
///
42-
/// | Operation | Transform2D | Notes |
43-
/// |--------------------------------|--------------------------------|-------------------------------------|
44-
/// | Apply | transform * v | Supports [`Rect2`] and [`Vector2`]. |
45-
/// | Apply inverse | transform.xform_inv(v) | Supports [`Rect2`] and [`Vector2`]. |
46-
/// | Apply, no translate | transform.basis_xform(v) | Supports [`Vector2`]. |
47-
/// | Apply inverse, no translate | transform.basis_xform_inv(v) | Supports [`Vector2`]. |
42+
/// | Operation | Transform2D | Notes |
43+
/// |--------------------------------|----------------------------------|-------------------------------------|
44+
/// | Apply | `transform * v` | Supports [`Rect2`] and [`Vector2`]. |
45+
/// | Apply inverse | `transform.xform_inv(v)` | Supports [`Rect2`] and [`Vector2`]. |
46+
/// | Apply, no translate | `transform.basis_xform(v)` | Supports [`Vector2`]. |
47+
/// | Apply inverse, no translate | `transform.basis_xform_inv(v)` | Supports [`Vector2`]. |
4848
///
4949
/// # Godot docs
5050
///
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) godot-rust; Bromeon and contributors.
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
//! Functions shared between various built-in tests.
9+
10+
use godot::builtin::VariantOperator;
11+
use godot::meta::{FromGodot, ToGodot};
12+
use godot::private::class_macros::assert_eq_approx;
13+
14+
/// Asserts that result of evaluated operation via variants and expected one are approximately equal.
15+
///
16+
/// Used to check if operations performed in Godot Rust yield the same result as ones performed via Godot runtime.
17+
pub(crate) fn assert_evaluate_approx_eq<T, U, E>(
18+
left: T,
19+
right: U,
20+
op: VariantOperator,
21+
expected: E,
22+
) where
23+
T: ToGodot,
24+
U: ToGodot,
25+
E: FromGodot + std::fmt::Debug + godot::builtin::math::ApproxEq + Copy,
26+
{
27+
let lhs = left
28+
.to_variant()
29+
.evaluate(&right.to_variant(), op)
30+
.expect("Result of evaluation can't be null!")
31+
.to::<E>();
32+
33+
assert_eq_approx!(lhs, expected);
34+
}

itest/rust/src/builtin_tests/geometry/basis_test.rs

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8+
use crate::builtin_tests::common::assert_evaluate_approx_eq;
89
use godot::builtin::inner::InnerBasis;
910
use godot::builtin::math::assert_eq_approx;
10-
use godot::builtin::{real, Basis, EulerOrder, RealConv, VariantOperator, Vector3};
11-
use godot::meta::ToGodot;
11+
use godot::builtin::{real, Basis, EulerOrder, RealConv, VariantOperator, Vector3, XformInv};
1212

1313
use crate::framework::itest;
1414

@@ -20,22 +20,37 @@ const TEST_BASIS: Basis = Basis::from_rows(
2020

2121
#[itest]
2222
fn basis_multiply_same() {
23-
let rust_res = TEST_BASIS * Basis::IDENTITY;
24-
let godot_res = TEST_BASIS
25-
.to_variant()
26-
.evaluate(&Basis::IDENTITY.to_variant(), VariantOperator::MULTIPLY)
27-
.unwrap()
28-
.to::<Basis>();
29-
assert_eq_approx!(rust_res, godot_res);
30-
31-
let rhs = Basis::from_axis_angle(Vector3::new(1.0, 2.0, 3.0).normalized(), 0.5);
32-
let rust_res = TEST_BASIS * rhs;
33-
let godot_res = TEST_BASIS
34-
.to_variant()
35-
.evaluate(&rhs.to_variant(), VariantOperator::MULTIPLY)
36-
.unwrap()
37-
.to::<Basis>();
38-
assert_eq_approx!(rust_res, godot_res);
23+
// operator: Basis * Identity
24+
assert_evaluate_approx_eq(
25+
TEST_BASIS,
26+
Basis::IDENTITY,
27+
VariantOperator::MULTIPLY,
28+
TEST_BASIS * Basis::IDENTITY,
29+
);
30+
31+
// operator: Basis * rotated Basis
32+
let rotated_basis = Basis::from_axis_angle(Vector3::new(1.0, 2.0, 3.0).normalized(), 0.5);
33+
assert_evaluate_approx_eq(
34+
TEST_BASIS,
35+
rotated_basis,
36+
VariantOperator::MULTIPLY,
37+
TEST_BASIS * rotated_basis,
38+
);
39+
40+
// orthonormalized
41+
let orthonormalized_basis = TEST_BASIS.orthonormalized();
42+
assert_evaluate_approx_eq(
43+
orthonormalized_basis,
44+
orthonormalized_basis.inverse(),
45+
VariantOperator::MULTIPLY,
46+
Basis::IDENTITY,
47+
);
48+
assert_evaluate_approx_eq(
49+
orthonormalized_basis,
50+
orthonormalized_basis.transposed(),
51+
VariantOperator::MULTIPLY,
52+
Basis::IDENTITY,
53+
);
3954
}
4055

4156
#[itest]
@@ -161,6 +176,34 @@ fn basis_equiv() {
161176
)
162177
}
163178

179+
#[itest]
180+
fn basis_xform_equiv() {
181+
let orthonormalized_basis = TEST_BASIS.orthonormalized();
182+
let vec = Vector3::new(1.0, 2.0, 3.0);
183+
184+
// operator: Basis * Vector3
185+
assert_evaluate_approx_eq(
186+
orthonormalized_basis,
187+
vec,
188+
VariantOperator::MULTIPLY,
189+
orthonormalized_basis * vec,
190+
);
191+
}
192+
193+
#[itest]
194+
fn basis_xform_inv_equiv() {
195+
let orthonormalized_basis = TEST_BASIS.orthonormalized();
196+
let vec = Vector3::new(1.0, 2.0, 3.0);
197+
198+
// operator: Vector3 * Basis
199+
assert_evaluate_approx_eq(
200+
vec,
201+
orthonormalized_basis,
202+
VariantOperator::MULTIPLY,
203+
orthonormalized_basis.xform_inv(vec),
204+
);
205+
}
206+
164207
fn deg_to_rad(rotation: Vector3) -> Vector3 {
165208
Vector3::new(
166209
rotation.x.to_radians(),

itest/rust/src/builtin_tests/geometry/transform2d_test.rs

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
use crate::framework::itest;
99

10+
use crate::builtin_tests::common::assert_evaluate_approx_eq;
1011
use godot::builtin::inner::InnerTransform2D;
1112
use godot::builtin::{real, RealConv, Rect2, Transform2D, VariantOperator, Vector2, XformInv};
12-
use godot::meta::ToGodot;
1313
use godot::private::class_macros::assert_eq_approx;
1414

1515
const TEST_TRANSFORM: Transform2D = Transform2D::from_cols(
@@ -79,81 +79,62 @@ fn transform2d_determinant() {
7979
fn transform2d_xform_equiv() {
8080
let vec = Vector2::new(1.0, 2.0);
8181

82-
assert_eq_approx!(
82+
// operator: Transform2D * Vector2
83+
assert_evaluate_approx_eq(
84+
TEST_TRANSFORM,
85+
vec,
86+
VariantOperator::MULTIPLY,
8387
TEST_TRANSFORM * vec,
84-
TEST_TRANSFORM
85-
.to_variant()
86-
.evaluate(&vec.to_variant(), VariantOperator::MULTIPLY)
87-
.unwrap()
88-
.to::<Vector2>(),
89-
"operator: Transform2D * Vector2"
9088
);
9189

9290
let rect_2 = Rect2::new(Vector2::new(1.0, 2.0), Vector2::new(3.0, 4.0));
9391

94-
assert_eq_approx!(
92+
// operator: Transform2D * Rect2 (1)
93+
assert_evaluate_approx_eq(
94+
TEST_TRANSFORM,
95+
rect_2,
96+
VariantOperator::MULTIPLY,
9597
TEST_TRANSFORM * rect_2,
96-
TEST_TRANSFORM
97-
.to_variant()
98-
.evaluate(&rect_2.to_variant(), VariantOperator::MULTIPLY)
99-
.unwrap()
100-
.to::<Rect2>(),
101-
"operator: Transform2D * Rect2 (1)"
10298
);
10399

104-
assert_eq_approx!(
105-
TEST_TRANSFORM.rotated(0.8) * rect_2,
106-
TEST_TRANSFORM
107-
.rotated(0.8)
108-
.to_variant()
109-
.evaluate(&rect_2.to_variant(), VariantOperator::MULTIPLY)
110-
.unwrap()
111-
.to::<Rect2>(),
112-
"operator: Transform2D * Rect2 (2)"
100+
// "operator: Transform2D * Rect2 (2)"
101+
let transform_rotated = TEST_TRANSFORM_ORTHONORMAL.rotated(0.8);
102+
assert_evaluate_approx_eq(
103+
transform_rotated,
104+
rect_2,
105+
VariantOperator::MULTIPLY,
106+
transform_rotated * rect_2,
113107
);
114108
}
115109

116110
#[itest]
117111
fn transform2d_xform_inv_equiv() {
118112
let vec = Vector2::new(1.0, 2.0);
119113

120-
assert_eq_approx!(
114+
// operator: Vector2 * Transform2D
115+
assert_evaluate_approx_eq(
116+
vec,
117+
TEST_TRANSFORM_ORTHONORMAL,
118+
VariantOperator::MULTIPLY,
121119
TEST_TRANSFORM_ORTHONORMAL.xform_inv(vec),
122-
vec.to_variant()
123-
.evaluate(
124-
&TEST_TRANSFORM_ORTHONORMAL.to_variant(),
125-
VariantOperator::MULTIPLY
126-
)
127-
.unwrap()
128-
.to::<Vector2>(),
129-
"operator: Transform2D * Vector2"
130120
);
131121

132122
let rect_2 = Rect2::new(Vector2::new(1.0, 2.0), Vector2::new(3.0, 4.0));
133123

134-
assert_eq_approx!(
124+
// operator: Rect2 * Transform2D (1)
125+
assert_evaluate_approx_eq(
126+
rect_2,
127+
TEST_TRANSFORM_ORTHONORMAL,
128+
VariantOperator::MULTIPLY,
135129
TEST_TRANSFORM_ORTHONORMAL.xform_inv(rect_2),
136-
rect_2
137-
.to_variant()
138-
.evaluate(
139-
&TEST_TRANSFORM_ORTHONORMAL.to_variant(),
140-
VariantOperator::MULTIPLY
141-
)
142-
.unwrap()
143-
.to::<Rect2>(),
144-
"operator: Transform2D * Rect2 (1)"
145130
);
146131

147-
assert_eq_approx!(
148-
TEST_TRANSFORM_ORTHONORMAL.rotated(0.8).xform_inv(rect_2),
149-
rect_2
150-
.to_variant()
151-
.evaluate(
152-
&TEST_TRANSFORM_ORTHONORMAL.rotated(0.8).to_variant(),
153-
VariantOperator::MULTIPLY
154-
)
155-
.unwrap()
156-
.to::<Rect2>(),
157-
"operator: Transform2D * Rect2 (2)"
132+
// operator: Rect2 * Transform2D (2)
133+
let transform_rotated = TEST_TRANSFORM_ORTHONORMAL.rotated(0.8);
134+
assert_evaluate_approx_eq(
135+
rect_2,
136+
transform_rotated,
137+
VariantOperator::MULTIPLY,
138+
transform_rotated.xform_inv(rect_2),
158139
);
159140
}

0 commit comments

Comments
 (0)