From fce0a9b27961bc246370cdf45eef6ba248e522d2 Mon Sep 17 00:00:00 2001 From: David Hoppenbrouwers Date: Thu, 18 Mar 2021 15:40:08 +0100 Subject: [PATCH 1/2] Replace euclid with glam --- examples/dodge_the_creeps/src/main_scene.rs | 3 +- examples/dodge_the_creeps/src/player.rs | 11 +- examples/scene_create/Cargo.toml | 2 +- examples/scene_create/src/lib.rs | 3 +- gdnative-core/Cargo.toml | 4 +- gdnative-core/src/core_types/geom/basis.rs | 92 ++-- gdnative-core/src/core_types/geom/mod.rs | 12 - gdnative-core/src/core_types/geom/plane.rs | 31 +- .../src/core_types/geom/transform.rs | 20 - gdnative-core/src/core_types/mod.rs | 65 ++- gdnative-core/src/core_types/point2.rs | 41 -- gdnative-core/src/core_types/quat.rs | 30 ++ gdnative-core/src/core_types/rect2.rs | 7 + gdnative-core/src/core_types/transform2d.rs | 8 + gdnative-core/src/core_types/typed_array.rs | 4 +- gdnative-core/src/core_types/vector2.rs | 407 ++++++++++++---- gdnative-core/src/core_types/vector3.rs | 446 ++++++++++++++++-- gdnative/src/prelude.rs | 12 +- .../ui/derive_fail_methods_missing_new.stderr | 11 +- gdnative/tests/ui/from_variant_fail_01.stderr | 4 +- gdnative/tests/ui/from_variant_fail_02.stderr | 6 +- gdnative/tests/ui/from_variant_fail_03.stderr | 4 +- gdnative/tests/ui/from_variant_fail_07.stderr | 2 +- gdnative/tests/ui/to_variant_fail_01.stderr | 4 +- gdnative/tests/ui/to_variant_fail_02.stderr | 6 +- gdnative/tests/ui/to_variant_fail_03.stderr | 4 +- gdnative/tests/ui/to_variant_fail_07.stderr | 2 +- 27 files changed, 938 insertions(+), 303 deletions(-) delete mode 100644 gdnative-core/src/core_types/point2.rs create mode 100644 gdnative-core/src/core_types/quat.rs create mode 100644 gdnative-core/src/core_types/rect2.rs create mode 100644 gdnative-core/src/core_types/transform2d.rs diff --git a/examples/dodge_the_creeps/src/main_scene.rs b/examples/dodge_the_creeps/src/main_scene.rs index cc0069907..6642ff959 100644 --- a/examples/dodge_the_creeps/src/main_scene.rs +++ b/examples/dodge_the_creeps/src/main_scene.rs @@ -116,8 +116,7 @@ impl Main { mob_owner .set_linear_velocity(Vector2::new(rng.gen_range(x.min_speed..x.max_speed), 0.0)); - mob_owner - .set_linear_velocity(mob_owner.linear_velocity().rotated(Angle { radians: d })); + mob_owner.set_linear_velocity(mob_owner.linear_velocity().rotated(d)); let hud = unsafe { owner.get_node_as_instance::("hud").unwrap() }; diff --git a/examples/dodge_the_creeps/src/player.rs b/examples/dodge_the_creeps/src/player.rs index ec5233cee..b3a38e879 100644 --- a/examples/dodge_the_creeps/src/player.rs +++ b/examples/dodge_the_creeps/src/player.rs @@ -32,7 +32,7 @@ impl Player { #[export] fn _ready(&mut self, owner: &Area2D) { let viewport = owner.get_viewport_rect(); - self.screen_size = viewport.size.to_vector(); + self.screen_size = viewport.size; owner.hide(); } @@ -61,7 +61,7 @@ impl Player { } if velocity.length() > 0.0 { - velocity = velocity.normalize() * self.speed; + velocity = velocity.normalized() * self.speed; let animation; @@ -82,8 +82,11 @@ impl Player { } let change = velocity * delta; - let position = - (owner.global_position() + change).clamp(Vector2::new(0.0, 0.0), self.screen_size); + let position = owner.global_position() + change; + let position = Vector2::new( + position.x.max(0.0).min(self.screen_size.x), + position.y.max(0.0).min(self.screen_size.y), + ); owner.set_global_position(position); } diff --git a/examples/scene_create/Cargo.toml b/examples/scene_create/Cargo.toml index 2b6d00da1..294df4bad 100644 --- a/examples/scene_create/Cargo.toml +++ b/examples/scene_create/Cargo.toml @@ -10,4 +10,4 @@ crate-type = ["cdylib"] [dependencies] gdnative = { path = "../../gdnative" } -euclid = "0.22.1" +glam = "0.13.0" diff --git a/examples/scene_create/src/lib.rs b/examples/scene_create/src/lib.rs index e63d4b191..7e79be78a 100644 --- a/examples/scene_create/src/lib.rs +++ b/examples/scene_create/src/lib.rs @@ -1,4 +1,3 @@ -use euclid::vec3; use gdnative::prelude::*; #[derive(Debug, Clone, PartialEq)] @@ -64,7 +63,7 @@ impl SceneCreate { let x = (self.children_spawned % 10) as f32; let z = (self.children_spawned / 10) as f32; - spatial.translate(vec3(-10.0 + x * 2.0, 0.0, -10.0 + z * 2.0)); + spatial.translate(Vector3::new(-10.0 + x * 2.0, 0.0, -10.0 + z * 2.0)); // You need to parent the new scene under some node if you want it in the scene. // We parent it under ourselves. diff --git a/gdnative-core/Cargo.toml b/gdnative-core/Cargo.toml index 74e426d90..8c16ae972 100644 --- a/gdnative-core/Cargo.toml +++ b/gdnative-core/Cargo.toml @@ -20,7 +20,7 @@ type_tag_fallback = [] gdnative-sys = { path = "../gdnative-sys", version = "0.9.3" } libc = "0.2" approx = "0.4.0" -euclid = "0.22.1" +glam = "0.13.0" indexmap = "1.6.0" ahash = "0.7.0" @@ -28,4 +28,4 @@ gdnative-impl-proc-macros = { path = "../impl/proc_macros", version = "=0.9.3" } bitflags = { version = "1.2", optional = true } parking_lot = { version = "0.11.0", optional = true } -atomic-take = "1.0.0" \ No newline at end of file +atomic-take = "1.0.0" diff --git a/gdnative-core/src/core_types/geom/basis.rs b/gdnative-core/src/core_types/geom/basis.rs index a9b024290..d053dc0b3 100644 --- a/gdnative-core/src/core_types/geom/basis.rs +++ b/gdnative-core/src/core_types/geom/basis.rs @@ -1,5 +1,5 @@ -use crate::core_types::{Quat, Vector3}; -use euclid::{approxeq::ApproxEq, default, Transform3D, UnknownUnit, Vector3D}; +use crate::core_types::{IsEqualApprox, Quat, Vector3}; +use core::ops::Mul; /// A 3x3 matrix. #[repr(C)] @@ -96,7 +96,7 @@ impl Basis { #[inline] pub fn from_axis_angle(axis: &Vector3, phi: f32) -> Self { assert!( - axis.length().approx_eq(&1.0), + axis.length().is_equal_approx(1.0), "The axis Vector3 must be normalized." ); @@ -147,7 +147,7 @@ impl Basis { ]; let det: f32 = x.x * co[0] + x.y * co[1] + x.z * co[2]; - assert!(!det.approx_eq(&0.0), "Determinant was zero"); + assert!(!det.is_equal_approx(0.0), "Determinant was zero"); let s: f32 = 1.0 / det; @@ -210,7 +210,7 @@ impl Basis { #[inline] pub fn orthonormalize(&mut self) { assert!( - !self.determinant().approx_eq(&0.0), + !self.determinant().is_equal_approx(0.0), "Determinant should not be zero." ); @@ -219,11 +219,11 @@ impl Basis { let mut y = self.y(); let mut z = self.z(); - x = x.normalize(); + x = x.normalized(); y = y - x * (x.dot(y)); - y = y.normalize(); + y = y.normalized(); z = z - x * (x.dot(z)) - y * (y.dot(z)); - z = z.normalize(); + z = z.normalized(); self.set_x(x); self.set_y(y); @@ -241,23 +241,23 @@ impl Basis { /// Returns `true` if `self` and `other` are approximately equal. #[inline] - pub fn approx_eq(&self, other: &Basis) -> bool { - self.elements[0].approx_eq(&other.elements[0]) - && self.elements[1].approx_eq(&other.elements[1]) - && self.elements[2].approx_eq(&other.elements[2]) + pub fn is_equal_approx(&self, other: &Basis) -> bool { + self.elements[0].is_equal_approx(other.elements[0]) + && self.elements[1].is_equal_approx(other.elements[1]) + && self.elements[2].is_equal_approx(other.elements[2]) } #[inline] fn is_orthogonal(&self) -> bool { let identity = Self::identity(); let m = (*self) * self.transposed(); - m.approx_eq(&identity) + m.is_equal_approx(&identity) } #[inline] fn is_rotation(&self) -> bool { let det = self.determinant(); - det.approx_eq(&1.0) && self.is_orthogonal() + det.is_equal_approx(1.0) && self.is_orthogonal() } /// Multiplies the matrix from left by the rotation matrix: M -> R.M @@ -328,9 +328,9 @@ impl Basis { let k = (i + 2) % 3; let elements_arr: [[f32; 3]; 3] = [ - matrix.elements[0].to_array(), - matrix.elements[1].to_array(), - matrix.elements[2].to_array(), + *matrix.elements[0].as_ref(), + *matrix.elements[1].as_ref(), + *matrix.elements[2].as_ref(), ]; let mut s = (elements_arr[i][i] - elements_arr[j][j] - elements_arr[k][k] + 1.0).sqrt(); @@ -343,7 +343,7 @@ impl Basis { } let [a, b, c, r] = temp; - Quat::quaternion(a, b, c, r) + Quat::new(a, b, c, r) } /// Returns the scale of the matrix. @@ -384,17 +384,17 @@ impl Basis { /// See [`Basis::to_quat`](#method.to_quat) if you need a quaternion instead. #[inline] pub fn to_euler(&self) -> Vector3 { - let mut euler = Vector3::zero(); + let mut euler = Vector3::ZERO; let m12 = self.elements[1].z; if m12 < 1.0 { if m12 > -1.0 { // is this a pure X rotation? - if self.elements[1].x.approx_eq(&0.0) - && self.elements[0].y.approx_eq(&0.0) - && self.elements[0].z.approx_eq(&0.0) - && self.elements[2].x.approx_eq(&0.0) - && self.elements[0].x.approx_eq(&1.0) + if self.elements[1].x.is_equal_approx(0.0) + && self.elements[0].y.is_equal_approx(0.0) + && self.elements[0].z.is_equal_approx(0.0) + && self.elements[2].x.is_equal_approx(0.0) + && self.elements[0].x.is_equal_approx(1.0) { // return the simplest form (human friendlier in editor and scripts) euler.x = (-m12).atan2(self.elements[1].y); @@ -471,6 +471,7 @@ impl Basis { ) } + /* /// Creates a `Basis` from the rotation and scaling of the provided transform. #[inline] pub fn from_transform(transform: &default::Transform3D) -> Basis { @@ -494,6 +495,7 @@ impl Basis { ], } } + */ /// Transposed dot product with the **X Axis** of the matrix. #[inline] @@ -580,6 +582,15 @@ impl core::ops::Mul for Basis { } } +impl Mul for Basis { + type Output = Vector3; + + #[inline] + fn mul(self, rhs: Self::Output) -> Self::Output { + Self::Output::new(self.tdotx(rhs), self.tdoty(rhs), self.tdotz(rhs)) + } +} + #[cfg(test)] #[allow(clippy::unreadable_literal)] mod tests { @@ -620,7 +631,7 @@ mod tests { #[test] fn set_is_sane() { let mut basis = Basis { - elements: [Vector3::zero(), Vector3::zero(), Vector3::zero()], + elements: [Vector3::ZERO, Vector3::ZERO, Vector3::ZERO], }; basis.set_x(Vector3::new(1.0, 4.0, 7.0)); @@ -634,7 +645,7 @@ mod tests { fn test_inputs() -> (Basis, Basis) { let v = Vector3::new(37.51756, 20.39467, 49.96816); - let vn = v.normalize(); + let vn = v.normalized(); let b = Basis::from_euler(v); let bn = Basis::from_euler(vn); (b, bn) @@ -644,14 +655,17 @@ mod tests { fn determinant() { let (b, _bn) = test_inputs(); - assert!(b.determinant().approx_eq(&1.0), "Determinant should be 1.0"); + assert!( + b.determinant().is_equal_approx(1.0), + "Determinant should be 1.0" + ); } #[test] fn euler() { let (_b, bn) = test_inputs(); - assert!(Vector3::new(0.57079, 0.310283, 0.760213).approx_eq(&bn.to_euler())); + assert!(Vector3::new(0.57079, 0.310283, 0.760213).is_equal_approx(bn.to_euler())); } #[test] @@ -663,7 +677,7 @@ mod tests { Vector3::new(-0.288147, 0.94041, 0.180557), Vector3::new(-0.95445, -0.297299, 0.025257), ]); - assert!(expected.approx_eq(&b.orthonormalized())); + assert!(expected.is_equal_approx(&b.orthonormalized())); } #[test] @@ -675,40 +689,40 @@ mod tests { Vector3::new(0.012407, -0.040492, -0.007774), Vector3::new(-0.682131, -0.212475, 0.018051), ]); - assert!(expected.approx_eq(&b.scaled(&Vector3::new(0.677813, -0.043058, 0.714685)))); + assert!(expected.is_equal_approx(&b.scaled(&Vector3::new(0.677813, -0.043058, 0.714685)))); } #[test] fn rotated() { let (b, _bn) = test_inputs(); - let r = Vector3::new(-50.167156, 60.67781, -70.04305).normalize(); + let r = Vector3::new(-50.167156, 60.67781, -70.04305).normalized(); let expected = Basis::from_elements([ Vector3::new(-0.676245, 0.113805, 0.727833), Vector3::new(-0.467094, 0.697765, -0.54309), Vector3::new(-0.569663, -0.707229, -0.418703), ]); - assert!(expected.approx_eq(&b.rotated(r, 1.0))); + assert!(expected.is_equal_approx(&b.rotated(r, 1.0))); } #[test] fn to_quat() { let (b, _bn) = test_inputs(); - assert!(Quat::quaternion(-0.167156, 0.677813, -0.043058, 0.714685).approx_eq(&b.to_quat())); + assert!(Quat::new(-0.167156, 0.677813, -0.043058, 0.714685).is_equal_approx(&b.to_quat())); } #[test] fn scale() { let (b, _bn) = test_inputs(); - assert!(Vector3::new(1.0, 1.0, 1.0).approx_eq(&b.to_scale())); + assert!(Vector3::new(1.0, 1.0, 1.0).is_equal_approx(b.to_scale())); } #[test] fn approx_eq() { let (b, _bn) = test_inputs(); - assert!(!b.approx_eq(&Basis::from_euler(Vector3::new(37.517, 20.394, 49.968)))); + assert!(!b.is_equal_approx(&Basis::from_euler(Vector3::new(37.517, 20.394, 49.968)))); } #[test] @@ -719,7 +733,7 @@ mod tests { Vector3::new(-0.165055, 0.94041, -0.297299), Vector3::new(0.98324, 0.180557, 0.025257), ]); - assert!(expected.approx_eq(&b.transposed())); + assert!(expected.is_equal_approx(&b.transposed())); } #[test] @@ -727,7 +741,7 @@ mod tests { let (b, _bn) = test_inputs(); assert!(Vector3::new(-0.273471, 0.478102, -0.690386) - .approx_eq(&b.xform(Vector3::new(0.5, 0.7, -0.2)))); + .is_equal_approx(b.xform(Vector3::new(0.5, 0.7, -0.2)))); } #[test] @@ -735,7 +749,7 @@ mod tests { let (b, _bn) = test_inputs(); assert!(Vector3::new(-0.884898, -0.460316, 0.071165) - .approx_eq(&b.xform_inv(Vector3::new(0.077431, -0.165055, 0.98324)))); + .is_equal_approx(b.xform_inv(Vector3::new(0.077431, -0.165055, 0.98324)))); } #[test] @@ -747,6 +761,6 @@ mod tests { Vector3::new(-0.165055, 0.94041, -0.297299), Vector3::new(0.98324, 0.180557, 0.025257), ]); - assert!(expected.approx_eq(&b.inverted())); + assert!(expected.is_equal_approx(&b.inverted())); } } diff --git a/gdnative-core/src/core_types/geom/mod.rs b/gdnative-core/src/core_types/geom/mod.rs index cef77aeba..e63c16e38 100644 --- a/gdnative-core/src/core_types/geom/mod.rs +++ b/gdnative-core/src/core_types/geom/mod.rs @@ -5,18 +5,6 @@ mod basis; mod plane; mod transform; -pub type Vector3 = euclid::default::Vector3D; -pub type Vector2 = euclid::default::Vector2D; -pub type Transform2D = euclid::default::Transform2D; -pub type Quat = euclid::default::Rotation3D; -pub type Size2 = euclid::default::Size2D; -pub type Rect2 = euclid::default::Rect; -pub type Angle = euclid::Angle; -pub type Point3 = euclid::default::Point3D; -pub type Point2 = euclid::default::Point2D; -pub type Rotation2D = euclid::default::Rotation2D; -pub type Rotation3D = euclid::default::Rotation3D; - pub use self::aabb::Aabb; pub use self::basis::Basis; pub use self::plane::Plane; diff --git a/gdnative-core/src/core_types/geom/plane.rs b/gdnative-core/src/core_types/geom/plane.rs index 4f15d4816..eec423f9c 100644 --- a/gdnative-core/src/core_types/geom/plane.rs +++ b/gdnative-core/src/core_types/geom/plane.rs @@ -1,5 +1,4 @@ -use crate::core_types::Vector3; -use euclid::approxeq::ApproxEq; +use crate::core_types::{IsEqualApprox, Vector3}; /// Plane in hessian form. #[repr(C)] @@ -43,7 +42,7 @@ impl Plane { /// If all three points are collinear, returns `None`. #[inline] pub fn from_points(a: Vector3, b: Vector3, c: Vector3) -> Option { - let normal = (a - c).cross(a - b).normalize(); + let normal = (a - c).cross(a - b).normalized(); if normal.x.is_nan() || normal.y.is_nan() || normal.z.is_nan() { None @@ -84,7 +83,7 @@ impl Plane { let denom = Vector3::cross(a.normal, b.normal).dot(c.normal); - if denom.approx_eq(&0.0) { + if denom.is_equal_approx(0.0) { None } else { Some( @@ -102,7 +101,7 @@ impl Plane { pub fn intersects_ray(&self, from: Vector3, dir: Vector3) -> Option { let den = self.normal.dot(dir); - if den.approx_eq(&0.0) { + if den.is_equal_approx(0.0) { return None; } @@ -122,7 +121,7 @@ impl Plane { let segment = begin - end; let den = self.normal.dot(segment); - if den.approx_eq(&0.0) { + if den.is_equal_approx(0.0) { return None; } @@ -139,8 +138,8 @@ impl Plane { /// Returns `true` if this `Plane` and `other` are approximately equal. /// Determined by running `approx_eq` on both `normal` and `d`. #[inline] - pub fn approx_eq(&self, other: Plane) -> bool { - self.normal.approx_eq(&other.normal) && self.d.approx_eq(&other.d) + pub fn is_equal_approx(self, other: Plane) -> bool { + self.normal.is_equal_approx(other.normal) && self.d.is_equal_approx(other.d) } /// Returns `true` if `point` is above the `Plane`. @@ -194,7 +193,7 @@ mod test { assert!(Plane::from_points(a, b, c) .unwrap() - .approx_eq(expected_valid)); + .is_equal_approx(expected_valid)); assert_eq!(Plane::from_points(a, b, d), None); } @@ -204,7 +203,7 @@ mod test { let expected = Vector3::new(0.0008, 0.0016, 0.0032); - assert!(p.center().approx_eq(&expected)); + assert!(p.center().is_equal_approx(expected)); } #[test] @@ -213,7 +212,7 @@ mod test { let expected = -0.0464; - assert!(p.distance_to(v).approx_eq(&expected)); + assert!(p.distance_to(v).is_equal_approx(expected)); } #[test] @@ -239,7 +238,7 @@ mod test { let d = Plane::from_coordinates(0.01, 0.02, 0.4, 0.16); let e = Plane::from_coordinates(0.01, 0.02, 0.4, 0.32); - assert!(p.intersect_3(b, c).unwrap().approx_eq(&expected)); + assert!(p.intersect_3(b, c).unwrap().is_equal_approx(expected)); assert_eq!(p.intersect_3(d, e), None); } @@ -252,7 +251,7 @@ mod test { assert!(p .intersects_ray(v, Vector3::new(0.0, 1.0, 0.0)) .unwrap() - .approx_eq(&expected)); + .is_equal_approx(expected)); assert_eq!(p.intersects_ray(v, Vector3::new(0.0, -1.0, 0.0)), None); } @@ -265,7 +264,7 @@ mod test { assert!(p .intersects_segment(v, Vector3::new(0.16, 10.0, 0.64)) .unwrap() - .approx_eq(&expected)); + .is_equal_approx(expected)); assert_eq!( p.intersects_segment(v, Vector3::new(0.16, -10.0, 0.64)), None @@ -284,7 +283,7 @@ mod test { fn normalize() { let (p, _v) = test_inputs(); - assert!(p.normalize().approx_eq(Plane::from_coordinates( + assert!(p.normalize().is_equal_approx(Plane::from_coordinates( 0.218218, 0.436436, 0.872872, 1.745743 ))); } @@ -295,6 +294,6 @@ mod test { let expected = Vector3::new(0.160464, 0.320928, 0.641856); - assert!(p.project(v).approx_eq(&expected)) + assert!(p.project(v).is_equal_approx(expected)) } } diff --git a/gdnative-core/src/core_types/geom/transform.rs b/gdnative-core/src/core_types/geom/transform.rs index c584d45c6..251459230 100644 --- a/gdnative-core/src/core_types/geom/transform.rs +++ b/gdnative-core/src/core_types/geom/transform.rs @@ -1,5 +1,4 @@ use crate::core_types::{Basis, Vector3}; -use euclid::{default, Point3D, Transform3D, UnknownUnit}; /// 3D Transformation (3x4 matrix) Using basis + origin representation. #[repr(C)] @@ -35,23 +34,4 @@ impl Transform { origin, } } - - /// Creates a `Basis` from the rotation and scaling of the provided transform. - #[inline] - pub fn from_transform(transform: &default::Transform3D) -> Transform { - Self::from_typed_transform::(transform) - } - - /// Creates a `Basis` from the rotation and scaling of the provided transform, in `Dst` space. - #[inline] - pub fn from_typed_transform(transform: &Transform3D) -> Transform { - Transform { - basis: Basis::from_typed_transform(transform), - origin: transform - .transform_point3d(Point3D::origin()) - .unwrap_or_else(Point3D::origin) - .to_vector() - .to_untyped(), - } - } } diff --git a/gdnative-core/src/core_types/mod.rs b/gdnative-core/src/core_types/mod.rs index 567c76833..72a3ebca5 100644 --- a/gdnative-core/src/core_types/mod.rs +++ b/gdnative-core/src/core_types/mod.rs @@ -1,27 +1,30 @@ //! Types that represent core-datatypes of Godot. -pub mod geom; +mod geom; + +mod access; +mod byte_array; +mod color; +mod color_array; +mod float32_array; +mod int32_array; +mod node_path; +mod quat; +mod rect2; +mod rid; +mod string; +mod string_array; +mod transform2d; +mod typed_array; +mod variant; +mod variant_array; +mod vector2; +mod vector2_array; +mod vector3_array; -pub mod access; -pub mod byte_array; -pub mod color; -pub mod color_array; pub mod dictionary; pub mod error; -pub mod float32_array; -pub mod int32_array; -pub mod node_path; -pub mod point2; -pub mod rid; -pub mod string; -pub mod string_array; -pub mod typed_array; -pub mod variant; -pub mod variant_array; -pub mod vector2; -pub mod vector2_array; pub mod vector3; -pub mod vector3_array; pub use geom::*; @@ -34,10 +37,12 @@ pub use error::GodotError; pub use float32_array::*; pub use int32_array::*; pub use node_path::*; -pub use point2::*; +pub use quat::*; +pub use rect2::*; pub use rid::*; pub use string::*; pub use string_array::*; +pub use transform2d::*; pub use typed_array::TypedArray; pub use variant::*; pub use variant_array::*; @@ -45,3 +50,25 @@ pub use vector2::*; pub use vector2_array::*; pub use vector3::*; pub use vector3_array::*; + +use approx::relative_eq; + +const CMP_EPSILON: f64 = 0.00001; + +// This trait is intended for internal use +trait IsEqualApprox { + #[allow(clippy::wrong_self_convention)] + fn is_equal_approx(self, to: Self) -> bool; +} + +impl IsEqualApprox for f32 { + fn is_equal_approx(self, to: Self) -> bool { + relative_eq!(self, to, epsilon = CMP_EPSILON as f32) + } +} + +impl IsEqualApprox for f64 { + fn is_equal_approx(self, to: Self) -> bool { + relative_eq!(self, to, epsilon = CMP_EPSILON) + } +} diff --git a/gdnative-core/src/core_types/point2.rs b/gdnative-core/src/core_types/point2.rs deleted file mode 100644 index d6a3fa6c7..000000000 --- a/gdnative-core/src/core_types/point2.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::core_types::{Angle, Point2, Vector2}; -use euclid::Trig; - -/// Helper methods for `Point2`. -/// -/// Trait used to provide additional methods that are equivalent to Godot's methods. -/// See the official [`Godot documentation`](https://docs.godotengine.org/en/3.1/classes/class_vector2.html). -pub trait Point2Godot { - /// Returns the angle in radians between the line connecting the two points and the x - /// coordinate. - fn angle_to_point(self, other: Point2) -> Angle; - /// Returns the normalized vector pointing from this point to `other`. - fn direction_to(self, other: Point2) -> Vector2; - /// Returns the distance to `other`. - fn distance_to(self, other: Point2) -> f32; - /// Returns the squared distance to `other`. Prefer this function over distance_to if you - /// need to sort points or need the squared distance for some formula. - fn distance_squared_to(self, other: Point2) -> f32; -} - -impl Point2Godot for Point2 { - #[inline] - fn angle_to_point(self, other: Point2) -> Angle { - Angle::radians(Trig::fast_atan2(self.y - other.y, self.x - other.x)) - } - - #[inline] - fn direction_to(self, other: Point2) -> Vector2 { - (other - self).normalize() - } - - #[inline] - fn distance_to(self, other: Point2) -> f32 { - (other - self).length() - } - - #[inline] - fn distance_squared_to(self, other: Point2) -> f32 { - (other - self).square_length() - } -} diff --git a/gdnative-core/src/core_types/quat.rs b/gdnative-core/src/core_types/quat.rs new file mode 100644 index 000000000..c4e36ffb9 --- /dev/null +++ b/gdnative-core/src/core_types/quat.rs @@ -0,0 +1,30 @@ +use super::IsEqualApprox; + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(C)] +pub struct Quat { + pub x: f32, + pub y: f32, + pub z: f32, + pub w: f32, +} + +impl Quat { + #[inline] + pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self { + Self { x, y, z, w } + } + + #[inline] + pub fn is_equal_approx(self, to: &Self) -> bool { + self.x.is_equal_approx(to.x) + && self.y.is_equal_approx(to.y) + && self.z.is_equal_approx(to.z) + && self.w.is_equal_approx(to.w) + } + + #[inline] + fn glam(self) -> glam::Quat { + glam::Quat::from_xyzw(self.x, self.y, self.z, self.w) + } +} diff --git a/gdnative-core/src/core_types/rect2.rs b/gdnative-core/src/core_types/rect2.rs new file mode 100644 index 000000000..1fe015688 --- /dev/null +++ b/gdnative-core/src/core_types/rect2.rs @@ -0,0 +1,7 @@ +use super::Vector2; + +#[repr(C)] +pub struct Rect2 { + pub position: Vector2, + pub size: Vector2, +} diff --git a/gdnative-core/src/core_types/transform2d.rs b/gdnative-core/src/core_types/transform2d.rs new file mode 100644 index 000000000..a8067908e --- /dev/null +++ b/gdnative-core/src/core_types/transform2d.rs @@ -0,0 +1,8 @@ +use super::Vector2; + +#[repr(C)] +pub struct Transform2D { + pub x: Vector2, + pub y: Vector2, + pub origin: Vector2, +} diff --git a/gdnative-core/src/core_types/typed_array.rs b/gdnative-core/src/core_types/typed_array.rs index 00bffe382..c5b2a822b 100644 --- a/gdnative-core/src/core_types/typed_array.rs +++ b/gdnative-core/src/core_types/typed_array.rs @@ -5,9 +5,7 @@ use std::iter::{Extend, FromIterator}; use gdnative_impl_proc_macros as macros; use crate::core_types::access::{Aligned, MaybeUnaligned}; -use crate::core_types::{ - Color, GodotString, VariantArray, Vector2, Vector2Godot, Vector3, Vector3Godot, -}; +use crate::core_types::{Color, GodotString, VariantArray, Vector2, Vector3}; use crate::private::get_api; use crate::NewRef; diff --git a/gdnative-core/src/core_types/vector2.rs b/gdnative-core/src/core_types/vector2.rs index 1c28871a6..61a596332 100644 --- a/gdnative-core/src/core_types/vector2.rs +++ b/gdnative-core/src/core_types/vector2.rs @@ -1,60 +1,91 @@ -use crate::core_types::{Angle, Rotation2D, Vector2}; +use super::IsEqualApprox; +use glam::Vec2; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(C)] +pub struct Vector2 { + pub x: f32, + pub y: f32, +} /// Helper methods for `Vector2`. /// -/// Trait used to provide additional methods that are equivalent to Godot's methods. /// See the official [`Godot documentation`](https://docs.godotengine.org/en/3.1/classes/class_vector2.html). -pub trait Vector2Godot { - /// Returns the ratio of x to y. - fn aspect(self) -> f32; - /// Cubicly interpolates between this vector and `b` using `pre_a` and `post_b` as handles, - /// and returns the result at position `t`. `t` is in the range of 0.0 - 1.0, representing - /// the amount of interpolation. - fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, t: f32) -> Self; - /// Returns the vector rotated by `angle` radians. - fn rotated(self, angle: Angle) -> Self; - /// Returns the component of the vector along a plane defined by the given normal. - fn slide(self, normal: Self) -> Self; - /// Returns the vector snapped to a grid with the given size. - fn snapped(self, by: Self) -> Self; - /// Returns a perpendicular vector. - fn tangent(self) -> Self; - /// Returns `self` moved towards `to` by the distance `delta`, clamped by `to`. - fn move_towards(self, to: Vector2, delta: f32) -> Self; - /// Returns the normalized vector pointing from this vector to `other`. - fn direction_to(self, other: Vector2) -> Vector2; - /// Returns the distance to `other`. - fn distance_to(self, other: Vector2) -> f32; - /// Returns the squared distance to `other`. +impl Vector2 { + /// Constructs a new Vector2 from the given x and y. + #[inline] + pub const fn new(x: f32, y: f32) -> Self { + Self { x, y } + } + + /// Returns a new vector with all components in absolute values (i.e. positive). + #[inline] + pub fn abs(self) -> Self { + Self::gd(self.glam().abs()) + } + + /// Returns this vector's angle with respect to the positive X axis, or (1, 0) vector, in + /// radians. /// - /// This method runs faster than distance_to, so prefer it if you need to compare vectors or - /// need the squared distance for some formula. - fn distance_squared_to(self, other: Vector2) -> f32; - /// Returns the vector with a maximum length by limiting its length to `length`. - fn clamped(self, length: f32) -> Self; - /// Internal API for converting to `sys` representation. Makes it possible to remove - /// `transmute`s elsewhere. - #[doc(hidden)] - #[allow(clippy::wrong_self_convention)] - fn to_sys(self) -> sys::godot_vector2; - /// Internal API for converting to `sys` representation. Makes it possible to remove - /// `transmute`s elsewhere. - #[doc(hidden)] - fn sys(&self) -> *const sys::godot_vector2; - /// Internal API for converting from `sys` representation. Makes it possible to remove - /// `transmute`s elsewhere. - #[doc(hidden)] - fn from_sys(v: sys::godot_vector2) -> Self; -} + /// For example, Vector2.RIGHT.angle() will return zero, Vector2.DOWN.angle() will return PI / 2 + /// (a quarter turn, or 90 degrees), and Vector2(1, -1).angle() will return -PI / 4 (a negative + /// eighth turn, or -45 degrees). + /// + /// Equivalent to the result of @GDScript.atan2 when called with the vector's y and x as + /// parameters: atan2(y, x). + #[inline] + pub fn angle(self) -> f32 { + self.glam().angle_between(Vec2::X) + } + + /// Returns the angle to the given vector, in radians. + #[inline] + pub fn angle_to(self, to: Self) -> f32 { + self.glam().angle_between(to.glam()) + } -impl Vector2Godot for Vector2 { + /// Returns the angle between the line connecting the two points and the X axis, in radians #[inline] - fn aspect(self) -> f32 { + pub fn angle_to_point(self, to: Self) -> f32 { + self.glam().angle_between(to.glam() - self.glam()) + } + + /// Returns the aspect ratio of this vector, the ratio of x to y. + #[inline] + pub fn aspect(self) -> f32 { self.x / self.y } + /// Returns the vector "bounced off" from a plane defined by the given normal. + #[inline] + pub fn bounce(self, n: Self) -> Self { + -self.reflect(n) + } + + /// Returns the vector with all components rounded up (towards positive infinity). #[inline] - fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, t: f32) -> Self { + pub fn ceil(self) -> Self { + Self::gd(self.glam().ceil()) + } + + /// Returns the vector with a maximum length by limiting its length to `length`. + #[inline] + pub fn clamped(self, length: f32) -> Self { + Self::gd(self.glam().clamp_length_max(length)) + } + + /// Returns the cross product of this vector and `with`. + #[inline] + pub fn cross(self, with: Self) -> f32 { + self.x * with.y - self.y * with.x + } + + /// Cubicly interpolates between this vector and `b` using `pre_a` and `post_b` as handles, + /// and returns the result at position `t`. `t` is in the range of 0.0 - 1.0, representing + /// the amount of interpolation. + #[inline] + pub fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, t: f32) -> Self { let v0 = pre_a; let v1 = self; let v2 = b; @@ -70,83 +101,284 @@ impl Vector2Godot for Vector2 { * 0.5 } + /// Returns the normalized vector pointing from this vector to `other`. #[inline] - fn rotated(self, angle: Angle) -> Self { - let r = Rotation2D::new(angle); - r.transform_vector(self) + pub fn direction_to(self, other: Self) -> Self { + (other - self).normalized() } + /// Returns the squared distance to `other`. + /// + /// This method runs faster than distance_to, so prefer it if you need to compare vectors or + /// need the squared distance for some formula. #[inline] - fn slide(self, normal: Self) -> Self { - self - normal * self.dot(normal) + pub fn distance_squared_to(self, other: Self) -> f32 { + self.glam().distance_squared(other.glam()) } + /// Returns the distance to `other`. #[inline] - fn snapped(self, by: Self) -> Self { - Vector2::new( - if by.x != 0.0 { - (self.x / by.x + 0.5).floor() * by.x - } else { - self.x - }, - if by.y != 0.0 { - (self.y / by.y + 0.5).floor() * by.y - } else { - self.y - }, - ) + pub fn distance_to(self, other: Self) -> f32 { + self.glam().distance(other.glam()) + } + + /// Returns the dot product of this vector and `with`. This can be used to compare the angle + /// between two vectors. For example, this can be used to determine whether an enemy is facing + /// the player. + /// + /// The dot product will be 0 for a straight angle (90 degrees), greater than 0 for angles + /// narrower than 90 degrees and lower than 0 for angles wider than 90 degrees. + /// + /// When using unit (normalized) vectors, the result will always be between -1.0 (180 degree + /// angle) when the vectors are facing opposite directions, and 1.0 (0 degree angle) when the + /// vectors are aligned. + /// + /// Note: `a.dot(b)` is equivalent to `b.dot(a)`. + #[inline] + pub fn dot(self, with: Self) -> f32 { + self.glam().dot(with.glam()) } + /// Returns the vector with all components rounded down (towards negative infinity). #[inline] - fn tangent(self) -> Self { - Vector2::new(self.y, -self.x) + pub fn floor(self) -> Self { + Self::gd(self.glam().floor()) } + /// Returns true if this vector and v are approximately equal, by running + /// `@GDScript.is_equal_approx` on each component. #[inline] - fn move_towards(self, to: Vector2, delta: f32) -> Self { + pub fn is_equal_approx(self, v: Self) -> bool { + self.x.is_equal_approx(v.x) && self.y.is_equal_approx(v.y) + } + + /// Returns `true` if the vector is normalized, and false otherwise. + #[inline] + pub fn is_normalized(self) -> bool { + self.glam().is_normalized() + } + + /// Returns the length (magnitude) of this vector. + #[inline] + pub fn length(self) -> f32 { + self.glam().length() + } + + /// Returns the squared length (squared magnitude) of this vector. + /// + /// This method runs faster than length, so prefer it if you need to compare vectors or need + /// the squared distance for some formula. + #[inline] + pub fn length_squared(self) -> f32 { + self.glam().length_squared() + } + + /// Returns the result of the 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. + #[inline] + pub fn linear_interpolate(self, b: Self, t: f32) -> Self { + Self::gd(self.glam().lerp(b.glam(), t)) + } + + /// Returns `self` moved towards `to` by the distance `delta`, clamped by `to`. + #[inline] + pub fn move_toward(self, to: Vector2, delta: f32) -> Self { let vd = to - self; let len = vd.length(); if len <= delta || approx::abs_diff_eq!(0.0, len) { to } else { - Vector2::lerp(self, to, delta / len) + Self::linear_interpolate(self, to, delta / len) } } + /// Returns the vector scaled to unit length. Equivalent to `v / v.length()`. + #[inline] + pub fn normalized(self) -> Self { + Self::gd(self.glam().normalize()) + } + + /// Returns a vector composed of the @GDScript.fposmod of this vector's components and `rem`. + #[inline] + pub fn posmod(self, rem: f32) -> Self { + self.posmodv(Self::new(rem, rem)) + } + + /// Returns a vector composed of the @GDScript.fposmod of this vector's components and `remv` components. + #[inline] + pub fn posmodv(self, remv: Self) -> Self { + Self::new(self.x.rem_euclid(remv.x), self.y.rem_euclid(remv.y)) + } + + /// Returns the vector projected onto the vector `b`. + #[inline] + pub fn project(self, b: Self) -> Self { + b * (self.dot(b) / b.length_squared()) + } + + /// Returns the vector reflected from a plane defined by the given normal. + #[inline] + pub fn reflect(self, n: Self) -> Self { + n * self.dot(n) * 2.0 - self + } + + /// Returns the vector rotated by `angle` radians. #[inline] - fn direction_to(self, other: Vector2) -> Vector2 { - (other - self).normalize() + pub fn rotated(self, angle: f32) -> Self { + let (cos, sin) = (angle.cos(), angle.sin()); + Self::new(cos * self.x + sin * self.y, sin * self.x + cos * self.y) } + /// Returns the vector with all components rounded to the nearest integer, with halfway cases + /// rounded away from zero. #[inline] - fn distance_to(self, other: Vector2) -> f32 { - (other - self).length() + pub fn round(self) -> Self { + Self::gd(self.glam().round()) } + /// Returns the vector with each component set to one or negative one, depending on the signs + /// of the components, or zero if the component is zero, by calling @GDScript.sign on each + /// component. #[inline] - fn distance_squared_to(self, other: Vector2) -> f32 { - (other - self).square_length() + pub fn sign(self) -> Self { + Self::gd(self.glam().signum()) } + /// 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(theta * t) + } + + /// Returns the component of the vector along a plane defined by the given normal. #[inline] - fn clamped(self, length: f32) -> Self { - self.clamp_length(0.0, length) + pub fn slide(self, normal: Self) -> Self { + self - normal * self.dot(normal) } + /// Returns the vector snapped to a grid with the given size. #[inline] - fn to_sys(self) -> sys::godot_vector2 { + pub fn snapped(self, by: Self) -> Self { + Vector2::new( + if by.x != 0.0 { + (self.x / by.x + 0.5).floor() * by.x + } else { + self.x + }, + if by.y != 0.0 { + (self.y / by.y + 0.5).floor() * by.y + } else { + self.y + }, + ) + } + + /// Returns a perpendicular vector. + #[inline] + pub fn tangent(self) -> Self { + Vector2::new(self.y, -self.x) + } + + /// Internal API for converting to `sys` representation. Makes it possible to remove + /// `transmute`s elsewhere. + #[doc(hidden)] + #[allow(clippy::wrong_self_convention)] + #[inline] + pub fn to_sys(self) -> sys::godot_vector2 { unsafe { std::mem::transmute(self) } } + /// Internal API for converting to `sys` representation. Makes it possible to remove + /// `transmute`s elsewhere. + #[doc(hidden)] #[inline] - fn sys(&self) -> *const sys::godot_vector2 { + pub fn sys(&self) -> *const sys::godot_vector2 { self as *const _ as *const _ } + /// Internal API for converting from `sys` representation. Makes it possible to remove + /// `transmute`s elsewhere. + #[doc(hidden)] #[inline] - fn from_sys(v: sys::godot_vector2) -> Self { + pub fn from_sys(v: sys::godot_vector2) -> Self { unsafe { std::mem::transmute(v) } } + + fn glam(self) -> Vec2 { + Vec2::new(self.x, self.y) + } + + fn gd(from: Vec2) -> Self { + Self::new(from.x, from.y) + } +} + +macro_rules! derive_op_impl { + ($trait:ident, $func:ident) => { + impl $trait for Vector2 { + type Output = Self; + + #[inline] + fn $func(self, with: Self) -> Self { + Self::gd(self.glam().$func(with.glam())) + } + } + }; + ($trait:ident, $func:ident, $in_type:ty) => { + impl $trait<$in_type> for Vector2 { + type Output = Self; + + #[inline] + fn $func(self, with: $in_type) -> Self { + Self::gd(self.glam().$func(with)) + } + } + }; +} + +macro_rules! derive_assign_op_impl { + ($trait:ident, $func:ident, $op_func:ident) => { + impl $trait for Vector2 { + #[inline] + fn $func(&mut self, with: Self) { + *self = self.$op_func(with); + } + } + }; + ($trait:ident, $func:ident, $op_func:ident, $in_type:ty) => { + impl $trait<$in_type> for Vector2 { + #[inline] + fn $func(&mut self, with: $in_type) { + *self = self.$op_func(with); + } + } + }; +} + +derive_op_impl!(Add, add); +derive_op_impl!(Sub, sub); +derive_op_impl!(Mul, mul); +derive_op_impl!(Div, div); +derive_op_impl!(Mul, mul, f32); +derive_op_impl!(Div, div, f32); +derive_assign_op_impl!(AddAssign, add_assign, add); +derive_assign_op_impl!(SubAssign, sub_assign, sub); +derive_assign_op_impl!(MulAssign, mul_assign, mul); +derive_assign_op_impl!(DivAssign, div_assign, div); +derive_assign_op_impl!(MulAssign, mul_assign, mul, f32); +derive_assign_op_impl!(DivAssign, div_assign, div, f32); + +impl Neg for Vector2 { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + Self::gd(-self.glam()) + } } godot_test!( @@ -189,7 +421,6 @@ godot_test!( #[cfg(test)] mod tests { - use crate::core_types::vector2::Vector2Godot; use crate::core_types::Vector2; #[test] @@ -216,11 +447,11 @@ mod tests { #[test] fn cubic_interpolate_is_sane() { - use euclid::approxeq::ApproxEq; + //use euclid::approxeq::ApproxEq; use Vector2 as V; assert!( - V::new(4.7328, -6.7936).approx_eq(&V::new(5.4, -6.8).cubic_interpolate( + V::new(4.7328, -6.7936).is_equal_approx(V::new(5.4, -6.8).cubic_interpolate( V::new(-1.2, 0.8), V::new(1.2, 10.3), V::new(-5.4, 4.2), @@ -229,7 +460,7 @@ mod tests { ); assert!( - V::new(-3.8376, 2.9384).approx_eq(&V::new(-4.2, 1.4).cubic_interpolate( + V::new(-3.8376, 2.9384).is_equal_approx(V::new(-4.2, 1.4).cubic_interpolate( V::new(-3.7, 2.1), V::new(5.4, -8.5), V::new(-10.8, -6.6), @@ -240,31 +471,29 @@ mod tests { #[test] fn slide_is_sane() { - use euclid::approxeq::ApproxEq; use Vector2 as V; let cases = &[ (V::new(1.0, 1.0), V::new(0.0, 1.0), V::new(1.0, 0.0)), ( V::new(3.0, 4.0), - V::new(-3.0, 1.0).normalize(), + V::new(-3.0, 1.0).normalized(), V::new(1.5, 4.5), ), ( V::new(-2.0, 1.0), - V::new(-1.0, 3.0).normalize(), + V::new(-1.0, 3.0).normalized(), V::new(-1.5, -0.5), ), ]; for &(v, normal, expected) in cases { - assert!(expected.approx_eq(&v.slide(normal))); + assert!(expected.is_equal_approx(v.slide(normal))); } } #[test] fn snapped_is_sane() { - use euclid::approxeq::ApproxEq; use Vector2 as V; let cases = &[ @@ -274,7 +503,7 @@ mod tests { ]; for &(v, by, expected) in cases { - assert!(expected.approx_eq(&v.snapped(by))); + assert!(expected.is_equal_approx(v.snapped(by))); } } } diff --git a/gdnative-core/src/core_types/vector3.rs b/gdnative-core/src/core_types/vector3.rs index 733d5d333..6291b1564 100644 --- a/gdnative-core/src/core_types/vector3.rs +++ b/gdnative-core/src/core_types/vector3.rs @@ -1,4 +1,15 @@ -use crate::core_types::Vector3; +use super::geom::Basis; +use super::IsEqualApprox; +use glam::Vec3A; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(C)] +pub struct Vector3 { + pub x: f32, + pub y: f32, + pub z: f32, +} #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u32)] @@ -10,64 +21,441 @@ pub enum Axis { /// Helper methods for `Vector3`. /// -/// Trait used to provide additional methods that are equivalent to Godot's methods. /// See the official [`Godot documentation`](https://docs.godotengine.org/en/3.1/classes/class_vector3.html). -pub trait Vector3Godot { - /// Internal API for converting to `sys` representation. Makes it possible to remove - /// `transmute`s elsewhere. +impl Vector3 { + pub const ZERO: Self = Self::new(0.0, 0.0, 0.0); + pub const ONE: Self = Self::new(1.0, 1.0, 1.0); + pub const INF: Self = Self::new(f32::INFINITY, f32::INFINITY, f32::INFINITY); + pub const LEFT: Self = Self::new(-1.0, 0.0, 0.0); + pub const RIGHT: Self = Self::new(1.0, 0.0, 0.0); + pub const UP: Self = Self::new(0.0, 1.0, 0.0); + pub const DOWN: Self = Self::new(0.0, -1.0, 0.0); + pub const FORWARD: Self = Self::new(0.0, 0.0, -1.0); + pub const BACK: Self = Self::new(0.0, 0.0, 1.0); + + /// Returns a Vector3 with the given components. + #[inline] + pub const fn new(x: f32, y: f32, z: f32) -> Self { + Self { x, y, z } + } + + /// Returns a new vector with all components in absolute values (i.e. positive). + #[inline] + pub fn abs(self) -> Self { + Self::gd(self.glam().abs()) + } + + /// Returns the minimum angle to the given vector, in radians. + #[inline] + pub fn angle_to(self, to: Self) -> f32 { + self.glam().angle_between(to.glam()) + } + + /// Returns the vector "bounced off" from a plane defined by the given normal. + #[inline] + pub fn bounce(self, n: Self) -> Self { + -self.reflect(n) + } + + /// Returns a new vector with all components rounded up (towards positive infinity). + #[inline] + pub fn ceil(self) -> Self { + Self::gd(self.glam().ceil()) + } + + /// Returns the cross product of this vector and b. + #[inline] + pub fn cross(self, b: Self) -> Self { + Self::gd(self.glam().cross(b.glam())) + } + + /// Performs a cubic interpolation between vectors pre_a, a, b, post_b (a is current), by the + /// given amount t. t is on the range of 0.0 to 1.0, representing the amount of interpolation. + #[inline] + pub fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, t: f32) -> Self { + let mut p = (pre_a, self, b, post_b); + + { + let ab = p.0.distance_to(p.1); + let bc = p.1.distance_to(p.2); + let cd = p.2.distance_to(p.3); + + if ab > 0.0 { + p.0 = p.1 + (p.0 - p.1) * (bc / ab); + } + if cd > 0.0 { + p.3 = p.2 + (p.3 - p.2) * (bc / cd); + } + } + + let t = (t, t * t, t * t * t); + + 0.5 * ((p.1 * 2.0) + + (-p.0 + p.2) * t.0 + + (2.0 * p.0 - 5.0 * p.1 + 4.0 * p.2 - p.3) * t.1 + + (-p.0 + 3.0 * p.1 - 3.0 * p.2 + p.3) * t.2) + } /// Returns the normalized vector pointing from this vector to `other`. - fn direction_to(self, other: Vector3) -> Vector3; - /// Returns the distance to `other`. - fn distance_to(self, other: Vector3) -> f32; + #[inline] + pub fn direction_to(self, other: Vector3) -> Vector3 { + Self::gd((other.glam() - self.glam()).normalize()) + } + /// Returns the squared distance to `other`. /// /// This method runs faster than distance_to, so prefer it if you need to compare vectors or /// need the squared distance for some formula. - fn distance_squared_to(self, other: Vector3) -> f32; + #[inline] + pub fn distance_squared_to(self, other: Vector3) -> f32 { + other.glam().distance_squared(self.glam()) + } + + /// Returns the distance to `other`. + #[inline] + pub fn distance_to(self, other: Vector3) -> f32 { + other.glam().distance(self.glam()) + } + + /// Returns the dot product of this vector and b. This can be used to compare the angle between + /// two vectors. For example, this can be used to determine whether an enemy is facing the player. + /// + /// The dot product will be 0 for a straight angle (90 degrees), greater than 0 for angles + /// narrower than 90 degrees and lower than 0 for angles wider than 90 degrees. + /// + /// When using unit (normalized) vectors, the result will always be between -1.0 (180 degree + /// angle) when the vectors are facing opposite directions, and 1.0 (0 degree angle) when the + /// vectors are aligned. + /// + /// Note: a.dot(b) is equivalent to b.dot(a). + #[inline] + pub fn dot(self, other: Self) -> f32 { + self.glam().dot(other.glam()) + } + + /// Returns a new vector with all components rounded down (towards negative infinity). + #[inline] + pub fn floor(self) -> Self { + Self::gd(self.glam().floor()) + } + + /// Returns the inverse of the vector. This is the same as + /// `Vector3::new(1.0 / self.x, 1.0 / self.y, 1.0 / self.z)`. + #[inline] + pub fn inverse(self) -> Self { + Self::new(1.0 / self.x, 1.0 / self.y, 1.0 / self.z) + } + + /// Returns `true` if this vector and v are approximately equal, by running `relative_eq` on + /// each component. + #[inline] + pub fn is_equal_approx(self, v: Self) -> bool { + self.x.is_equal_approx(v.x) && self.y.is_equal_approx(v.y) && self.z.is_equal_approx(v.z) + } + + /// Returns `true` if the vector is normalized, and `false` otherwise. + #[inline] + pub fn is_normalized(self) -> bool { + self.glam().is_normalized() + } + + /// Returns the length (magnitude) of this vector. + #[inline] + pub fn length(self) -> f32 { + self.glam().length() + } + + /// Returns the squared length (squared magnitude) of this vector. + /// + /// This method runs faster than length, so prefer it if you need to compare vectors or need + /// the squared distance for some formula. + #[inline] + pub fn length_squared(self) -> f32 { + self.glam().length_squared() + } + + /// Returns the result of the 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. + #[inline] + pub fn linear_interpolate(self, b: Self, t: f32) -> Self { + Self::gd(self.glam().lerp(b.glam(), t)) + } + + /// Returns the axis of the vector's largest value. See `Axis` enum. + /// If all components are equal, this method returns `Axis::X`. + #[inline] + #[allow(clippy::collapsible_else_if)] + pub fn max_axis(self) -> Axis { + if self.z > self.y { + if self.z > self.x { + Axis::Z + } else { + Axis::X + } + } else { + if self.y > self.x { + Axis::Y + } else { + Axis::X + } + } + } + + /// Returns the axis of the vector's smallest value. See `Axis` enum. + /// If all components are equal, this method returns `Axis::Z`. + #[inline] + #[allow(clippy::collapsible_else_if)] + pub fn min_axis(self) -> Axis { + if self.x < self.y { + if self.x < self.z { + Axis::X + } else { + Axis::Z + } + } else { + if self.y < self.z { + Axis::Y + } else { + Axis::Z + } + } + } + + /// Moves this vector toward `to` by the fixed `delta` amount. + #[inline] + pub fn move_toward(self, to: Self, delta: f32) -> Self { + let vd = to - self; + let len = vd.length(); + if len <= delta || approx::abs_diff_eq!(0.0, len) { + to + } else { + self.linear_interpolate(to, delta / len) + } + } + + /// Returns the vector scaled to unit length. Equivalent to `v / v.length()`. + #[inline] + pub fn normalized(self) -> Self { + Self::gd(self.glam().normalize()) + } + + /// Returns the outer product with `b`. + #[inline] + pub fn outer(self, b: Self) -> Basis { + Basis::from_elements([b * self.x, b * self.y, b * self.z]) + } + + /// Returns a vector composed of the `rem_euclid` of this vector's components and `mod`. + #[inline] + pub fn posmod(self, rem: f32) -> Self { + self.posmodv(Self::new(rem, rem, rem)) + } + + /// Returns a vector composed of the `rem_euclid` of this vector's components and `remv` + /// components. + #[inline] + pub fn posmodv(self, remv: Self) -> Self { + Self::new( + self.x.rem_euclid(remv.x), + self.y.rem_euclid(remv.y), + self.z.rem_euclid(remv.z), + ) + } + + /// Returns this vector projected onto another vector `b`. + #[inline] + pub fn project(self, b: Self) -> Self { + b * (self.dot(b) / b.length_squared()) + } + + /// Returns this vector reflected from a plane defined by the given normal. + #[inline] + pub fn reflect(self, n: Self) -> Self { + n * self.dot(n) * 2.0 - self + } + + /// Rotates this vector around a given axis by `phi` radians. The axis must be a normalized + /// vector. + #[inline] + pub fn rotated(self, axis: Self, phi: f32) -> Self { + Basis::from_axis_angle(&axis, phi) * self + } + + /// Returns this vector with all components rounded to the nearest integer, with halfway cases + /// rounded away from zero. + #[inline] + pub fn round(self) -> Self { + Self::gd(self.glam().round()) + } + + /// Returns a vector with each component set to one or negative one, depending on the signs of + /// this vector's components, or zero if the component is zero, by calling `signum` on each + /// component. + #[inline] + pub fn sign(self) -> Self { + Self::gd(self.glam().signum()) + } + + /// 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) + } + + /// Returns this vector slid along a plane defined by the given normal. + #[inline] + pub fn slide(self, n: Self) -> Self { + self - n * self.dot(n) + } + + /// Returns this vector with each component snapped to the nearest multiple of step. + /// This can also be used to round to an arbitrary number of decimals. + #[inline] + pub fn snapped(self, by: Self) -> Self { + let stepify = |v: f32, s: f32| { + if by.x != 0.0 { + (v / s + 0.5).floor() * s + } else { + v + } + }; + Self::new( + stepify(self.x, by.x), + stepify(self.y, by.y), + stepify(self.z, by.z), + ) + } + + /// Returns a diagonal matrix with the vector as main diagonal. + /// + /// This is equivalent to a Basis with no rotation or shearing and this vector's components set + /// as the scale. + #[inline] + pub fn to_diagonal_matrix(self) -> Basis { + Basis::from_diagonal(self) + } #[doc(hidden)] #[allow(clippy::wrong_self_convention)] - fn to_sys(self) -> sys::godot_vector3; + #[inline] + pub fn to_sys(self) -> sys::godot_vector3 { + unsafe { std::mem::transmute(self) } + } + /// Internal API for converting to `sys` representation. Makes it possible to remove /// `transmute`s elsewhere. #[doc(hidden)] - fn sys(&self) -> *const sys::godot_vector3; + #[inline] + pub fn sys(&self) -> *const sys::godot_vector3 { + self as *const _ as *const _ + } + /// Internal API for converting from `sys` representation. Makes it possible to remove /// `transmute`s elsewhere. #[doc(hidden)] - fn from_sys(v: sys::godot_vector3) -> Self; -} - -impl Vector3Godot for Vector3 { #[inline] - fn direction_to(self, other: Vector3) -> Vector3 { - (other - self).normalize() + pub fn from_sys(v: sys::godot_vector3) -> Self { + unsafe { std::mem::transmute(v) } } #[inline] - fn distance_to(self, other: Vector3) -> f32 { - (other - self).length() + fn glam(self) -> Vec3A { + Vec3A::new(self.x, self.y, self.z) } #[inline] - fn distance_squared_to(self, other: Vector3) -> f32 { - (other - self).square_length() + fn gd(from: Vec3A) -> Self { + Self::new(from.x, from.y, from.z) } +} +impl AsRef<[f32; 3]> for Vector3 { #[inline] - fn to_sys(self) -> sys::godot_vector3 { - unsafe { std::mem::transmute(self) } + fn as_ref(&self) -> &[f32; 3] { + // SAFETY: Vector3 is repr(C) + unsafe { &*(self as *const Vector3 as *const [f32; 3]) } } +} - #[inline] - fn sys(&self) -> *const sys::godot_vector3 { - self as *const _ as *const _ - } +macro_rules! derive_op_impl { + ($trait:ident, $func:ident) => { + impl $trait for Vector3 { + type Output = Self; + + #[inline] + fn $func(self, with: Self) -> Self { + Self::gd(self.glam().$func(with.glam())) + } + } + }; + ($trait:ident, $func:ident, $in_type:ty) => { + impl $trait<$in_type> for Vector3 { + type Output = Self; + + #[inline] + fn $func(self, with: $in_type) -> Self { + Self::gd(self.glam().$func(with)) + } + } + }; +} + +macro_rules! derive_op_impl_rev { + ($trait:ident, $func:ident, $in_type:ty) => { + impl $trait for $in_type { + type Output = Vector3; + + #[inline] + fn $func(self, with: Self::Output) -> Self::Output { + $trait::$func(with, self) + } + } + }; +} + +macro_rules! derive_assign_op_impl { + ($trait:ident, $func:ident, $op_func:ident) => { + impl $trait for Vector3 { + #[inline] + fn $func(&mut self, with: Self) { + *self = self.$op_func(with); + } + } + }; + ($trait:ident, $func:ident, $op_func:ident, $in_type:ty) => { + impl $trait<$in_type> for Vector3 { + #[inline] + fn $func(&mut self, with: $in_type) { + *self = self.$op_func(with); + } + } + }; +} + +derive_op_impl!(Add, add); +derive_op_impl!(Sub, sub); +derive_op_impl!(Mul, mul); +derive_op_impl!(Div, div); +derive_op_impl!(Mul, mul, f32); +derive_op_impl!(Div, div, f32); +derive_op_impl_rev!(Mul, mul, f32); +derive_assign_op_impl!(AddAssign, add_assign, add); +derive_assign_op_impl!(SubAssign, sub_assign, sub); +derive_assign_op_impl!(MulAssign, mul_assign, mul); +derive_assign_op_impl!(DivAssign, div_assign, div); +derive_assign_op_impl!(MulAssign, mul_assign, mul, f32); +derive_assign_op_impl!(DivAssign, div_assign, div, f32); + +impl Neg for Vector3 { + type Output = Self; #[inline] - fn from_sys(v: sys::godot_vector3) -> Self { - unsafe { std::mem::transmute(v) } + fn neg(self) -> Self { + Self::gd(-self.glam()) } } diff --git a/gdnative/src/prelude.rs b/gdnative/src/prelude.rs index 0fc6a068b..cb3a35fa5 100644 --- a/gdnative/src/prelude.rs +++ b/gdnative/src/prelude.rs @@ -1,13 +1,11 @@ pub use gdnative_core::core_types::{ - self, error::GodotError, Aabb, Angle, Basis, ByteArray, Color, ColorArray, Dictionary, - Float32Array, GodotString, Int32Array, NodePath, Plane, Point2, Point3, Quat, Rect2, Rid, - Rotation2D, Rotation3D, Size2, StringArray, StringName, Transform, Transform2D, TypedArray, - Variant, VariantArray, VariantDispatch, VariantOperator, VariantType, Vector2, Vector2Array, - Vector3, Vector3Array, + self, error::GodotError, Aabb, Basis, ByteArray, Color, ColorArray, Dictionary, Float32Array, + GodotString, Int32Array, NodePath, Plane, Quat, Rect2, Rid, StringArray, StringName, Transform, + Transform2D, TypedArray, Variant, VariantArray, VariantDispatch, VariantOperator, VariantType, + Vector2, Vector2Array, Vector3, Vector3Array, }; pub use gdnative_core::core_types::{ - FromVariant, FromVariantError, OwnedToVariant, ToVariant, ToVariantEq, Vector2Godot, - Vector3Godot, + FromVariant, FromVariantError, OwnedToVariant, ToVariant, ToVariantEq, }; pub use gdnative_core::object::{ diff --git a/gdnative/tests/ui/derive_fail_methods_missing_new.stderr b/gdnative/tests/ui/derive_fail_methods_missing_new.stderr index f3665cbb4..0f5a0d668 100644 --- a/gdnative/tests/ui/derive_fail_methods_missing_new.stderr +++ b/gdnative/tests/ui/derive_fail_methods_missing_new.stderr @@ -12,5 +12,14 @@ error[E0599]: no function or associated item named `new` found for struct `Foo` candidate #1: `UserData` candidate #2: `gdnative::GodotObject` candidate #3: `PtrWrapper` - candidate #4: `parking_lot_core::thread_parker::ThreadParkerT` + candidate #4: `glam::core::traits::matrix::Matrix2x2` + candidate #5: `glam::core::traits::matrix::Matrix3x3` + candidate #6: `glam::core::traits::matrix::Matrix4x4` + candidate #7: `glam::core::traits::vector::MaskVector2` + candidate #8: `glam::core::traits::vector::MaskVector3` + candidate #9: `glam::core::traits::vector::MaskVector4` + candidate #10: `glam::core::traits::vector::Vector2` + candidate #11: `glam::core::traits::vector::Vector3` + candidate #12: `glam::core::traits::vector::Vector4` + candidate #13: `parking_lot_core::thread_parker::ThreadParkerT` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/gdnative/tests/ui/from_variant_fail_01.stderr b/gdnative/tests/ui/from_variant_fail_01.stderr index 27b6b6218..815d6bc2e 100644 --- a/gdnative/tests/ui/from_variant_fail_01.stderr +++ b/gdnative/tests/ui/from_variant_fail_01.stderr @@ -1,6 +1,6 @@ error: Missing macro arguments - expecting #[variant(...)]. See documentation: - https://docs.rs/gdnative/0.9.0/gdnative/core_types/trait.ToVariant.html#field-attributes +expecting #[variant(...)]. See documentation: +https://docs.rs/gdnative/0.9.0/gdnative/core_types/trait.ToVariant.html#field-attributes --> $DIR/from_variant_fail_01.rs:5:7 | 5 | #[variant] diff --git a/gdnative/tests/ui/from_variant_fail_02.stderr b/gdnative/tests/ui/from_variant_fail_02.stderr index 7a8757103..471c23e06 100644 --- a/gdnative/tests/ui/from_variant_fail_02.stderr +++ b/gdnative/tests/ui/from_variant_fail_02.stderr @@ -1,7 +1,7 @@ error: Invalid syntax - expecting #[variant(...)]. See documentation: - https://docs.rs/gdnative/0.9.0/gdnative/core_types/trait.ToVariant.html#field-attributes +expecting #[variant(...)]. See documentation: +https://docs.rs/gdnative/0.9.0/gdnative/core_types/trait.ToVariant.html#field-attributes --> $DIR/from_variant_fail_02.rs:6:15 | 6 | #[variant(baz::quux)] - | ^^^ + | ^^^^^^^^^ diff --git a/gdnative/tests/ui/from_variant_fail_03.stderr b/gdnative/tests/ui/from_variant_fail_03.stderr index 6ad0fa3b6..e98d3a61c 100644 --- a/gdnative/tests/ui/from_variant_fail_03.stderr +++ b/gdnative/tests/ui/from_variant_fail_03.stderr @@ -1,6 +1,6 @@ error: Found baz::quux, expected one of: - to_variant_with, from_variant_with, with, skip_to_variant, skip_from_variant, skip +to_variant_with, from_variant_with, with, skip_to_variant, skip_from_variant, skip --> $DIR/from_variant_fail_03.rs:6:15 | 6 | #[variant(baz::quux = "path::to::function")] - | ^^^ + | ^^^^^^^^^ diff --git a/gdnative/tests/ui/from_variant_fail_07.stderr b/gdnative/tests/ui/from_variant_fail_07.stderr index fc4adcd41..f75c8357a 100644 --- a/gdnative/tests/ui/from_variant_fail_07.stderr +++ b/gdnative/tests/ui/from_variant_fail_07.stderr @@ -1,5 +1,5 @@ error: unknown argument, expected one of: - to_variant_with, from_variant_with, with, skip_to_variant, skip_from_variant, skip +to_variant_with, from_variant_with, with, skip_to_variant, skip_from_variant, skip --> $DIR/from_variant_fail_07.rs:5:15 | 5 | #[variant(aoeu = "aoeu")] diff --git a/gdnative/tests/ui/to_variant_fail_01.stderr b/gdnative/tests/ui/to_variant_fail_01.stderr index f7c61a9b4..8c818d723 100644 --- a/gdnative/tests/ui/to_variant_fail_01.stderr +++ b/gdnative/tests/ui/to_variant_fail_01.stderr @@ -1,6 +1,6 @@ error: Missing macro arguments - expecting #[variant(...)]. See documentation: - https://docs.rs/gdnative/0.9.0/gdnative/core_types/trait.ToVariant.html#field-attributes +expecting #[variant(...)]. See documentation: +https://docs.rs/gdnative/0.9.0/gdnative/core_types/trait.ToVariant.html#field-attributes --> $DIR/to_variant_fail_01.rs:5:7 | 5 | #[variant] diff --git a/gdnative/tests/ui/to_variant_fail_02.stderr b/gdnative/tests/ui/to_variant_fail_02.stderr index b2df1e9af..931a230ab 100644 --- a/gdnative/tests/ui/to_variant_fail_02.stderr +++ b/gdnative/tests/ui/to_variant_fail_02.stderr @@ -1,7 +1,7 @@ error: Invalid syntax - expecting #[variant(...)]. See documentation: - https://docs.rs/gdnative/0.9.0/gdnative/core_types/trait.ToVariant.html#field-attributes +expecting #[variant(...)]. See documentation: +https://docs.rs/gdnative/0.9.0/gdnative/core_types/trait.ToVariant.html#field-attributes --> $DIR/to_variant_fail_02.rs:6:15 | 6 | #[variant(baz::quux)] - | ^^^ + | ^^^^^^^^^ diff --git a/gdnative/tests/ui/to_variant_fail_03.stderr b/gdnative/tests/ui/to_variant_fail_03.stderr index f416db5c3..fc2cf3328 100644 --- a/gdnative/tests/ui/to_variant_fail_03.stderr +++ b/gdnative/tests/ui/to_variant_fail_03.stderr @@ -1,6 +1,6 @@ error: Found baz::quux, expected one of: - to_variant_with, from_variant_with, with, skip_to_variant, skip_from_variant, skip +to_variant_with, from_variant_with, with, skip_to_variant, skip_from_variant, skip --> $DIR/to_variant_fail_03.rs:6:15 | 6 | #[variant(baz::quux = "path::to::function")] - | ^^^ + | ^^^^^^^^^ diff --git a/gdnative/tests/ui/to_variant_fail_07.stderr b/gdnative/tests/ui/to_variant_fail_07.stderr index 066f14912..d26655713 100644 --- a/gdnative/tests/ui/to_variant_fail_07.stderr +++ b/gdnative/tests/ui/to_variant_fail_07.stderr @@ -1,5 +1,5 @@ error: unknown argument, expected one of: - to_variant_with, from_variant_with, with, skip_to_variant, skip_from_variant, skip +to_variant_with, from_variant_with, with, skip_to_variant, skip_from_variant, skip --> $DIR/to_variant_fail_07.rs:5:15 | 5 | #[variant(aoeu = "aoeu")] From b1213f946fdace191cf293cae2dc7aa17cf3e9a6 Mon Sep 17 00:00:00 2001 From: toasteater <48371905+toasteater@users.noreply.github.com> Date: Tue, 30 Mar 2021 09:23:00 +0000 Subject: [PATCH 2/2] Run tests that depend on Span::join only on nightly --- gdnative/Cargo.toml | 1 + gdnative/tests/ui.rs | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/gdnative/Cargo.toml b/gdnative/Cargo.toml index d1df97eff..0bb21ea70 100644 --- a/gdnative/Cargo.toml +++ b/gdnative/Cargo.toml @@ -26,6 +26,7 @@ gdnative-bindings = { optional = true, path = "../gdnative-bindings", version = [dev-dependencies] trybuild = "1.0" +rustversion = "1.0" [package.metadata.docs.rs] all-features = true diff --git a/gdnative/tests/ui.rs b/gdnative/tests/ui.rs index 46c980169..14af2f9ac 100644 --- a/gdnative/tests/ui.rs +++ b/gdnative/tests/ui.rs @@ -20,8 +20,7 @@ fn ui_tests() { // ToVariant t.compile_fail("tests/ui/to_variant_fail_01.rs"); - t.compile_fail("tests/ui/to_variant_fail_02.rs"); - t.compile_fail("tests/ui/to_variant_fail_03.rs"); + to_variant_ui_path(&t); t.compile_fail("tests/ui/to_variant_fail_04.rs"); t.compile_fail("tests/ui/to_variant_fail_05.rs"); t.compile_fail("tests/ui/to_variant_fail_06.rs"); @@ -29,10 +28,31 @@ fn ui_tests() { // FromVariant t.compile_fail("tests/ui/from_variant_fail_01.rs"); - t.compile_fail("tests/ui/from_variant_fail_02.rs"); - t.compile_fail("tests/ui/from_variant_fail_03.rs"); + from_variant_ui_path(&t); t.compile_fail("tests/ui/from_variant_fail_04.rs"); t.compile_fail("tests/ui/from_variant_fail_05.rs"); t.compile_fail("tests/ui/from_variant_fail_06.rs"); t.compile_fail("tests/ui/from_variant_fail_07.rs"); } + +// FIXME(rust/issues/54725): Full path spans are only available on nightly as of now +#[rustversion::not(nightly)] +fn to_variant_ui_path(_t: &trybuild::TestCases) {} + +// FIXME(rust/issues/54725): Full path spans are only available on nightly as of now +#[rustversion::nightly] +fn to_variant_ui_path(t: &trybuild::TestCases) { + t.compile_fail("tests/ui/to_variant_fail_02.rs"); + t.compile_fail("tests/ui/to_variant_fail_03.rs"); +} + +// FIXME(rust/issues/54725): Full path spans are only available on nightly as of now +#[rustversion::not(nightly)] +fn from_variant_ui_path(_t: &trybuild::TestCases) {} + +// FIXME(rust/issues/54725): Full path spans are only available on nightly as of now +#[rustversion::nightly] +fn from_variant_ui_path(t: &trybuild::TestCases) { + t.compile_fail("tests/ui/from_variant_fail_02.rs"); + t.compile_fail("tests/ui/from_variant_fail_03.rs"); +}