From 5e7dc53dffdc3ade83b9c542cad052a709464afe Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Tue, 8 Mar 2022 18:12:01 +0100 Subject: [PATCH 1/9] Use Godot 3.4.1 in CI --- .github/workflows/full-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/full-ci.yml b/.github/workflows/full-ci.yml index 501dc6d36..78e059609 100644 --- a/.github/workflows/full-ci.yml +++ b/.github/workflows/full-ci.yml @@ -249,13 +249,13 @@ jobs: include: # Latest Godot with different Rust versions - rust: stable - godot: "3.4.1" + godot: "3.4.3" postfix: '' - rust: nightly - godot: "3.4.1" + godot: "3.4.3" postfix: ' (nightly)' - rust: '1.56' - godot: "3.4.1" + godot: "3.4.3" postfix: ' (msrv 1.56)' # Test with oldest supported engine version From 2b752325a3e239a8663c3beff84a93ed77696d46 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 13 Mar 2022 20:22:01 +0100 Subject: [PATCH 2/9] Examples: package names consistent with directories --- examples/array-export/Cargo.toml | 2 +- examples/dodge-the-creeps/Cargo.toml | 2 +- examples/hello-world/Cargo.toml | 2 +- examples/native-plugin/Cargo.toml | 2 +- examples/scene-create/Cargo.toml | 2 +- examples/spinning-cube/Cargo.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/array-export/Cargo.toml b/examples/array-export/Cargo.toml index d52c8d87f..7f8bd2b77 100644 --- a/examples/array-export/Cargo.toml +++ b/examples/array-export/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "array_export" +name = "array-export" version = "0.1.0" authors = ["The godot-rust developers"] edition = "2021" diff --git a/examples/dodge-the-creeps/Cargo.toml b/examples/dodge-the-creeps/Cargo.toml index ef15843fb..defc51d20 100644 --- a/examples/dodge-the-creeps/Cargo.toml +++ b/examples/dodge-the-creeps/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "dodge_the_creeps" +name = "dodge-the-creeps" version = "0.1.0" authors = ["The godot-rust developers"] publish = false diff --git a/examples/hello-world/Cargo.toml b/examples/hello-world/Cargo.toml index b91d4a686..38defbf6b 100644 --- a/examples/hello-world/Cargo.toml +++ b/examples/hello-world/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "hello_world" +name = "hello-world" version = "0.1.0" authors = ["The godot-rust developers"] publish = false diff --git a/examples/native-plugin/Cargo.toml b/examples/native-plugin/Cargo.toml index 612951127..28d3428b7 100644 --- a/examples/native-plugin/Cargo.toml +++ b/examples/native-plugin/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "native_plugin" +name = "native-plugin" version = "0.1.0" authors = ["The godot-rust developers"] edition = "2021" diff --git a/examples/scene-create/Cargo.toml b/examples/scene-create/Cargo.toml index 30a121d31..654bb58db 100644 --- a/examples/scene-create/Cargo.toml +++ b/examples/scene-create/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "scene_create" +name = "scene-create" version = "0.1.0" authors = ["The godot-rust developers"] publish = false diff --git a/examples/spinning-cube/Cargo.toml b/examples/spinning-cube/Cargo.toml index c164ce0eb..df0606871 100644 --- a/examples/spinning-cube/Cargo.toml +++ b/examples/spinning-cube/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "spinning_cube" +name = "spinning-cube" version = "0.1.0" authors = ["The godot-rust developers"] edition = "2021" From 2cbbc6f015abe8541150339ffe14942c772b2500 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 13 Mar 2022 20:23:59 +0100 Subject: [PATCH 3/9] Update godot-rust version: 0.10.0-rc.0 -> 0.10.0 --- bindings-generator/Cargo.toml | 2 +- gdnative-async/Cargo.toml | 8 ++++---- gdnative-bindings/Cargo.toml | 8 ++++---- gdnative-core/Cargo.toml | 6 +++--- gdnative-derive/Cargo.toml | 2 +- gdnative-sys/Cargo.toml | 2 +- gdnative/Cargo.toml | 10 +++++----- impl/proc-macros/Cargo.toml | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/bindings-generator/Cargo.toml b/bindings-generator/Cargo.toml index aa154ce1d..cb1fdfcc3 100644 --- a/bindings-generator/Cargo.toml +++ b/bindings-generator/Cargo.toml @@ -6,7 +6,7 @@ documentation = "https://docs.rs/crate/gdnative_bindings_generator" repository = "https://github.com/godot-rust/godot-rust" homepage = "https://godot-rust.github.io/" license = "MIT" -version = "0.10.0-rc.0" +version = "0.10.0" workspace = ".." edition = "2021" rust-version = "1.56" diff --git a/gdnative-async/Cargo.toml b/gdnative-async/Cargo.toml index 20c1eb630..4e7ad0602 100644 --- a/gdnative-async/Cargo.toml +++ b/gdnative-async/Cargo.toml @@ -5,7 +5,7 @@ description = "Runtime async support for godot-rust." documentation = "https://docs.rs/crate/gdnative-async" repository = "https://github.com/godot-rust/godot-rust" homepage = "https://godot-rust.github.io/" -version = "0.10.0-rc.0" +version = "0.10.0" license = "MIT" workspace = ".." edition = "2021" @@ -14,9 +14,9 @@ rust-version = "1.56" [features] [dependencies] -gdnative-derive = { path = "../gdnative-derive", version = "=0.10.0-rc.0" } -gdnative-core = { path = "../gdnative-core", version = "=0.10.0-rc.0" } -gdnative-bindings = { path = "../gdnative-bindings", version = "=0.10.0-rc.0" } +gdnative-derive = { path = "../gdnative-derive", version = "=0.10.0" } +gdnative-core = { path = "../gdnative-core", version = "=0.10.0" } +gdnative-bindings = { path = "../gdnative-bindings", version = "=0.10.0" } atomic-waker = "1" crossbeam-channel = "0.5" crossbeam-utils = "0.8" diff --git a/gdnative-bindings/Cargo.toml b/gdnative-bindings/Cargo.toml index a9f0f9b30..1b0b4803a 100644 --- a/gdnative-bindings/Cargo.toml +++ b/gdnative-bindings/Cargo.toml @@ -5,7 +5,7 @@ description = "The Godot game engine's automatcally generated bindings to Godot documentation = "https://docs.rs/crate/gdnative-bindings" repository = "https://github.com/godot-rust/godot-rust" homepage = "https://godot-rust.github.io/" -version = "0.10.0-rc.0" +version = "0.10.0" license = "MIT" workspace = ".." edition = "2021" @@ -17,10 +17,10 @@ one-class-one-file = [] custom-godot = ["gdnative_bindings_generator/custom-godot"] [dependencies] -gdnative-sys = { path = "../gdnative-sys", version = "=0.10.0-rc.0" } -gdnative-core = { path = "../gdnative-core", version = "=0.10.0-rc.0" } +gdnative-sys = { path = "../gdnative-sys", version = "=0.10.0" } +gdnative-core = { path = "../gdnative-core", version = "=0.10.0" } bitflags = "1" libc = "0.2" [build-dependencies] -gdnative_bindings_generator = { path = "../bindings-generator", version = "=0.10.0-rc.0" } +gdnative_bindings_generator = { path = "../bindings-generator", version = "=0.10.0" } diff --git a/gdnative-core/Cargo.toml b/gdnative-core/Cargo.toml index c3fcc1a7a..28d9bad57 100644 --- a/gdnative-core/Cargo.toml +++ b/gdnative-core/Cargo.toml @@ -5,7 +5,7 @@ description = "The Godot game engine's gdnative core bindings." documentation = "https://docs.rs/crate/gdnative-core" repository = "https://github.com/godot-rust/godot-rust" homepage = "https://godot-rust.github.io/" -version = "0.10.0-rc.0" +version = "0.10.0" license = "MIT" workspace = ".." edition = "2021" @@ -17,8 +17,8 @@ gd-test = [] type-tag-fallback = [] [dependencies] -gdnative-sys = { path = "../gdnative-sys", version = "=0.10.0-rc.0" } -gdnative-impl-proc-macros = { path = "../impl/proc-macros", version = "=0.10.0-rc.0" } +gdnative-sys = { path = "../gdnative-sys", version = "=0.10.0" } +gdnative-impl-proc-macros = { path = "../impl/proc-macros", version = "=0.10.0" } ahash = "0.7.6" approx = "0.5" atomic-take = "1" diff --git a/gdnative-derive/Cargo.toml b/gdnative-derive/Cargo.toml index 5727bd80f..32c702690 100644 --- a/gdnative-derive/Cargo.toml +++ b/gdnative-derive/Cargo.toml @@ -5,7 +5,7 @@ description = "The Godot game engine's gdnative derive and procedural macros." documentation = "https://docs.rs/crate/gdnative-derive" repository = "https://github.com/godot-rust/godot-rust" homepage = "https://godot-rust.github.io/" -version = "0.10.0-rc.0" +version = "0.10.0" license = "MIT" workspace = ".." edition = "2021" diff --git a/gdnative-sys/Cargo.toml b/gdnative-sys/Cargo.toml index abeb67f15..5733a3b5e 100644 --- a/gdnative-sys/Cargo.toml +++ b/gdnative-sys/Cargo.toml @@ -5,7 +5,7 @@ description = "Generated bindings to the Godot game engine's gdnative core types documentation = "https://docs.rs/crate/gdnative-sys" repository = "https://github.com/godot-rust/godot-rust" homepage = "https://godot-rust.github.io/" -version = "0.10.0-rc.0" +version = "0.10.0" build = "build.rs" license = "MIT" workspace = ".." diff --git a/gdnative/Cargo.toml b/gdnative/Cargo.toml index 97581ea03..a615fd008 100644 --- a/gdnative/Cargo.toml +++ b/gdnative/Cargo.toml @@ -6,7 +6,7 @@ keywords = ["gamedev", "godot", "engine", "bindings"] documentation = "https://docs.rs/crate/gdnative" repository = "https://github.com/godot-rust/godot-rust" homepage = "https://godot-rust.github.io/" -version = "0.10.0-rc.0" +version = "0.10.0" license = "MIT" workspace = ".." readme = "../README.md" @@ -26,10 +26,10 @@ gd-test = ["gdnative-core/gd-test"] type-tag-fallback = ["gdnative-core/type-tag-fallback"] [dependencies] -gdnative-derive = { path = "../gdnative-derive", version = "=0.10.0-rc.0" } -gdnative-core = { path = "../gdnative-core", version = "=0.10.0-rc.0" } -gdnative-bindings = { path = "../gdnative-bindings", version = "=0.10.0-rc.0" } -gdnative-async = { path = "../gdnative-async", version = "=0.10.0-rc.0", optional = true } +gdnative-derive = { path = "../gdnative-derive", version = "=0.10.0" } +gdnative-core = { path = "../gdnative-core", version = "=0.10.0" } +gdnative-bindings = { path = "../gdnative-bindings", version = "=0.10.0" } +gdnative-async = { path = "../gdnative-async", version = "=0.10.0", optional = true } [dev-dependencies] trybuild = "1.0.18" # earrlier versions use broken termcolor 1.0.0 diff --git a/impl/proc-macros/Cargo.toml b/impl/proc-macros/Cargo.toml index caec21787..26d0f20eb 100644 --- a/impl/proc-macros/Cargo.toml +++ b/impl/proc-macros/Cargo.toml @@ -4,7 +4,7 @@ authors = ["The godot-rust developers"] description = "Internal dependency of the gdnative bindings." repository = "https://github.com/godot-rust/godot-rust" homepage = "https://godot-rust.github.io/" -version = "0.10.0-rc.0" +version = "0.10.0" license = "MIT" workspace = "../.." edition = "2021" From acceeaf4e12732b8278dbcc6f7f5f04c21a94520 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 13 Mar 2022 21:35:20 +0100 Subject: [PATCH 4/9] Plane: API consistency, unit-length invariant enforced, tests --- CHANGELOG.md | 4 + gdnative-core/src/core_types/geom/plane.rs | 330 ++++++++++++++------- 2 files changed, 229 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 840424fe6..61b58ca37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Geometric types API consistency ([#827](https://github.com/godot-rust/godot-rust/pull/827)) - Rename basis vectors `x, y, z` -> `a, b, c` - Pass by value/ref consistency + - `Plane` invariants ([#874](https://github.com/godot-rust/godot-rust/pull/874)) - Other changes (see PRs) - Method renames - `{String,Variant}::forget()` -> `leak()` ([#828](https://github.com/godot-rust/godot-rust/pull/828)) @@ -72,6 +73,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Basis::to_scale()` -> `scale()` - `Basis::from_elements()` -> `from_rows()` - `Transform2D::from_axis_origin()` -> `from_basis_origin()` + - `Plane::intersects_*()` -> `intersect_*()` ([#874](https://github.com/godot-rust/godot-rust/pull/874)) + - `Plane::normalize()` -> `normalized()` + - `Plane::has_point()` -> `contains_point()` + `contains_point_eps()` - Relax `Dictionary` key bounds: `ToVariant` -> `OwnedToVariant` ([#809](https://github.com/godot-rust/godot-rust/pull/809)) - `#[inherit]` is now optional and defaults to `Reference` ([#705](https://github.com/godot-rust/godot-rust/pull/705)) - `Instance` and `TInstance` now use `Own=Shared` by default ([#823](https://github.com/godot-rust/godot-rust/pull/823)) diff --git a/gdnative-core/src/core_types/geom/plane.rs b/gdnative-core/src/core_types/geom/plane.rs index 052aed8ee..c27bb2aba 100644 --- a/gdnative-core/src/core_types/geom/plane.rs +++ b/gdnative-core/src/core_types/geom/plane.rs @@ -1,33 +1,62 @@ use crate::core_types::{IsEqualApprox, Vector3}; -/// Plane in hessian form. +// TODO enforce invariants via setters, make fields private +// Otherwise almost all methods need to panic +// - normal.length() == 1 +// - d > 0 + +/// 3D plane in Hessian form: `a*b + b*y + c*z + d = 0` +/// +/// Note: almost all methods on `Plane` require that the `normal` vector have +/// unit length and will panic if this invariant is violated. This is not separately +/// annotated for each method. #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Plane { + /// Normal vector, perpendicular to the plane. + /// + /// Most `Plane` methods expect this vector to be normalized and may panic if this is not the case. pub normal: Vector3, + + /// Distance from the coordinate system origin (in the direction of `normal`). + /// + /// This value is typically non-negative. It can however be negative, which behaves as if `normal` changed direction. pub d: f32, } impl Plane { - /// Creates a new `Plane` from the ['Vector3'](./type.Vector3.html) normal and the distance from the origin. + /// Creates a new `Plane` from the `normal` and the distance from the origin `d`. + /// + /// # Panics + /// In contrast to construction via `Plane { normal, d }`, this verifies that `normal` has unit length, and will + /// panic if this is not the case. #[inline] pub fn new(normal: Vector3, d: f32) -> Self { - Self { normal, d } + // Design: we could call normalize() here, however that suggests to the user that vectors with non-unit + // length are valid normals, and tempts users to assign those directly to the field. It's also confusing + // if Plane { normal, d } and Plane::new(normal, d) have fundamentally different behaviors. + // If invariants were enforced at the field level using setters, the story might be different. + + let result = Self { normal, d }; + result.ensure_normalized(); + result } - /// Creates a new `Plane` from four floats. - /// a, b, c are used for the normal ['Vector3'](./type.Vector3.html). - /// d is the distance from the origin. + /// Creates a new `Plane` from normal and origin distance. + /// + /// `a`, `b`, `c` are used for the `normal` vector. + /// `d` is the distance from the origin. + /// + /// # Panics + /// See [`Self::new()`]. #[inline] pub fn from_coordinates(a: f32, b: f32, c: f32, d: f32) -> Self { - Self { - normal: Vector3::new(a, b, c), - d, - } + Self::new(Vector3::new(a, b, c), d) } /// Creates a new `Plane` from three [`Vector3`](./type.Vector3.html), given in clockwise order. + /// /// If all three points are collinear, returns `None`. #[inline] pub fn from_points(a: Vector3, b: Vector3, c: Vector3) -> Option { @@ -43,33 +72,67 @@ impl Plane { } } - /// Returns the center of the `Plane`. + /// Returns the point on the `Plane`, which is closest to the origin. + /// + /// This is equivalent to `self.project(Vector3::ZERO)`. #[inline] - pub fn center(&self) -> Vector3 { + pub fn center(self) -> Vector3 { + self.ensure_normalized(); + self.normal * self.d } - /// Returns the shortest distance from the `Plane` to `point`. + /// Returns the orthogonal projection of `point` onto a point in the `Plane`. + /// + /// The projection is a point, which lies on the plane and is closest to `point`. + #[inline] + pub fn project(self, point: Vector3) -> Vector3 { + // Note: invariant check in distance_to() + + point - self.normal * self.distance_to(point) + } + + /// Returns the **signed** distance from the `Plane` to `point`. + /// + /// This value is negative, if `self.is_point_over(point)` is false. #[inline] - pub fn distance_to(&self, point: Vector3) -> f32 { + pub fn distance_to(self, point: Vector3) -> f32 { + self.ensure_normalized(); + (self.normal.dot(point)) - self.d } /// Returns `true` if `point` is inside the `Plane`. - /// `epislon` specifies the minimum threshold to be considered inside the `Plane`. + /// + /// Uses a default epsilon for the boundary check. Use [`Self::contains_point_eps()`] for more control. #[inline] - pub fn has_point(&self, point: Vector3, epsilon: f32) -> bool { - let dist = self.distance_to(point).abs(); + pub fn contains_point(self, point: Vector3) -> bool { + // Note: invariant check in distance_to() + + self.contains_point_eps(point, crate::core_types::CMP_EPSILON as f32) + } + + /// Returns `true` if `point` is inside the `Plane`. + /// + /// `epsilon` specifies the minimum distance, at and below which a point is considered inside the `Plane`. + #[inline] + pub fn contains_point_eps(self, point: Vector3, epsilon: f32) -> bool { + // Note: invariant check in distance_to() + let dist = self.distance_to(point).abs(); dist <= epsilon } - /// Returns the intersection point of the three planes `b`, `c` and this `Plane`. - /// Returns `None` if the 'Plane's don't intersect. + /// Returns the intersection point of the three planes `self`, `b` and `c`. + /// + /// Returns `None` if the planes don't intersect. #[inline] - pub fn intersect_3(&self, b: Plane, c: Plane) -> Option { - let a = &self; + pub fn intersect_3(self, b: Plane, c: Plane) -> Option { + self.ensure_normalized(); + b.ensure_normalized(); + c.ensure_normalized(); + let a = self; let denom = Vector3::cross(a.normal, b.normal).dot(c.normal); if denom.is_equal_approx(0.0) { @@ -84,19 +147,20 @@ impl Plane { } } - /// Returns the intersection point of a ray consisting of the position `from` and the direction normal `dir` with this plane/ + /// Returns the intersection point of a ray consisting of the position `from` and the direction vector `dir` with this plane. + /// /// Returns `None` if the ray doesn't intersect. #[inline] - pub fn intersects_ray(&self, from: Vector3, dir: Vector3) -> Option { - let den = self.normal.dot(dir); + pub fn intersect_ray(self, from: Vector3, dir: Vector3) -> Option { + self.ensure_normalized(); - if den.is_equal_approx(0.0) { + let denom = self.normal.dot(dir); + if denom.is_equal_approx(0.0) { return None; } - let dist = (self.normal.dot(from) - self.d) / den; - - if dist > std::f32::EPSILON { + let dist = (self.normal.dot(from) - self.d) / denom; + if dist > f32::EPSILON { return None; } @@ -104,61 +168,64 @@ impl Plane { } /// Returns the intersection point of a segment from `begin` to `end` with this `Plane`. + /// /// Returns `None` if the the segment doesn't intersect. #[inline] - pub fn intersects_segment(&self, begin: Vector3, end: Vector3) -> Option { + pub fn intersect_segment(self, begin: Vector3, end: Vector3) -> Option { + self.ensure_normalized(); + let segment = begin - end; - let den = self.normal.dot(segment); + let denom = self.normal.dot(segment); - if den.is_equal_approx(0.0) { + if denom.is_equal_approx(0.0) { return None; } - let dist = (self.normal.dot(begin) - self.d) / den; + let dist = (self.normal.dot(begin) - self.d) / denom; // check that dist is not in -EPSILON..(EPSILON+1) - if !(-std::f32::EPSILON..=(std::f32::EPSILON + 1.0)).contains(&dist) { - return None; + if (-f32::EPSILON..=(f32::EPSILON + 1.0)).contains(&dist) { + Some(begin + segment * -dist) + } else { + None } - - Some(begin + segment * -dist) } /// Returns `true` if this `Plane` and `other` are approximately equal. - /// Determined by running `approx_eq` on both `normal` and `d`. #[inline] 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`. + /// Returns `true` if `point` is on the side of the `Plane`, into which `normal` points. + /// + /// Points that lie exactly on the plane will be returned as `false`. #[inline] - pub fn is_point_over(&self, point: Vector3) -> bool { + pub fn is_point_over(self, point: Vector3) -> bool { + self.ensure_normalized(); + self.normal.dot(point) > self.d } /// Returns the `Plane` normalized. + /// + /// # Panics + /// If `self.normal` is a zero vector. All other vectors are explicitly allowed. #[inline] - pub fn normalize(mut self) -> Self { + pub fn normalized(mut self) -> Self { let l = self.normal.length(); - if l == 0.0 { - self.normal = Vector3::new(0.0, 0.0, 0.0); - self.d = 0.0; - } else { - self.normal /= l; - self.d /= l; - } + assert_ne!( + l, 0.0, + "Plane::normal {:?} must not be a zero vector", + self.normal + ); + self.normal /= l; + self.d /= l; self } - /// Returns the orthogonal projection of `point` into a point in the `Plane`. - #[inline] - pub fn project(&self, point: Vector3) -> Vector3 { - point - self.normal * self.distance_to(point) - } - #[doc(hidden)] #[inline] pub fn sys(&self) -> *const sys::godot_plane { @@ -170,16 +237,26 @@ impl Plane { pub fn from_sys(c: sys::godot_plane) -> Self { unsafe { std::mem::transmute::(c) } } + + #[inline] + fn ensure_normalized(self) { + assert!( + self.normal.is_normalized(), + "Plane::normal {:?} does not have unit length", + self.normal + ); + } } #[cfg(test)] mod test { use super::*; - fn test_inputs() -> (Plane, Vector3) { + fn test_inputs() -> (Plane, Vector3, Vector3) { ( - Plane::from_coordinates(0.01, 0.02, 0.04, 0.08), - Vector3::new(0.16, 0.32, 0.64), + Plane::new(Vector3::new(2.0, 3.0, 7.0).normalized(), 1.5), + Vector3::new(8.0, 2.0, 5.0), + Vector3::new(-1.0, 5.0, -3.0), ) } @@ -195,106 +272,149 @@ mod test { assert!(Plane::from_points(a, b, c) .unwrap() .is_equal_approx(expected_valid)); + assert_eq!(Plane::from_points(a, b, d), None); } #[test] fn center() { - let (p, _v) = test_inputs(); - - let expected = Vector3::new(0.0008, 0.0016, 0.0032); + let (p, ..) = test_inputs(); + let expected = Vector3::new(0.381, 0.571501, 1.333501); assert!(p.center().is_equal_approx(expected)); } + #[test] + fn project() { + let (p, v, w) = test_inputs(); + let expected_o = Vector3::new(0.381, 0.571501, 1.333501); + let expected_v = Vector3::new(6.542291, -0.186564, -0.101982); + let expected_w = Vector3::new(-0.360935, 5.958597, -0.763273); + + assert!(p.project(Vector3::ZERO).is_equal_approx(expected_o)); + assert!(p.project(v).is_equal_approx(expected_v)); + assert!(p.project(w).is_equal_approx(expected_w)); + } + #[test] fn distance_to() { - let (p, v) = test_inputs(); + let (p, v, w) = test_inputs(); - let expected = -0.0464; + let expected_o = -p.d; // negative, because the origin is on opposite side of plane's normal + let expected_v = 5.739007; + let expected_w = -2.516001; - assert!(p.distance_to(v).is_equal_approx(expected)); + assert!(p.distance_to(Vector3::ZERO).is_equal_approx(expected_o)); + assert!(p.distance_to(v).is_equal_approx(expected_v)); + assert!(p.distance_to(w).is_equal_approx(expected_w)); } #[test] - fn has_point() { - let p = Plane::new(Vector3::new(1.0, 1.0, 1.0), 1.0); + fn contains_point() { + let (p, ..) = test_inputs(); - let outside = Vector3::new(0.0, 0.0, 0.0); - let inside = Vector3::new(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0); + // points from project() + assert!(p.contains_point(Vector3::new(0.381, 0.571501, 1.333501))); + assert!(p.contains_point(Vector3::new(6.542291, -0.186564, -0.101982))); - assert!(!p.has_point(outside, 0.00001)); - assert!(p.has_point(inside, 0.00001)); + // slightly modified X coord + assert!(!p.contains_point(Vector3::new(6.552291, -0.186564, -0.101982))); + assert!(!p.contains_point(Vector3::new(6.562291, -0.186564, -0.101982))); } + // TODO contains_point_eps() + #[test] fn intersect_3() { - let (p, _v) = test_inputs(); + let (p, ..) = test_inputs(); - let b = Plane::from_coordinates(0.08, 0.04, 0.03, 0.01); - let c = Plane::from_coordinates(0.05, 0.2, 0.1, 0.6); + let p2 = Plane::new(Vector3::new(3.0, 1.0, -2.0).normalized(), 1.0); + let p3 = Plane::new(Vector3::new(7.0, -4.0, 5.0).normalized(), 0.0); - let expected = Vector3::new(-1.707317, 2.95122, 0.95122); + let q = Plane::new(p.normal, 2.5); // parallel to p - 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); + let expected = Vector3::new(0.868692, 2.161257, 0.512837); - assert!(p.intersect_3(b, c).unwrap().is_equal_approx(expected)); - assert_eq!(p.intersect_3(d, e), None); + assert!(p.intersect_3(p2, p3).unwrap().is_equal_approx(expected)); + assert!(p.intersect_3(p3, p2).unwrap().is_equal_approx(expected)); + + assert_eq!(p.intersect_3(q, p2), None); + assert_eq!(p.intersect_3(q, p3), None); } #[test] fn intersects_ray() { - let (p, v) = test_inputs(); + let (p, v, w) = test_inputs(); - let expected = Vector3::new(0.16, 2.64, 0.64); + let expected = Vector3::new(1.743063, 4.085646, -0.561722); - assert!(p - .intersects_ray(v, Vector3::new(0.0, 1.0, 0.0)) - .unwrap() - .is_equal_approx(expected)); - assert_eq!(p.intersects_ray(v, Vector3::new(0.0, -1.0, 0.0)), None); + assert!(p.intersect_ray(v, w - v).unwrap().is_equal_approx(expected)); + assert!(p.intersect_ray(w, v - w).unwrap().is_equal_approx(expected)); + + // Vector is perpendicular to normal + let u = Vector3::new(-3.0, 2.0, 0.0); + + // Test with any points in direction of u (on the plane, not on the plane) + assert_eq!(p.intersect_ray(p.center(), u), None); + assert_eq!(p.intersect_ray(v, u), None); + assert_eq!(p.intersect_ray(w, u), None); } #[test] fn intersects_segment() { - let (p, v) = test_inputs(); + let (p, v, w) = test_inputs(); - let expected = Vector3::new(0.16, 2.64, 0.64); + let expected = Vector3::new(1.743063, 4.085646, -0.561722); - assert!(p - .intersects_segment(v, Vector3::new(0.16, 10.0, 0.64)) - .unwrap() - .is_equal_approx(expected)); - assert_eq!( - p.intersects_segment(v, Vector3::new(0.16, -10.0, 0.64)), - None - ); + assert!(p.intersect_segment(v, w).unwrap().is_equal_approx(expected)); + assert!(p.intersect_segment(w, v).unwrap().is_equal_approx(expected)); + + // Vector is perpendicular to normal + let u = Vector3::new(-3.0, 2.0, 0.0); + let pc = p.center(); + + // Test with any points in direction of u (on the plane, not on the plane) + assert_eq!(p.intersect_segment(pc, pc + u), None); + assert_eq!(p.intersect_segment(v, v + u), None); + assert_eq!(p.intersect_segment(w, w + u), None); } #[test] fn is_point_over() { - let (p, v) = test_inputs(); + let (p, v, w) = test_inputs(); - assert!(!p.is_point_over(v)); - assert!(p.is_point_over(Vector3::new(1.0, 10.0, 2.0))); + assert!(p.is_point_over(v)); + + assert!(!p.is_point_over(w)); + assert!(!p.is_point_over(p.center())); // strictly > } #[test] - fn normalize() { - let (p, _v) = test_inputs(); + fn normalized() { + let (p, ..) = test_inputs(); + + let raw = Plane { + normal: Vector3::new(2.0, 3.0, 7.0), + d: 11.811012, // = 1.5 * normal.length() + }; - assert!(p.normalize().is_equal_approx(Plane::from_coordinates( - 0.218218, 0.436436, 0.872872, 1.745743 - ))); + assert!(raw.normalized().is_equal_approx(p)); } #[test] - fn project() { - let (p, v) = test_inputs(); + fn is_equal_approx() { + let (p, ..) = test_inputs(); + + // Vectors differing in only d or normal + let new_d = p.d + 0.1; + let new_normal = Vector3::new(p.normal.x, p.normal.y, p.normal.z + 0.1).normalized(); + + let q1 = Plane::new(p.normal, new_d); + let q2 = Plane::new(new_normal, p.d); - let expected = Vector3::new(0.160464, 0.320928, 0.641856); + assert!(p.is_equal_approx(p)); - assert!(p.project(v).is_equal_approx(expected)) + assert!(!p.is_equal_approx(q1)); + assert!(!p.is_equal_approx(q2)); } } From b4fb50b4aa865765c0bc002d2da248a08fec7a12 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 14 Mar 2022 00:11:58 +0100 Subject: [PATCH 5/9] Minor dependency + clippy + check.sh adjustments --- bindings-generator/Cargo.toml | 2 +- check.sh | 4 ++++ gdnative-core/src/core_types/pool_array.rs | 1 + test/Cargo.toml | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/bindings-generator/Cargo.toml b/bindings-generator/Cargo.toml index cb1fdfcc3..4b14c7ec1 100644 --- a/bindings-generator/Cargo.toml +++ b/bindings-generator/Cargo.toml @@ -21,7 +21,7 @@ memchr = "2" miniserde = "0.1.10" proc-macro2 = "1" quote = "1" -regex = "1.5.5" # for security: https://blog.rust-lang.org/2022/03/08/cve-2022-24713.html +regex = { version = "1.5.5", default-features = false } # for security: https://blog.rust-lang.org/2022/03/08/cve-2022-24713.html roxmltree = "0.14" syn = { version = "1", features = ["full", "extra-traits", "visit"] } unindent = "0.1.5" diff --git a/check.sh b/check.sh index 51476baad..c1e5baf45 100755 --- a/check.sh +++ b/check.sh @@ -22,6 +22,7 @@ for arg in "${args[@]}"; do echo " test run unit tests (no Godot)" echo " itest run integration tests (Godot)" echo " doc generate docs for 'gdnative' crate" + echo " dok generate docs and open in browser" echo "" echo "Examples:" echo " check.sh fmt clippy" @@ -82,6 +83,9 @@ for arg in "${args[@]}"; do doc) cmds+=("cargo doc --lib -p gdnative --no-deps --features $features") ;; + dok) + cmds+=("cargo doc --lib -p gdnative --no-deps --features $features --open") + ;; *) echo "Unrecognized command '$arg'" exit 2 diff --git a/gdnative-core/src/core_types/pool_array.rs b/gdnative-core/src/core_types/pool_array.rs index 91bcf8cdc..119967e30 100644 --- a/gdnative-core/src/core_types/pool_array.rs +++ b/gdnative-core/src/core_types/pool_array.rs @@ -133,6 +133,7 @@ impl PoolArray { /// /// If the resulting length would not fit in `i32`. #[inline] + #[allow(clippy::iter_with_drain)] // "`drain(..)` used on a `Vec`"; suggests `into_iter()` but we don't have the vec by value pub fn append_vec(&mut self, src: &mut Vec) { let start = self.len() as usize; let new_len = start + src.len(); diff --git a/test/Cargo.toml b/test/Cargo.toml index 1bb7ddfa8..42c1389ec 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -24,6 +24,6 @@ serde = "1" serde_json = "1" bincode = "1" serde_yaml = "0.8.23" -rmp-serde = "0.15" +rmp-serde = "1" futures = "0.3" once_cell = "1" From 3d081a2e99a93080f45ce2a56f75bde6703b62b1 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 14 Mar 2022 00:22:51 +0100 Subject: [PATCH 6/9] Use `debug` feature in bindings generator; fix related issue not detected in CI --- .github/workflows/full-ci.yml | 2 +- .github/workflows/minimal-ci.yml | 2 ++ .github/workflows/release-version.yml | 3 ++- bindings-generator/src/lib.rs | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/full-ci.yml b/.github/workflows/full-ci.yml index 78e059609..94ea79119 100644 --- a/.github/workflows/full-ci.yml +++ b/.github/workflows/full-ci.yml @@ -32,7 +32,7 @@ env: # Local variables # Note: using variables is limited at the moment, see https://github.com/actions/runner/issues/480 - GDRUST_FEATURES: "gdnative/async,gdnative/serde" + GDRUST_FEATURES: "gdnative/async,gdnative/serde,gdnative_bindings_generator/debug" CARGO_DENY_VERSION: "0.11.0" CARGO_DINGHY_VERSION: "0.4.68" diff --git a/.github/workflows/minimal-ci.yml b/.github/workflows/minimal-ci.yml index 0f3733a0b..b36e0f19f 100644 --- a/.github/workflows/minimal-ci.yml +++ b/.github/workflows/minimal-ci.yml @@ -13,6 +13,8 @@ env: # Local variables # Note: using variables is limited at the moment, see https://github.com/actions/runner/issues/480 GODOT_VER: "3.4.1" + + # Don't use more features like "gdnative_bindings_generator/debug" to keep CI truly minimal GDRUST_FEATURES: "gdnative/async,gdnative/serde" on: diff --git a/.github/workflows/release-version.yml b/.github/workflows/release-version.yml index 12ec3bc40..83677c58b 100644 --- a/.github/workflows/release-version.yml +++ b/.github/workflows/release-version.yml @@ -8,7 +8,8 @@ on: - '0.10.[0-9]+-?*' env: - GDRUST_FEATURES: "gdnative/async,gdnative/serde" + # Note: used for test and clippy, not for publish + GDRUST_FEATURES: "gdnative/async,gdnative/serde,gdnative_bindings_generator/debug" # Crates to publish -- important, this doesn't work when there are spaces in any of the paths! GDRUST_CRATES: > diff --git a/bindings-generator/src/lib.rs b/bindings-generator/src/lib.rs index 249191446..be285b26f 100644 --- a/bindings-generator/src/lib.rs +++ b/bindings-generator/src/lib.rs @@ -225,7 +225,7 @@ pub(crate) mod test_prelude { let api = Api::new(include_str!("../../gdnative-bindings/api.json")); let mut buffer = BufWriter::new(Vec::with_capacity(16384)); - for class in api.classes { + for class in &api.classes { let mut icalls = HashMap::new(); let code = generate_module_doc(&class); From d52a230388757c3250c0e60c34e2b983d35c8076 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 16 Mar 2022 14:46:06 +0100 Subject: [PATCH 7/9] NameString: slight modernization Changes: * Remove operator_less() * Rename get_name() -> to_godot_string() * Documentation --- gdnative-core/src/core_types/string.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/gdnative-core/src/core_types/string.rs b/gdnative-core/src/core_types/string.rs index 3100d98bd..8bf4d3673 100644 --- a/gdnative-core/src/core_types/string.rs +++ b/gdnative-core/src/core_types/string.rs @@ -542,6 +542,12 @@ impl fmt::Debug for Utf8String { } } +/// Interned string. +/// +/// Like [`GodotString`], but unique: two `StringName`s with the same string value share the same +/// internal object. Just like the `GodotString` struct, this type is immutable. +/// +/// Use [`Self::from_godot_string()`] and [`Self::to_godot_string()`] for conversions. pub struct StringName(pub(crate) sys::godot_string_name); impl StringName { @@ -579,15 +585,10 @@ impl StringName { } #[inline] - pub fn get_name(&self) -> GodotString { + pub fn to_godot_string(&self) -> GodotString { unsafe { GodotString((get_api().godot_string_name_get_name)(&self.0)) } } - #[inline] - pub fn operator_less(&self, s: &StringName) -> bool { - unsafe { (get_api().godot_string_name_operator_less)(&self.0, &s.0) } - } - #[doc(hidden)] #[inline] pub fn sys(&self) -> *const sys::godot_string_name { @@ -617,7 +618,7 @@ impl_basic_traits_as_sys! { impl fmt::Debug for StringName { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.get_name().to_string().fmt(f) + self.to_godot_string().fmt(f) } } From 5760cc8c81c9b998623914f591b0c542d580637c Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 16 Mar 2022 19:46:14 +0100 Subject: [PATCH 8/9] Remove #[property(before_get|before_set|after_get|after_set)] Obsolete through #[property(get|set)] --- gdnative-derive/src/lib.rs | 5 - gdnative-derive/src/native_script.rs | 53 +-------- .../src/native_script/property_args.rs | 104 ---------------- gdnative/tests/ui/derive_property_basic.rs | 16 --- test/src/test_derive.rs | 111 ------------------ 5 files changed, 1 insertion(+), 288 deletions(-) diff --git a/gdnative-derive/src/lib.rs b/gdnative-derive/src/lib.rs index 612a0df54..c1900857a 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -213,11 +213,6 @@ pub fn profiled(meta: TokenStream, input: TokenStream) -> TokenStream { /// Sets the default value *in the inspector* for this property. The setter is *not* /// guaranteed to be called by the engine with the value. /// -/// - `before_get` / `after_get` / `before_set` / `after_set` `= "Self::hook_method"` -/// -/// Call hook methods with `self` and `owner` before and/or after the generated property -/// accessors. -/// /// - `get` / `get_ref` / `set` /// /// Configure getter/setter for property. All of them can accept a path to specify a custom diff --git a/gdnative-derive/src/native_script.rs b/gdnative-derive/src/native_script.rs index e06ac1cb8..2735915ac 100644 --- a/gdnative-derive/src/native_script.rs +++ b/gdnative-derive/src/native_script.rs @@ -117,12 +117,6 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result = config - .before_get - .map(|path_expr| parse_quote!(#path_expr(this, _owner);)); - let after_get: Option = config - .after_get - .map(|path_expr| parse_quote!(#path_expr(this, _owner);)); let with_getter = get.map(|get| { let register_fn = match get { PropertyGet::Owned(_) => quote!(with_getter), @@ -134,19 +128,10 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result| { - #before_get - let res = #get; - #after_get - res + #get }) ) }); - let before_set: Option = config - .before_set - .map(|path_expr| parse_quote!(#path_expr(this, _owner);)); - let after_set: Option = config - .after_set - .map(|path_expr| parse_quote!(#path_expr(this, _owner);)); let with_setter = set.map(|set| { let set: Stmt = match set { PropertySet::Default => parse_quote!(this.#ident = v;), @@ -154,9 +139,7 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result, v| { - #before_set #set - #after_set })) }); @@ -351,40 +334,6 @@ mod tests { parse_derive_input(&input).unwrap(); } - #[test] - fn derive_property_before_get() { - let input: TokenStream2 = syn::parse_str( - r#" - #[inherit(Node)] - struct Foo { - #[property(before_get = "foo::bar")] - bar: String, - }"#, - ) - .unwrap(); - - let input: DeriveInput = syn::parse2(input).unwrap(); - - parse_derive_input(&input).unwrap(); - } - - #[test] - fn derive_property_before_get_err() { - let input: TokenStream2 = syn::parse_str( - r#" - #[inherit(Node)] - struct Foo { - #[property(before_get = "foo::bar")] - bar: String, - }"#, - ) - .unwrap(); - - let input: DeriveInput = syn::parse2(input).unwrap(); - - parse_derive_input(&input).unwrap(); - } - #[test] fn derive_property_no_editor() { let input: TokenStream2 = syn::parse_str( diff --git a/gdnative-derive/src/native_script/property_args.rs b/gdnative-derive/src/native_script/property_args.rs index bd679d2a6..0fa0a108a 100644 --- a/gdnative-derive/src/native_script/property_args.rs +++ b/gdnative-derive/src/native_script/property_args.rs @@ -18,12 +18,8 @@ pub struct PropertyAttrArgs { pub path: Option, pub default: Option, pub hint: Option, - pub before_get: Option, pub get: Option, - pub after_get: Option, - pub before_set: Option, pub set: Option, - pub after_set: Option, pub no_editor: bool, } @@ -32,12 +28,8 @@ pub struct PropertyAttrArgsBuilder { path: Option, default: Option, hint: Option, - before_get: Option, get: Option, - after_get: Option, - before_set: Option, set: Option, - after_set: Option, no_editor: bool, } @@ -48,12 +40,8 @@ impl PropertyAttrArgsBuilder { path: None, default: None, hint: None, - before_get: None, get: None, - after_get: None, - before_set: None, set: None, - after_set: None, no_editor: false, } } @@ -120,28 +108,6 @@ impl PropertyAttrArgsBuilder { )); } } - "before_get" => { - let string = if let syn::Lit::Str(lit_str) = &pair.lit { - lit_str.value() - } else { - return Err(syn::Error::new( - pair.span(), - "'before_get' value is not a string literal", - )); - }; - - let path = - syn::parse_str::(string.as_str()).map_err(invalid_value_path)?; - if let Some(old) = self.before_get.replace(path) { - return Err(syn::Error::new( - pair.span(), - format!( - "there is already a 'before_get' attribute with value: {:?}", - old - ), - )); - } - } "get" => { let string = if let syn::Lit::Str(lit_str) = &pair.lit { lit_str.value() @@ -185,50 +151,6 @@ impl PropertyAttrArgsBuilder { )); } } - "after_get" => { - let string = if let syn::Lit::Str(lit_str) = &pair.lit { - lit_str.value() - } else { - return Err(syn::Error::new( - pair.span(), - "'after_get' value is not a string literal", - )); - }; - - let path = - syn::parse_str::(string.as_str()).map_err(invalid_value_path)?; - if let Some(old) = self.after_get.replace(path) { - return Err(syn::Error::new( - pair.span(), - format!( - "there is already a 'after_get' attribute with value: {:?}", - old - ), - )); - } - } - "before_set" => { - let string = if let syn::Lit::Str(lit_str) = &pair.lit { - lit_str.value() - } else { - return Err(syn::Error::new( - pair.span(), - "'before_set' value is not a string literal", - )); - }; - - let path = - syn::parse_str::(string.as_str()).map_err(invalid_value_path)?; - if let Some(old) = self.before_set.replace(path) { - return Err(syn::Error::new( - pair.span(), - format!( - "there is already a 'before_set' attribute with value: {:?}", - old - ), - )); - } - } "set" => { let string = if let syn::Lit::Str(lit_str) = &pair.lit { lit_str.value() @@ -249,28 +171,6 @@ impl PropertyAttrArgsBuilder { )); } } - "after_set" => { - let string = if let syn::Lit::Str(lit_str) = &pair.lit { - lit_str.value() - } else { - return Err(syn::Error::new( - pair.span(), - "'after_set' value is not a string literal", - )); - }; - - let path = - syn::parse_str::(string.as_str()).map_err(invalid_value_path)?; - if let Some(old) = self.after_set.replace(path) { - return Err(syn::Error::new( - pair.span(), - format!( - "there is already a 'after_set' attribute with value: {:?}", - old - ), - )); - } - } _ => { return Err(syn::Error::new( pair.span(), @@ -317,12 +217,8 @@ impl PropertyAttrArgsBuilder { path: self.path, default: self.default, hint: self.hint, - before_get: self.before_get, get: self.get, - after_get: self.after_get, - before_set: self.before_set, set: self.set, - after_set: self.after_set, no_editor: self.no_editor, } } diff --git a/gdnative/tests/ui/derive_property_basic.rs b/gdnative/tests/ui/derive_property_basic.rs index 26343d3d1..f6d7c1c45 100644 --- a/gdnative/tests/ui/derive_property_basic.rs +++ b/gdnative/tests/ui/derive_property_basic.rs @@ -4,10 +4,6 @@ use gdnative::prelude::*; fn test_hint() -> StringHint { StringHint::File(EnumHint::new(vec![])) } -fn test_before_get(_this: &Foo, _owner: TRef) {} -fn test_before_set(_this: &mut Foo, _owner: TRef) {} -fn test_after_get(_this: &Foo, _owner: TRef) {} -fn test_after_set(_this: &mut Foo, _owner: TRef) {} #[derive(Default, NativeClass)] #[inherit(Node)] @@ -18,18 +14,6 @@ struct Foo { // hint #[property(hint = "test_hint")] prop_hint: String, - - // before get & set - #[property(before_get = "test_before_get")] - prop_before_get: String, - #[property(before_set = "test_before_set")] - prop_before_set: String, - - // after get & set - #[property(after_get = "test_after_get")] - prop_after_get: String, - #[property(after_set = "test_after_set")] - prop_after_set: String, } #[methods] diff --git a/test/src/test_derive.rs b/test/src/test_derive.rs index f47e173ba..b5ff2ecff 100644 --- a/test/src/test_derive.rs +++ b/test/src/test_derive.rs @@ -180,117 +180,6 @@ fn test_derive_owned_to_variant() -> bool { ok } -#[derive(gdnative::derive::NativeClass)] -#[inherit(Node)] -struct PropertyHooks { - #[property( - before_get = "Self::before_get", - after_get = "Self::after_get", - before_set = "Self::before_set", - after_set = "Self::after_set" - )] - value: u32, - - pub before_get_called: Cell, - pub after_get_called: Cell, - pub before_set_value: Option, - pub after_set_value: Option, -} - -#[gdnative_derive::methods] -impl PropertyHooks { - fn new(_owner: &Node) -> Self { - Self { - value: 0, - before_get_called: Cell::new(0), - after_get_called: Cell::new(0), - before_set_value: None, - after_set_value: None, - } - } - - fn before_get(&self, _owner: TRef) { - assert_eq!(self.before_get_called.get(), self.after_get_called.get()); - self.before_get_called.set(self.before_get_called.get() + 1); - } - - fn after_get(&self, _owner: TRef) { - assert_eq!( - self.before_get_called.get(), - self.after_get_called.get() + 1 - ); - self.after_get_called.set(self.after_get_called.get() + 1); - } - - fn assert_get_calls(&self, times: u32) { - assert_eq!(times, self.before_get_called.get()); - assert_eq!(times, self.after_get_called.get()); - } - - fn before_set(&mut self, _owner: TRef) { - self.before_set_value = Some(self.value); - } - - fn after_set(&mut self, _owner: TRef) { - self.after_set_value = Some(self.value); - } - - fn reset_set_value(&mut self) { - self.before_set_value = None; - self.after_set_value = None; - } -} - -fn test_derive_nativeclass_with_property_hooks() -> bool { - println!(" -- test_derive_nativeclass_with_property_hooks"); - - let ok = std::panic::catch_unwind(|| { - use gdnative::export::user_data::MapMut; - - let thing = Instance::::new(); - let (owner, script) = thing.decouple(); - - owner.set("value", 42); - script - .map_mut(|script| { - assert_eq!(Some(0), script.before_set_value); - assert_eq!(Some(42), script.after_set_value); - script.reset_set_value(); - }) - .unwrap(); - - script - .map_mut(|script| { - script.assert_get_calls(0); - }) - .unwrap(); - assert_eq!(42, u32::from_variant(&owner.get("value")).unwrap()); - script - .map_mut(|script| { - script.assert_get_calls(1); - }) - .unwrap(); - - owner.set("value", 12345); - script - .map_mut(|script| { - assert_eq!(Some(42), script.before_set_value); - assert_eq!(Some(12345), script.after_set_value); - script.reset_set_value(); - }) - .unwrap(); - - owner.free(); - }) - .is_ok(); - - if !ok { - godot_error!(" !! Test test_derive_owned_to_variant failed"); - } - - ok -} - #[derive(NativeClass)] #[inherit(Reference)] #[no_constructor] From 8cfc2efb2b91137293d10bfc5905bc31cb7e95e5 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 16 Mar 2022 19:51:24 +0100 Subject: [PATCH 9/9] Update changelog + ReadMe --- CHANGELOG.md | 8 ++++++-- README.md | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61b58ca37..8818c51e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.10.0] - unreleased +## [0.10.0] ### Added @@ -47,7 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rust edition is now 2021 ([#870](https://github.com/godot-rust/godot-rust/pull/870)) - `euclid` vector library replaced with `glam`, no longer part of public API ([#713](https://github.com/godot-rust/godot-rust/pull/713)) - `Variant` has now a redesigned conversion API ([#819](https://github.com/godot-rust/godot-rust/pull/819)) -- Type renames (815, 828) +- Type renames ([#815](https://github.com/godot-rust/godot-rust/pull/815), [#828](https://github.com/godot-rust/godot-rust/pull/828)) - `RefInstance` -> `TInstance` - `RefKind` -> `Memory` - `ThreadAccess` -> `Ownership` @@ -73,6 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Basis::to_scale()` -> `scale()` - `Basis::from_elements()` -> `from_rows()` - `Transform2D::from_axis_origin()` -> `from_basis_origin()` + - `StringName::get_name()` -> `to_godot_string()` ([#874](https://github.com/godot-rust/godot-rust/pull/874)) - `Plane::intersects_*()` -> `intersect_*()` ([#874](https://github.com/godot-rust/godot-rust/pull/874)) - `Plane::normalize()` -> `normalized()` - `Plane::has_point()` -> `contains_point()` + `contains_point_eps()` @@ -102,6 +103,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - access methods for `VariantArray` ([#795](https://github.com/godot-rust/godot-rust/pull/795)) - `Basis::invert()`, `orthonormalize()`, `rotate()`, `tdotx()`, `tdoty()`, `tdotz()` ([#827](https://github.com/godot-rust/godot-rust/pull/827)) - `Rid::operator_less()` ([#844](https://github.com/godot-rust/godot-rust/pull/844)) + - `StringName::operator_less()` ([#874](https://github.com/godot-rust/godot-rust/pull/874)) +- Macros and attributes + - `#[property(before_get|before_set|after_get|after_set)]`, replaced with `#[property(get|set)]` ([#874](https://github.com/godot-rust/godot-rust/pull/874)) - From `prelude` - macros`godot_gdnative_init`, `godot_gdnative_terminate`, `godot_nativescript_init`, `godot_site` ([#811](https://github.com/godot-rust/godot-rust/pull/811)) diff --git a/README.md b/README.md index 1d3340c7f..dca06b1da 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The bindings cover most of the exposed API of Godot 3.4, and are being used on a number of projects in development, but we still expect non-trivial breaking changes in the API in the coming releases. godot-rust adheres to [Cargo's semantic versioning](https://doc.rust-lang.org/cargo/reference/semver.html). -Minimum supported Rust version (MSRV) is **1.56**. +Minimum supported Rust version (MSRV) is **1.56**. We use the Rust 2021 Edition. ## Engine compatibility @@ -38,7 +38,7 @@ This is the recommended way of using godot-rust. After `bindgen` dependencies an ```toml [dependencies] -gdnative = "0.10.0-rc.0" +gdnative = "0.10" [lib] crate-type = ["cdylib"] @@ -71,7 +71,7 @@ A typical use case is to expose your own _Native Class_, a Rust API that can be This happens via dynamic libraries and the _GDNative interface_, which will be loaded from Godot. The necessary wiring is done behind the scenes by godot-rust. A simple "Hello world" application could look like this: -```rs +```rust use gdnative::prelude::*; #[derive(NativeClass)] @@ -104,7 +104,7 @@ godot_init!(init); > > Before launching the examples in the Godot editor, you must first run `cargo build` and wait for the build operations to finish successfully. > ->At startup, the Godot editor tries to load all resources used by the project, including the native library. If the latter isn't present, the editor will skip properties or signals associated with the missing native scripts in the scene. This will cause the scene tree to be non-functional for any sample that relies on properties or signals configured in the editor. +>At startup, the Godot editor tries to load all resources used by the project, including the native library. If the latter isn't present, the editor will skip properties or signals associated with the missing native scripts in the scene. This causes the scene tree to be non-functional for any sample that relies on properties or signals configured in the editor. The [/examples](https://github.com/godot-rust/godot-rust/tree/master/examples) directory contains several ready to use examples, complete with Godot projects and setup for easy compilation from Cargo: