Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR #3/5 Astolfo feature/builtin-vector #67

Merged
merged 1 commit into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions godot-core/src/builtin/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ pub fn snapped(mut value: f32, step: f32) -> f32 {
value
}

pub fn sign(value: f32) -> f32 {
if value == 0.0 {
0.0
} else if value < 0.0 {
-1.0
} else {
1.0
}
}

pub fn bezier_derivative(start: f32, control_1: f32, control_2: f32, end: f32, t: f32) -> f32 {
let omt = 1.0 - t;
let omt2 = omt * omt;
Expand All @@ -66,6 +76,7 @@ pub fn cubic_interpolate(from: f32, to: f32, pre: f32, post: f32, weight: f32) -
+ (-pre + 3.0 * from - 3.0 * to + post) * (weight * weight * weight))
}

#[allow(clippy::too_many_arguments)]
pub fn cubic_interpolate_in_time(
from: f32,
to: f32,
Expand Down
2 changes: 2 additions & 0 deletions godot-core/src/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ mod vector_macros;
mod arrays;
mod color;
mod dictionary;
mod math;
mod node_path;
mod others;
mod packed_array;
Expand All @@ -58,6 +59,7 @@ pub use crate::dict;
pub use arrays::*;
pub use color::*;
pub use dictionary::*;
pub use math::*;
pub use node_path::*;
pub use others::*;
pub use packed_array::*;
Expand Down
215 changes: 209 additions & 6 deletions godot-core/src/builtin/vector2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use std::fmt;
use std::ops::*;

use godot_ffi as sys;
use sys::{ffi_methods, GodotFfi};

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

/// Vector used for 2D math using floating point coordinates.
Expand Down Expand Up @@ -70,11 +71,6 @@ impl Vector2 {
}
}

/// Returns the result of rotating this vector by `angle` (in radians).
pub fn rotated(self, angle: f32) -> Self {
Self::from_glam(glam::Affine2::from_angle(angle).transform_vector2(self.to_glam()))
}

/// Converts the corresponding `glam` type to `Self`.
fn from_glam(v: glam::Vec2) -> Self {
Self::new(v.x, v.y)
Expand All @@ -85,6 +81,213 @@ impl Vector2 {
glam::Vec2::new(self.x, self.y)
}

pub fn angle(self) -> f32 {
self.y.atan2(self.x)
}

pub fn angle_to(self, to: Self) -> f32 {
self.to_glam().angle_between(to.to_glam())
}

pub fn angle_to_point(self, to: Self) -> f32 {
(to - self).angle()
}

pub fn aspect(self) -> f32 {
self.x / self.y
}

pub fn bezier_derivative(self, control_1: Self, control_2: Self, end: Self, t: f32) -> Self {
let x = bezier_derivative(self.x, control_1.x, control_2.x, end.x, t);
let y = bezier_derivative(self.y, control_1.y, control_2.y, end.y, t);

Self::new(x, y)
}

pub fn bezier_interpolate(self, control_1: Self, control_2: Self, end: Self, t: f32) -> Self {
let x = bezier_interpolate(self.x, control_1.x, control_2.x, end.x, t);
let y = bezier_interpolate(self.y, control_1.y, control_2.y, end.y, t);

Self::new(x, y)
}

pub fn bounce(self, normal: Self) -> Self {
-self.reflect(normal)
}

pub fn ceil(self) -> Self {
Self::from_glam(self.to_glam().ceil())
}

pub fn clamp(self, min: Self, max: Self) -> Self {
Self::from_glam(self.to_glam().clamp(min.to_glam(), max.to_glam()))
}

pub fn cross(self, with: Self) -> f32 {
self.to_glam().perp_dot(with.to_glam())
}

pub fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, weight: f32) -> Self {
let x = cubic_interpolate(self.x, b.x, pre_a.x, post_b.x, weight);
let y = cubic_interpolate(self.y, b.y, pre_a.y, post_b.y, weight);

Self::new(x, y)
}

#[allow(clippy::too_many_arguments)]
pub fn cubic_interpolate_in_time(
self,
b: Self,
pre_a: Self,
post_b: Self,
weight: f32,
b_t: f32,
pre_a_t: f32,
post_b_t: f32,
) -> Self {
let x = cubic_interpolate_in_time(
self.x, b.x, pre_a.x, post_b.x, weight, b_t, pre_a_t, post_b_t,
);
let y = cubic_interpolate_in_time(
self.y, b.y, pre_a.y, post_b.y, weight, b_t, pre_a_t, post_b_t,
);

Self::new(x, y)
}

pub fn direction_to(self, to: Self) -> Self {
(to - self).normalized()
}

pub fn distance_squared_to(self, to: Self) -> f32 {
(to - self).length_squared()
}

pub fn distance_to(self, to: Self) -> f32 {
(to - self).length()
}

pub fn dot(self, other: Self) -> f32 {
self.to_glam().dot(other.to_glam())
}

pub fn floor(self) -> Self {
Self::from_glam(self.to_glam().floor())
}

