use std::cmp::Ordering;
use std::fmt;
use godot_ffi as sys;
use sys::{ExtVariantType, GodotFfi, ffi_methods};
use crate::builtin::math::{FloatExt, GlamConv, GlamType};
use crate::builtin::vectors::Vector2Axis;
use crate::builtin::{RAffine2, RVec2, Vector2i, inner, real};
#[doc = shared_vector_docs!()]
#[derive(Default, Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Vector2 {
pub x: real,
pub y: real,
}
impl Vector2 {
impl_vector_consts!(real);
impl_float_vector_consts!();
impl_vector2x_consts!(real);
}
impl_vector_fns!(Vector2, RVec2, real, (x, y));
impl Vector2 {
#[inline]
pub fn from_angle(angle: real) -> Self {
Self::from_glam(RVec2::from_angle(angle))
}
#[inline]
pub fn angle(self) -> real {
self.y.atan2(self.x)
}
#[inline]
pub fn angle_to(self, to: Self) -> real {
self.glam2(&to, |a, b| a.angle_to(b))
}
#[inline]
pub fn angle_to_point(self, to: Self) -> real {
(to - self).angle()
}
#[inline]
pub fn cross(self, with: Self) -> real {
self.to_glam().perp_dot(with.to_glam())
}
#[inline]
pub fn orthogonal(self) -> Self {
Self::new(self.y, -self.x)
}
#[inline]
pub fn rotated(self, angle: real) -> Self {
Self::from_glam(RAffine2::from_angle(angle).transform_vector2(self.to_glam()))
}
#[inline]
pub fn slerp(self, to: Self, weight: real) -> 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 = real::lerp(start_length, end_length_sq.sqrt(), weight);
let angle = self.angle_to(to);
self.rotated(angle * weight) * (result_length / start_length)
}
#[doc(hidden)]
#[inline]
pub fn as_inner(&self) -> inner::InnerVector2<'_> {
inner::InnerVector2::from_outer(self)
}
}
impl_float_vector_fns!(Vector2, Vector2i, (x, y));
impl_vector2x_fns!(Vector2, Vector3, real);
impl_vector2_vector3_fns!(Vector2, (x, y));
impl_vector_operators!(Vector2, real, (x, y));
impl fmt::Display for Vector2 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
unsafe impl GodotFfi for Vector2 {
const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::VECTOR2);
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
}
crate::meta::impl_godot_as_self!(Vector2: ByValue);
impl GlamConv for Vector2 {
type Glam = RVec2;
}
impl GlamType for RVec2 {
type Mapped = Vector2;
fn to_front(&self) -> Self::Mapped {
Vector2::new(self.x, self.y)
}
fn from_front(mapped: &Self::Mapped) -> Self {
RVec2::new(mapped.x, mapped.y)
}
}
#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
mod test {
use super::*;
use crate::assert_eq_approx;
#[test]
fn coord_min_max() {
let a = Vector2::new(1.2, 3.4);
let b = Vector2::new(0.1, 5.6);
assert_eq_approx!(a.coord_min(b), Vector2::new(0.1, 3.4));
assert_eq_approx!(a.coord_max(b), Vector2::new(1.2, 5.6));
}
#[test]
fn sign() {
let vector = Vector2::new(0.2, -0.5);
assert_eq!(vector.sign(), Vector2::new(1., -1.));
let vector = Vector2::new(0.1, 0.0);
assert_eq!(vector.sign(), Vector2::new(1., 0.));
}
#[cfg(feature = "serde")] #[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
#[test]
fn serde_roundtrip() {
let vector = Vector2::default();
let expected_json = "{\"x\":0.0,\"y\":0.0}";
crate::builtin::test_utils::roundtrip(&vector, expected_json);
}
}