Skip to content

Commit f428763

Browse files
committed
Refactor 2D vectors with new macros
1 parent c00663b commit f428763

File tree

2 files changed

+78
-227
lines changed

2 files changed

+78
-227
lines changed

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

Lines changed: 62 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
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::meta::impl_godot_as_self;
1414
use crate::builtin::vectors::Vector2Axis;
15-
use crate::builtin::{real, RAffine2, RVec2, Vector2i};
15+
use crate::builtin::{inner, real, RAffine2, RVec2, Vector2i};
1616

1717
use std::fmt;
1818

@@ -37,172 +37,101 @@ pub struct Vector2 {
3737
pub y: real,
3838
}
3939

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

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

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

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

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

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

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

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

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

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

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

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

237148
/// Formats the vector like Godot: `(x, y)`.
@@ -241,12 +152,6 @@ impl fmt::Display for Vector2 {
241152
}
242153
}
243154

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

0 commit comments

Comments
 (0)