pub fn from_angle(angle: f32) -> Self {
Self::from_glam(glam::Vec2::from_angle(angle))
}

pub fn is_equal_approx(self, to: Self) -> bool {
is_equal_approx(self.x, to.x) && is_equal_approx(self.y, to.y)
}

pub fn is_finite(self) -> bool {
self.to_glam().is_finite()
}

pub fn is_normalized(self) -> bool {
self.to_glam().is_normalized()
}

pub fn is_zero_approx(self) -> bool {
is_zero_approx(self.x) && is_zero_approx(self.y)
}

pub fn length_squared(self) -> f32 {
self.to_glam().length_squared()
}

pub fn lerp(self, to: Self, weight: f32) -> Self {
Self::from_glam(self.to_glam().lerp(to.to_glam(), weight))
}

pub fn limit_length(self, length: Option<f32>) -> Self {
Self::from_glam(self.to_glam().clamp_length_max(length.unwrap_or(1.0)))
}

pub fn max_axis_index(self) -> Vector2Axis {
if self.x < self.y {
Vector2Axis::Y
} else {
Vector2Axis::X
}
}

pub fn min_axis_index(self) -> Vector2Axis {
if self.x < self.y {
Vector2Axis::X
} else {
Vector2Axis::Y
}
}

pub fn move_toward(self, to: Self, delta: f32) -> Self {
let vd = to - self;
let len = vd.length();
if len <= delta || len < CMP_EPSILON {
to
} else {
self + vd / len * delta
}
}

pub fn orthogonal(self) -> Self {
Self::new(self.y, -self.x)
}

pub fn posmod(self, pmod: f32) -> Self {
Self::new(fposmod(self.x, pmod), fposmod(self.y, pmod))
}

pub fn posmodv(self, modv: Self) -> Self {
Self::new(fposmod(self.x, modv.x), fposmod(self.y, modv.y))
}

pub fn project(self, b: Self) -> Self {
Self::from_glam(self.to_glam().project_onto(b.to_glam()))
}

pub fn reflect(self, normal: Self) -> Self {
Self::from_glam(self.to_glam().reject_from(normal.to_glam()))
}

pub fn round(self) -> Self {
Self::from_glam(self.to_glam().round())
}

pub fn sign(self) -> Self {
Self::new(sign(self.x), sign(self.y))
}

// TODO compare with gdnative implementation:
// https://github.com/godot-rust/gdnative/blob/master/gdnative-core/src/core_types/vector3.rs#L335-L343
pub fn slerp(self, to: Self, weight: f32) -> Self {
let start_length_sq = self.length_squared();
let end_length_sq = to.length_squared();
if start_length_sq == 0.0 || end_length_sq == 0.0 {
return self.lerp(to, weight);
}
let start_length = start_length_sq.sqrt();
let result_length = lerp(start_length, end_length_sq.sqrt(), weight);
let angle = self.angle_to(to);
self.rotated(angle * weight) * (result_length / start_length)
}
Comment on lines +266 to +276
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gdnative implementation is significantly easier; I wonder if it's correct, too?

    /// Returns the result of spherical linear interpolation between this vector and b, by amount t.
    /// t is on the range of 0.0 to 1.0, representing the amount of interpolation.
    ///
    /// **Note**: Both vectors must be normalized.
    #[inline]
    pub fn slerp(self, b: Self, t: f32) -> Self {
        let theta = self.angle_to(b);
        self.rotated(self.cross(b).normalized(), theta * t)
    }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps the gdnative implementation is valid as well, i simply looked at upstream godot and converted the language. up to you if you prefer the gdnative version as opposed to upstream.

Vector2 Vector2::slerp(const Vector2 &p_to, const real_t p_weight) const {
	real_t start_length_sq = length_squared();
	real_t end_length_sq = p_to.length_squared();
	if (unlikely(start_length_sq == 0.0f || end_length_sq == 0.0f)) {
		// Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
		return lerp(p_to, p_weight);
	}
	real_t start_length = Math::sqrt(start_length_sq);
	real_t result_length = Math::lerp(start_length, Math::sqrt(end_length_sq), p_weight);
	real_t angle = angle_to(p_to);
	return rotated(angle * p_weight) * (result_length / start_length);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe keep your implementation for now. In the first line, can you add this comment?

// TODO compare with gdnative implementation:
// https://github.com/godot-rust/gdnative/blob/master/gdnative-core/src/core_types/vector3.rs#L335-L343

Then we can check it later. We anyway need to work out a testing strategy at some point 🙂


pub fn slide(self, normal: Self) -> Self {
self - normal * self.dot(normal)
}

pub fn snapped(self, step: Self) -> Self {
Self::new(snapped(self.x, step.x), snapped(self.y, step.y))
}

/// Returns the result of rotating this vector by `angle` (in radians).
pub fn rotated(self, angle: f32) -> Self {
Self::from_glam(glam::Affine2::from_angle(angle).transform_vector2(self.to_glam()))
}

#[doc(hidden)]
pub fn as_inner(&self) -> inner::InnerVector2 {
inner::InnerVector2::from_outer(self)
Expand Down
Loading