Skip to content

Commit b3b9e0e

Browse files
bors[bot]lilizoey
andauthored
Merge #128
128: Tweaks to glamconv and indexing for vectors r=Bromeon a=sayaks Add bool as glam type Add glamconv/glamtype for Vector2/3/4 Add assert_eq/ne_approx Add lerp_angle Split from #124 Co-authored-by: Lili Zoey <[email protected]>
2 parents 89c0e31 + 91cc0a3 commit b3b9e0e

File tree

5 files changed

+175
-4
lines changed

5 files changed

+175
-4
lines changed

godot-core/src/builtin/glam_helpers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,5 @@ macro_rules! impl_glam_map_self {
7171
}
7272

7373
impl_glam_map_self!(f32);
74+
impl_glam_map_self!(bool);
7475
impl_glam_map_self!((f32, f32, f32));

godot-core/src/builtin/math.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7+
use std::f32::consts::TAU;
8+
79
pub const CMP_EPSILON: f32 = 0.00001;
810

911
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
@@ -119,3 +121,99 @@ pub fn cubic_interpolate_in_time(
119121
let b2 = lerp(a2, a3, if post_t == 0.0 { 1.0 } else { t / post_t });
120122
lerp(b1, b2, if to_t == 0.0 { 0.5 } else { t / to_t })
121123
}
124+
125+
/// Linearly interpolates between two angles (in radians) by a `weight` value
126+
/// between 0.0 and 1.0.
127+
///
128+
/// Similar to [`lerp`], but interpolates correctly when the angles wrap around
129+
/// [`TAU`].
130+
///
131+
/// The resulting angle is not normalized.
132+
///
133+
/// Note: This function lerps through the shortest path between `from` and
134+
/// `to`. However, when these two angles are approximately `PI + k * TAU` apart
135+
/// for any integer `k`, it's not obvious which way they lerp due to
136+
/// floating-point precision errors. For example, `lerp_angle(0.0, PI, weight)`
137+
/// lerps clockwise, while `lerp_angle(0.0, PI + 3.0 * TAU, weight)` lerps
138+
/// counter-clockwise.
139+
///
140+
/// _Godot equivalent: @GlobalScope.lerp_angle()_
141+
pub fn lerp_angle(from: f32, to: f32, weight: f32) -> f32 {
142+
let difference = (to - from) % TAU;
143+
let distance = (2.0 * difference) % TAU - difference;
144+
from + distance * weight
145+
}
146+
147+
/// Asserts that two values are approximately equal, using the provided `func`
148+
/// for equality checking.
149+
#[macro_export]
150+
macro_rules! assert_eq_approx {
151+
($a:expr, $b:expr, $func:expr $(,)?) => {
152+
match ($a, $b) {
153+
(a, b) => {
154+
assert!(($func)(a,b), "\n left: {:?},\n right: {:?}", $a, $b);
155+
}
156+
}
157+
};
158+
($a:expr, $b:expr, $func:expr, $($t:tt)+) => {
159+
match ($a, $b) {
160+
(a, b) => {
161+
assert!(($func)(a,b), "\n left: {:?},\n right: {:?},\n{}", $a, $b, format_args!($($t)+));
162+
}
163+
}
164+
};
165+
}
166+
167+
/// Asserts that two values are not approximately equal, using the provided
168+
/// `func` for equality checking.
169+
#[macro_export]
170+
macro_rules! assert_ne_approx {
171+
($a:expr, $b:expr, $func:expr $(, $($t:tt)*)?) => {
172+
#[allow(clippy::redundant_closure_call)]
173+
{
174+
assert_eq_approx!($a, $b, |a,b| !($func)(a,b) $(, $($t)*)?)
175+
}
176+
};
177+
}
178+
179+
#[cfg(test)]
180+
mod test {
181+
use std::f32::consts::{FRAC_PI_2, PI};
182+
183+
use super::*;
184+
185+
#[test]
186+
fn equal_approx() {
187+
assert_eq_approx!(1.0, 1.000001, is_equal_approx);
188+
assert_ne_approx!(1.0, 2.0, is_equal_approx);
189+
assert_eq_approx!(1.0, 1.000001, is_equal_approx, "Message {}", "formatted");
190+
assert_ne_approx!(1.0, 2.0, is_equal_approx, "Message {}", "formatted");
191+
}
192+
193+
#[test]
194+
#[should_panic(expected = "I am inside format")]
195+
fn eq_approx_fail_with_message() {
196+
assert_eq_approx!(1.0, 2.0, is_equal_approx, "I am inside {}", "format");
197+
}
198+
199+
#[test]
200+
fn lerp_angle_test() {
201+
assert_eq_approx!(lerp_angle(0.0, PI, 0.5), -FRAC_PI_2, is_equal_approx);
202+
assert_eq_approx!(
203+
lerp_angle(0.0, PI + 3.0 * TAU, 0.5),
204+
FRAC_PI_2,
205+
is_equal_approx
206+
);
207+
let angle = PI * 2.0 / 3.0;
208+
assert_eq_approx!(
209+
lerp_angle(-5.0 * TAU, angle + 3.0 * TAU, 0.5).sin(),
210+
(angle / 2.0).sin(),
211+
is_equal_approx
212+
);
213+
assert_eq_approx!(
214+
lerp_angle(-5.0 * TAU, angle + 3.0 * TAU, 0.5).cos(),
215+
(angle / 2.0).cos(),
216+
is_equal_approx
217+
);
218+
}
219+
}

godot-core/src/builtin/vector2.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66
use std::fmt;
77
use std::ops::*;
88

9+
use glam::Vec2;
910
use godot_ffi as sys;
1011
use sys::{ffi_methods, GodotFfi};
1112

1213
use crate::builtin::math::*;
1314
use crate::builtin::{inner, Vector2i};
1415

16+
use super::glam_helpers::GlamConv;
17+
use super::glam_helpers::GlamType;
18+
1519
/// Vector used for 2D math using floating point coordinates.
1620
///
1721
/// 2-element structure that can be used to represent positions in 2D space or any other pair of
@@ -97,6 +101,10 @@ impl Vector2 {
97101
self.x / self.y
98102
}
99103

104+
pub fn lerp(self, other: Self, weight: f32) -> Self {
105+
Self::new(lerp(self.x, other.x, weight), lerp(self.y, other.y, weight))
106+
}
107+
100108
pub fn bezier_derivative(self, control_1: Self, control_2: Self, end: Self, t: f32) -> Self {
101109
let x = bezier_derivative(self.x, control_1.x, control_2.x, end.x, t);
102110
let y = bezier_derivative(self.y, control_1.y, control_2.y, end.y, t);
@@ -199,10 +207,6 @@ impl Vector2 {
199207
self.to_glam().length_squared()
200208
}
201209

202-
pub fn lerp(self, to: Self, weight: f32) -> Self {
203-
Self::from_glam(self.to_glam().lerp(to.to_glam(), weight))
204-
}
205-
206210
pub fn limit_length(self, length: Option<f32>) -> Self {
207211
Self::from_glam(self.to_glam().clamp_length_max(length.unwrap_or(1.0)))
208212
}
@@ -323,3 +327,19 @@ pub enum Vector2Axis {
323327
impl GodotFfi for Vector2Axis {
324328
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
325329
}
330+
331+
impl GlamType for Vec2 {
332+
type Mapped = Vector2;
333+
334+
fn to_front(&self) -> Self::Mapped {
335+
Vector2::new(self.x, self.y)
336+
}
337+
338+
fn from_front(mapped: &Self::Mapped) -> Self {
339+
Vec2::new(mapped.x, mapped.y)
340+
}
341+
}
342+
343+
impl GlamConv for Vector2 {
344+
type Glam = Vec2;
345+
}

godot-core/src/builtin/vector3.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@ use std::ops::*;
77

88
use std::fmt;
99

10+
use glam::f32::Vec3;
11+
use glam::Vec3A;
1012
use godot_ffi as sys;
1113
use sys::{ffi_methods, GodotFfi};
1214

1315
use crate::builtin::math::*;
1416
use crate::builtin::Vector3i;
1517

18+
use super::glam_helpers::GlamConv;
19+
use super::glam_helpers::GlamType;
20+
1621
/// Vector used for 3D math using floating point coordinates.
1722
///
1823
/// 3-element structure that can be used to represent positions in 3D space or any other triple of
@@ -336,3 +341,31 @@ pub enum Vector3Axis {
336341
impl GodotFfi for Vector3Axis {
337342
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
338343
}
344+
345+
impl GlamType for Vec3 {
346+
type Mapped = Vector3;
347+
348+
fn to_front(&self) -> Self::Mapped {
349+
Vector3::new(self.x, self.y, self.z)
350+
}
351+
352+
fn from_front(mapped: &Self::Mapped) -> Self {
353+
Vec3::new(mapped.x, mapped.y, mapped.z)
354+
}
355+
}
356+
357+
impl GlamType for Vec3A {
358+
type Mapped = Vector3;
359+
360+
fn to_front(&self) -> Self::Mapped {
361+
Vector3::new(self.x, self.y, self.z)
362+
}
363+
364+
fn from_front(mapped: &Self::Mapped) -> Self {
365+
Vec3A::new(mapped.x, mapped.y, mapped.z)
366+
}
367+
}
368+
369+
impl GlamConv for Vector3 {
370+
type Glam = Vec3;
371+
}

godot-core/src/builtin/vector4.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66

77
use std::fmt;
88

9+
use glam::Vec4;
910
use godot_ffi as sys;
1011
use sys::{ffi_methods, GodotFfi};
1112

1213
use crate::builtin::Vector4i;
1314

15+
use super::glam_helpers::{GlamConv, GlamType};
16+
1417
/// Vector used for 4D math using floating point coordinates.
1518
///
1619
/// 4-element structure that can be used to represent any quadruplet of numeric values.
@@ -107,3 +110,19 @@ pub enum Vector4Axis {
107110
impl GodotFfi for Vector4Axis {
108111
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
109112
}
113+
114+
impl GlamType for Vec4 {
115+
type Mapped = Vector4;
116+
117+
fn to_front(&self) -> Self::Mapped {
118+
Vector4::new(self.x, self.y, self.z, self.w)
119+
}
120+
121+
fn from_front(mapped: &Self::Mapped) -> Self {
122+
Vec4::new(mapped.x, mapped.y, mapped.z, mapped.w)
123+
}
124+
}
125+
126+
impl GlamConv for Vector4 {
127+
type Glam = Vec4;
128+
}

0 commit comments

Comments
 (0)