Skip to content

Commit 6c92a05

Browse files
authored
Merge pull request #721 from joriskleiber/refactor-vectors
Refactor vector_macros to consistently generate all vector types
2 parents e1f1ce3 + e9d5b92 commit 6c92a05

File tree

17 files changed

+2329
-802
lines changed

17 files changed

+2329
-802
lines changed

godot-core/src/builtin/aabb.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,18 @@ impl Aabb {
189189

190190
/// Returns the normalized longest axis of the AABB.
191191
#[inline]
192-
pub fn longest_axis(&self) -> Vector3 {
193-
match self.longest_axis_index() {
192+
pub fn longest_axis(&self) -> Option<Vector3> {
193+
self.longest_axis_index().map(|axis| match axis {
194194
Vector3Axis::X => Vector3::RIGHT,
195195
Vector3Axis::Y => Vector3::UP,
196196
Vector3Axis::Z => Vector3::BACK,
197-
}
197+
})
198198
}
199199

200200
/// Returns the index of the longest axis of the AABB (according to Vector3's AXIS_* constants).
201201
#[inline]
202-
pub fn longest_axis_index(&self) -> Vector3Axis {
203-
self.size.max_axis_index()
202+
pub fn longest_axis_index(&self) -> Option<Vector3Axis> {
203+
self.size.max_axis()
204204
}
205205

206206
/// Returns the scalar length of the longest axis of the AABB.
@@ -211,18 +211,18 @@ impl Aabb {
211211

212212
/// Returns the normalized shortest axis of the AABB.
213213
#[inline]
214-
pub fn shortest_axis(&self) -> Vector3 {
215-
match self.shortest_axis_index() {
214+
pub fn shortest_axis(&self) -> Option<Vector3> {
215+
self.shortest_axis_index().map(|axis| match axis {
216216
Vector3Axis::X => Vector3::RIGHT,
217217
Vector3Axis::Y => Vector3::UP,
218218
Vector3Axis::Z => Vector3::BACK,
219-
}
219+
})
220220
}
221221

222222
/// Returns the index of the shortest axis of the AABB (according to Vector3::AXIS* enum).
223223
#[inline]
224-
pub fn shortest_axis_index(&self) -> Vector3Axis {
225-
self.size.min_axis_index()
224+
pub fn shortest_axis_index(&self) -> Option<Vector3Axis> {
225+
self.size.min_axis()
226226
}
227227

228228
/// Returns the scalar length of the shortest axis of the AABB.
@@ -436,12 +436,12 @@ mod test {
436436
size: Vector3::new(4.0, 6.0, 8.0),
437437
};
438438

439-
assert_eq!(aabb.shortest_axis(), Vector3::RIGHT);
440-
assert_eq!(aabb.longest_axis(), Vector3::BACK);
439+
assert_eq!(aabb.shortest_axis(), Some(Vector3::RIGHT));
440+
assert_eq!(aabb.longest_axis(), Some(Vector3::BACK));
441441
assert_eq!(aabb.shortest_axis_size(), 4.0);
442442
assert_eq!(aabb.longest_axis_size(), 8.0);
443-
assert_eq!(aabb.shortest_axis_index(), Vector3Axis::X);
444-
assert_eq!(aabb.longest_axis_index(), Vector3Axis::Z);
443+
assert_eq!(aabb.shortest_axis_index(), Some(Vector3Axis::X));
444+
assert_eq!(aabb.longest_axis_index(), Some(Vector3Axis::Z));
445445
}
446446

447447
#[test]

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub trait FloatExt: private::Sealed + Copy {
3232
/// Check if `self` is within [`Self::CMP_EPSILON`] of `0.0`.
3333
fn is_zero_approx(self) -> bool;
3434

35+
/// Returns the floating-point modulus of `self` divided by `pmod`, wrapping equally in positive and negative.
3536
fn fposmod(self, pmod: Self) -> Self;
3637

3738
/// Returns the multiple of `step` that is closest to `self`.

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

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

8+
use core::cmp::Ordering;
89
use godot_ffi as sys;
910
use sys::{ffi_methods, GodotFfi};
1011

11-
use crate::builtin::inner;
1212
use crate::builtin::math::{FloatExt, GlamConv, GlamType};
1313
use crate::builtin::vectors::Vector2Axis;
14-
use crate::builtin::{real, RAffine2, RVec2, Vector2i};
14+
use crate::builtin::{inner, real, RAffine2, RVec2, Vector2i};
1515

1616
use std::fmt;
1717

@@ -36,172 +36,100 @@ pub struct Vector2 {
3636
pub y: real,
3737
}
3838

39-
impl Vector2 {
40-
/// Vector with all components set to `0.0`.
41-
pub const ZERO: Self = Self::splat(0.0);
42-
43-
/// Vector with all components set to `1.0`.
44-
pub const ONE: Self = Self::splat(1.0);
45-
46-
/// Vector with all components set to `real::INFINITY`.
47-
pub const INF: Self = Self::splat(real::INFINITY);
48-
49-
/// Unit vector in -X direction (right in 2D coordinate system).
50-
pub const LEFT: Self = Self::new(-1.0, 0.0);
51-
52-
/// Unit vector in +X direction (right in 2D coordinate system).
53-
pub const RIGHT: Self = Self::new(1.0, 0.0);
54-
55-
/// Unit vector in -Y direction (up in 2D coordinate system).
56-
pub const UP: Self = Self::new(0.0, -1.0);
39+
impl_vector_operators!(Vector2, real, (x, y));
5740

58-
/// Unit vector in +Y direction (down in 2D coordinate system).
59-
pub const DOWN: Self = Self::new(0.0, 1.0);
41+
impl_vector_consts!(Vector2, real);
42+
impl_float_vector_consts!(Vector2);
43+
impl_vector2x_consts!(Vector2, real);
6044

61-
/// Constructs a new `Vector2` from the given `x` and `y`.
62-
pub const fn new(x: real, y: real) -> Self {
63-
Self { x, y }
64-
}
65-
66-
/// Constructs a new `Vector2` with both components set to `v`.
67-
pub const fn splat(v: real) -> Self {
68-
Self::new(v, v)
69-
}
45+
impl_vector_fns!(Vector2, RVec2, real, (x, y));
46+
impl_float_vector_fns!(Vector2, (x, y));
47+
impl_vector2x_fns!(Vector2, real);
48+
impl_vector2_vector3_fns!(Vector2, (x, y));
7049

50+
impl Vector2 {
7151
/// Constructs a new `Vector2` from a [`Vector2i`].
52+
#[inline]
7253
pub const fn from_vector2i(v: Vector2i) -> Self {
7354
Self {
7455
x: v.x as real,
7556
y: v.y as real,
7657
}
7758
}
7859

79-
/// Converts the corresponding `glam` type to `Self`.
80-
fn from_glam(v: RVec2) -> Self {
81-
Self::new(v.x, v.y)
82-
}
83-
84-
/// Converts `self` to the corresponding `glam` type.
85-
fn to_glam(self) -> RVec2 {
86-
RVec2::new(self.x, self.y)
60+
#[doc(hidden)]
61+
#[inline]
62+
pub fn as_inner(&self) -> inner::InnerVector2 {
63+
inner::InnerVector2::from_outer(self)
8764
}
8865

66+
/// Returns this vector's angle with respect to the positive X axis, or `(1.0, 0.0)` vector, in radians.
67+
///
68+
/// For example, `Vector2::RIGHT.angle()` will return zero, `Vector2::DOWN.angle()` will return `PI / 2` (a quarter turn, or 90 degrees),
69+
/// and `Vector2::new(1.0, -1.0).angle()` will return `-PI / 4` (a negative eighth turn, or -45 degrees).
70+
///
71+
/// [Illustration of the returned angle.](https://raw.githubusercontent.com/godotengine/godot-docs/master/img/vector2_angle.png)
72+
///
73+
/// Equivalent to the result of `y.atan2(x)`.
74+
#[inline]
8975
pub fn angle(self) -> real {
9076
self.y.atan2(self.x)
9177
}
9278

93-
pub fn angle_to(self, to: Self) -> real {
94-
self.to_glam().angle_between(to.to_glam())
95-
}
96-
79+
/// Returns the angle to the given vector, in radians.
80+
///
81+
/// [Illustration of the returned angle.](https://raw.githubusercontent.com/godotengine/godot-docs/master/img/vector2_angle_to.png)
82+
#[inline]
9783
pub fn angle_to_point(self, to: Self) -> real {
9884
(to - self).angle()
9985
}
10086

101-
pub fn aspect(self) -> real {
102-
self.x / self.y
103-
}
104-
105-
pub fn bounce(self, normal: Self) -> Self {
106-
-self.reflect(normal)
107-
}
108-
109-
pub fn ceil(self) -> Self {
110-
Self::from_glam(self.to_glam().ceil())
111-
}
112-
113-
pub fn clamp(self, min: Self, max: Self) -> Self {
114-
Self::from_glam(self.to_glam().clamp(min.to_glam(), max.to_glam()))
115-
}
116-
87+
/// Returns the 2D analog of the cross product for this vector and `with`.
88+
///
89+
/// This is the signed area of the parallelogram formed by the two vectors. If the second vector is clockwise from the first vector,
90+
/// then the cross product is the positive area. If counter-clockwise, the cross product is the negative area. If the two vectors are
91+
/// parallel this returns zero, making it useful for testing if two vectors are parallel.
92+
///
93+
/// Note: Cross product is not defined in 2D mathematically. This method embeds the 2D vectors in the XY plane of 3D space and uses
94+
/// their cross product's Z component as the analog.
95+
#[inline]
11796
pub fn cross(self, with: Self) -> real {
11897
self.to_glam().perp_dot(with.to_glam())
11998
}
12099

121-
pub fn direction_to(self, to: Self) -> Self {
122-
(to - self).normalized()
123-
}
124-
125-
pub fn distance_squared_to(self, to: Self) -> real {
126-
(to - self).length_squared()
127-
}
128-
129-
pub fn distance_to(self, to: Self) -> real {
130-
(to - self).length()
131-
}
132-
133-
pub fn dot(self, other: Self) -> real {
134-
self.to_glam().dot(other.to_glam())
135-
}
136-
137-
pub fn floor(self) -> Self {
138-
Self::from_glam(self.to_glam().floor())
139-
}
140-
100+
/// Creates a unit Vector2 rotated to the given `angle` in radians. This is equivalent to doing `Vector2::new(angle.cos(), angle.sin())`
101+
/// or `Vector2::RIGHT.rotated(angle)`.
102+
///
103+
/// ```no_run
104+
/// use godot::prelude::*;
105+
///
106+
/// let a = Vector2::from_angle(0.0); // (1.0, 0.0)
107+
/// let b = Vector2::new(1.0, 0.0).angle(); // 0.0
108+
/// let c = Vector2::from_angle(real_consts::PI / 2.0); // (0.0, 1.0)
109+
/// ```
110+
#[inline]
141111
pub fn from_angle(angle: real) -> Self {
142112
Self::from_glam(RVec2::from_angle(angle))
143113
}
144114

145-
pub fn is_finite(self) -> bool {
146-
self.to_glam().is_finite()
147-
}
148-
149-
pub fn is_normalized(self) -> bool {
150-
self.to_glam().is_normalized()
151-
}
152-
153-
pub fn length_squared(self) -> real {
154-
self.to_glam().length_squared()
155-
}
156-
157-
pub fn limit_length(self, length: Option<real>) -> Self {
158-
Self::from_glam(self.to_glam().clamp_length_max(length.unwrap_or(1.0)))
159-
}
160-
161-
pub fn max_axis_index(self) -> Vector2Axis {
162-
if self.x < self.y {
163-
Vector2Axis::Y
164-
} else {
165-
Vector2Axis::X
166-
}
167-
}
168-
169-
pub fn min_axis_index(self) -> Vector2Axis {
170-
if self.x < self.y {
171-
Vector2Axis::X
172-
} else {
173-
Vector2Axis::Y
174-
}
175-
}
176-
177-
pub fn move_toward(self, to: Self, delta: real) -> Self {
178-
let vd = to - self;
179-
let len = vd.length();
180-
if len <= delta || len < real::CMP_EPSILON {
181-
to
182-
} else {
183-
self + vd / len * delta
184-
}
185-
}
186-
115+
/// Returns a perpendicular vector rotated 90 degrees counter-clockwise compared to the original, with the same length.
116+
#[inline]
187117
pub fn orthogonal(self) -> Self {
188118
Self::new(self.y, -self.x)
189119
}
190120

191-
pub fn project(self, b: Self) -> Self {
192-
Self::from_glam(self.to_glam().project_onto(b.to_glam()))
193-
}
194-
195-
pub fn reflect(self, normal: Self) -> Self {
196-
2.0 * normal * self.dot(normal) - self
197-
}
198-
199-
pub fn round(self) -> Self {
200-
Self::from_glam(self.to_glam().round())
121+
/// Returns the result of rotating this vector by `angle` (in radians).
122+
#[inline]
123+
pub fn rotated(self, angle: real) -> Self {
124+
Self::from_glam(RAffine2::from_angle(angle).transform_vector2(self.to_glam()))
201125
}
202126

203-
// TODO compare with gdnative implementation:
204-
// https://github.com/godot-rust/gdnative/blob/master/gdnative-core/src/core_types/vector3.rs#L335-L343
127+
/// Returns the result of spherical linear interpolation between this vector and `to`, by amount `weight`.
128+
/// `weight` is on the range of 0.0 to 1.0, representing the amount of interpolation.
129+
///
130+
/// This method also handles interpolating the lengths if the input vectors have different lengths.
131+
/// For the special case of one or both input vectors having zero length, this method behaves like [`Vector2::lerp`].
132+
#[inline]
205133
pub fn slerp(self, to: Self, weight: real) -> Self {
206134
let start_length_sq = self.length_squared();
207135
let end_length_sq = to.length_squared();
@@ -213,24 +141,6 @@ impl Vector2 {
213141
let angle = self.angle_to(to);
214142
self.rotated(angle * weight) * (result_length / start_length)
215143
}
216-
217-
pub fn slide(self, normal: Self) -> Self {
218-
self - normal * self.dot(normal)
219-
}
220-
221-
/// Returns the result of rotating this vector by `angle` (in radians).
222-
pub fn rotated(self, angle: real) -> Self {
223-
Self::from_glam(RAffine2::from_angle(angle).transform_vector2(self.to_glam()))
224-
}
225-
226-
#[doc(hidden)]
227-
pub fn as_inner(&self) -> inner::InnerVector2 {
228-
inner::InnerVector2::from_outer(self)
229-
}
230-
231-
pub fn coords(&self) -> (real, real) {
232-
(self.x, self.y)
233-
}
234144
}
235145

236146
/// Formats the vector like Godot: `(x, y)`.
@@ -240,12 +150,6 @@ impl fmt::Display for Vector2 {
240150
}
241151
}
242152

243-
impl_common_vector_fns!(Vector2, real);
244-
impl_float_vector_glam_fns!(Vector2, real);
245-
impl_float_vector_component_fns!(Vector2, real, (x, y));
246-
impl_vector_operators!(Vector2, real, (x, y));
247-
impl_swizzle_trait_for_vector2x!(Vector2, real);
248-
249153
// SAFETY:
250154
// This type is represented as `Self` in Godot, so `*mut Self` is sound.
251155
unsafe impl GodotFfi for Vector2 {

0 commit comments

Comments
 (0)