2021-12-18 22:59:55 +00:00
|
|
|
#![doc = include_str!("../README.md")]
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
mod list;
|
|
|
|
mod map;
|
|
|
|
mod path;
|
|
|
|
mod reflect;
|
|
|
|
mod struct_trait;
|
2021-01-08 03:50:09 +00:00
|
|
|
mod tuple;
|
2020-11-28 00:39:59 +00:00
|
|
|
mod tuple_struct;
|
|
|
|
mod type_registry;
|
|
|
|
mod type_uuid;
|
|
|
|
mod impls {
|
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
mod glam;
|
|
|
|
#[cfg(feature = "smallvec")]
|
|
|
|
mod smallvec;
|
|
|
|
mod std;
|
|
|
|
|
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
pub use self::glam::*;
|
|
|
|
#[cfg(feature = "smallvec")]
|
|
|
|
pub use self::smallvec::*;
|
|
|
|
pub use self::std::*;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub mod serde;
|
2022-05-03 19:20:13 +00:00
|
|
|
pub mod std_traits;
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
pub mod prelude {
|
2022-05-03 19:20:13 +00:00
|
|
|
pub use crate::std_traits::*;
|
2021-04-27 18:29:33 +00:00
|
|
|
#[doc(hidden)]
|
2020-11-28 00:39:59 +00:00
|
|
|
pub use crate::{
|
|
|
|
reflect_trait, GetField, GetTupleStructField, Reflect, ReflectDeserialize, Struct,
|
|
|
|
TupleStruct,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub use impls::*;
|
|
|
|
pub use list::*;
|
|
|
|
pub use map::*;
|
|
|
|
pub use path::*;
|
|
|
|
pub use reflect::*;
|
|
|
|
pub use struct_trait::*;
|
2021-01-08 03:50:09 +00:00
|
|
|
pub use tuple::*;
|
2020-11-28 00:39:59 +00:00
|
|
|
pub use tuple_struct::*;
|
|
|
|
pub use type_registry::*;
|
|
|
|
pub use type_uuid::*;
|
|
|
|
|
|
|
|
pub use bevy_reflect_derive::*;
|
|
|
|
pub use erased_serde;
|
|
|
|
|
2022-04-26 19:41:25 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub mod __macro_exports {
|
|
|
|
use crate::Uuid;
|
|
|
|
|
|
|
|
/// Generates a new UUID from the given UUIDs `a` and `b`,
|
|
|
|
/// where the bytes are generated by a bitwise `a ^ b.rotate_right(1)`.
|
|
|
|
/// The generated UUID will be a `UUIDv4` (meaning that the bytes should be random, not e.g. derived from the system time).
|
|
|
|
#[allow(clippy::unusual_byte_groupings)] // unusual byte grouping is meant to signal the relevant bits
|
|
|
|
pub const fn generate_composite_uuid(a: Uuid, b: Uuid) -> Uuid {
|
|
|
|
let mut new = [0; 16];
|
|
|
|
let mut i = 0;
|
|
|
|
while i < new.len() {
|
|
|
|
// rotating ensures different uuids for A<B<C>> and B<A<C>> because: A ^ (B ^ C) = B ^ (A ^ C)
|
|
|
|
// notice that you have to rotate the second parameter: A.rr ^ (B.rr ^ C) = B.rr ^ (A.rr ^ C)
|
|
|
|
// Solution: A ^ (B ^ C.rr).rr != B ^ (A ^ C.rr).rr
|
|
|
|
new[i] = a.as_bytes()[i] ^ b.as_bytes()[i].rotate_right(1);
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Version: the most significant 4 bits in the 6th byte: 11110000
|
|
|
|
new[6] = new[6] & 0b0000_1111 | 0b0100_0000; // set version to v4
|
|
|
|
|
|
|
|
// Variant: the most significant 3 bits in the 8th byte: 11100000
|
|
|
|
new[8] = new[8] & 0b000_11111 | 0b100_00000; // set variant to rfc4122
|
|
|
|
|
|
|
|
Uuid::from_bytes(new)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
#[cfg(test)]
|
2021-02-22 08:42:19 +00:00
|
|
|
#[allow(clippy::blacklisted_name, clippy::approx_constant)]
|
2020-11-28 00:39:59 +00:00
|
|
|
mod tests {
|
2022-05-09 16:32:15 +00:00
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
use ::glam::{vec3, Vec3};
|
2020-11-28 00:39:59 +00:00
|
|
|
use ::serde::de::DeserializeSeed;
|
|
|
|
use bevy_utils::HashMap;
|
|
|
|
use ron::{
|
|
|
|
ser::{to_string_pretty, PrettyConfig},
|
|
|
|
Deserializer,
|
|
|
|
};
|
|
|
|
|
|
|
|
use super::*;
|
2021-05-19 19:03:36 +00:00
|
|
|
use crate as bevy_reflect;
|
|
|
|
use crate::serde::{ReflectDeserializer, ReflectSerializer};
|
2021-02-22 08:42:19 +00:00
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
#[test]
|
|
|
|
fn reflect_struct() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
b: f32,
|
|
|
|
c: Bar,
|
|
|
|
}
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut foo = Foo {
|
|
|
|
a: 42,
|
|
|
|
b: 3.14,
|
|
|
|
c: Bar { x: 1 },
|
|
|
|
};
|
|
|
|
|
|
|
|
let a = *foo.get_field::<u32>("a").unwrap();
|
|
|
|
assert_eq!(a, 42);
|
|
|
|
|
|
|
|
*foo.get_field_mut::<u32>("a").unwrap() += 1;
|
|
|
|
assert_eq!(foo.a, 43);
|
|
|
|
|
|
|
|
let bar = foo.get_field::<Bar>("c").unwrap();
|
|
|
|
assert_eq!(bar.x, 1);
|
|
|
|
|
|
|
|
// nested retrieval
|
|
|
|
let c = foo.field("c").unwrap();
|
|
|
|
if let ReflectRef::Struct(value) = c.reflect_ref() {
|
|
|
|
assert_eq!(*value.get_field::<u32>("x").unwrap(), 1);
|
|
|
|
} else {
|
2020-12-02 19:31:16 +00:00
|
|
|
panic!("Expected a struct.");
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// patch Foo with a dynamic struct
|
|
|
|
let mut dynamic_struct = DynamicStruct::default();
|
|
|
|
dynamic_struct.insert("a", 123u32);
|
|
|
|
dynamic_struct.insert("should_be_ignored", 456);
|
|
|
|
|
|
|
|
foo.apply(&dynamic_struct);
|
|
|
|
assert_eq!(foo.a, 123);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_map() {
|
|
|
|
#[derive(Reflect, Hash)]
|
|
|
|
#[reflect(Hash)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
b: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
let key_a = Foo {
|
|
|
|
a: 1,
|
|
|
|
b: "k1".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let key_b = Foo {
|
|
|
|
a: 1,
|
|
|
|
b: "k1".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let key_c = Foo {
|
|
|
|
a: 3,
|
|
|
|
b: "k3".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(key_a, 10u32);
|
|
|
|
assert_eq!(10, *map.get(&key_b).unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
assert!(map.get(&key_c).is_none());
|
|
|
|
*map.get_mut(&key_b).unwrap().downcast_mut::<u32>().unwrap() = 20;
|
|
|
|
assert_eq!(20, *map.get(&key_b).unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-02-22 08:42:19 +00:00
|
|
|
#[allow(clippy::blacklisted_name)]
|
2020-11-28 00:39:59 +00:00
|
|
|
fn reflect_unit_struct() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo(u32, u64);
|
|
|
|
|
|
|
|
let mut foo = Foo(1, 2);
|
|
|
|
assert_eq!(1, *foo.get_field::<u32>(0).unwrap());
|
|
|
|
assert_eq!(2, *foo.get_field::<u64>(1).unwrap());
|
|
|
|
|
|
|
|
let mut patch = DynamicTupleStruct::default();
|
|
|
|
patch.insert(3u32);
|
|
|
|
patch.insert(4u64);
|
|
|
|
assert_eq!(3, *patch.field(0).unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
assert_eq!(4, *patch.field(1).unwrap().downcast_ref::<u64>().unwrap());
|
|
|
|
|
|
|
|
foo.apply(&patch);
|
|
|
|
assert_eq!(3, foo.0);
|
|
|
|
assert_eq!(4, foo.1);
|
|
|
|
|
|
|
|
let mut iter = patch.iter_fields();
|
|
|
|
assert_eq!(3, *iter.next().unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
assert_eq!(4, *iter.next().unwrap().downcast_ref::<u64>().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "the given key does not support hashing")]
|
|
|
|
fn reflect_map_no_hash() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let foo = Foo { a: 1 };
|
|
|
|
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(foo, 10u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_ignore() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
_b: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let foo = Foo { a: 1, _b: 2 };
|
|
|
|
|
|
|
|
let values: Vec<u32> = foo
|
|
|
|
.iter_fields()
|
|
|
|
.map(|value| *value.downcast_ref::<u32>().unwrap())
|
|
|
|
.collect();
|
|
|
|
assert_eq!(values, vec![1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_complex_patch() {
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Reflect, Eq, PartialEq, Debug, FromReflect)]
|
Reflection cleanup (#1536)
This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed.
Supersedes #1533 and resolves #1528.
---
I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here):
- Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive.
- Same as above for `struct` and `#[reflect(...)]`, respectively.
- If a `struct` is used as a component, it should also have `#[reflect(Component)]`
- All reflected types should be registered in their plugins
I treated the following as components (added `#[reflect(Component)]` if necessary):
- `bevy_render`
- `struct RenderLayers`
- `bevy_transform`
- `struct GlobalTransform`
- `struct Parent`
- `struct Transform`
- `bevy_ui`
- `struct Style`
Not treated as components:
- `bevy_math`
- `struct Size<T>`
- `struct Rect<T>`
- Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin.
- `bevy_render`
- `struct Color`
- `struct PipelineSpecialization`
- `struct ShaderSpecialization`
- `enum PrimitiveTopology`
- `enum IndexFormat`
Not Addressed:
- I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR.
- I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
|
|
|
#[reflect(PartialEq)]
|
2020-11-28 00:39:59 +00:00
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
_b: u32,
|
|
|
|
c: Vec<isize>,
|
|
|
|
d: HashMap<usize, i8>,
|
|
|
|
e: Bar,
|
2021-01-08 03:50:09 +00:00
|
|
|
f: (i32, Vec<isize>, Bar),
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
g: Vec<(Baz, HashMap<usize, Bar>)>,
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Reflect, Eq, PartialEq, Clone, Debug, FromReflect)]
|
Reflection cleanup (#1536)
This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed.
Supersedes #1533 and resolves #1528.
---
I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here):
- Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive.
- Same as above for `struct` and `#[reflect(...)]`, respectively.
- If a `struct` is used as a component, it should also have `#[reflect(Component)]`
- All reflected types should be registered in their plugins
I treated the following as components (added `#[reflect(Component)]` if necessary):
- `bevy_render`
- `struct RenderLayers`
- `bevy_transform`
- `struct GlobalTransform`
- `struct Parent`
- `struct Transform`
- `bevy_ui`
- `struct Style`
Not treated as components:
- `bevy_math`
- `struct Size<T>`
- `struct Rect<T>`
- Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin.
- `bevy_render`
- `struct Color`
- `struct PipelineSpecialization`
- `struct ShaderSpecialization`
- `enum PrimitiveTopology`
- `enum IndexFormat`
Not Addressed:
- I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR.
- I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
|
|
|
#[reflect(PartialEq)]
|
2020-11-28 00:39:59 +00:00
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Reflect, Eq, PartialEq, Debug, FromReflect)]
|
|
|
|
struct Baz(String);
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(1, 1);
|
|
|
|
hash_map.insert(2, 2);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
let mut hash_map_baz = HashMap::default();
|
|
|
|
hash_map_baz.insert(1, Bar { x: 0 });
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
let mut foo = Foo {
|
|
|
|
a: 1,
|
|
|
|
_b: 1,
|
|
|
|
c: vec![1, 2],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 1 },
|
2021-01-08 03:50:09 +00:00
|
|
|
f: (1, vec![1, 2], Bar { x: 1 }),
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
g: vec![(Baz("string".to_string()), hash_map_baz)],
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut foo_patch = DynamicStruct::default();
|
|
|
|
foo_patch.insert("a", 2u32);
|
|
|
|
foo_patch.insert("b", 2u32); // this should be ignored
|
|
|
|
|
|
|
|
let mut list = DynamicList::default();
|
|
|
|
list.push(3isize);
|
|
|
|
list.push(4isize);
|
|
|
|
list.push(5isize);
|
2021-01-08 03:50:09 +00:00
|
|
|
foo_patch.insert("c", list.clone_dynamic());
|
2020-11-28 00:39:59 +00:00
|
|
|
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(2usize, 3i8);
|
|
|
|
foo_patch.insert("d", map);
|
|
|
|
|
|
|
|
let mut bar_patch = DynamicStruct::default();
|
|
|
|
bar_patch.insert("x", 2u32);
|
2021-01-08 03:50:09 +00:00
|
|
|
foo_patch.insert("e", bar_patch.clone_dynamic());
|
|
|
|
|
|
|
|
let mut tuple = DynamicTuple::default();
|
|
|
|
tuple.insert(2i32);
|
|
|
|
tuple.insert(list);
|
|
|
|
tuple.insert(bar_patch);
|
|
|
|
foo_patch.insert("f", tuple);
|
2020-11-28 00:39:59 +00:00
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
let mut composite = DynamicList::default();
|
|
|
|
composite.push({
|
|
|
|
let mut tuple = DynamicTuple::default();
|
|
|
|
tuple.insert({
|
|
|
|
let mut tuple_struct = DynamicTupleStruct::default();
|
|
|
|
tuple_struct.insert("new_string".to_string());
|
|
|
|
tuple_struct
|
|
|
|
});
|
|
|
|
tuple.insert({
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(1usize, {
|
|
|
|
let mut struct_ = DynamicStruct::default();
|
|
|
|
struct_.insert("x", 7u32);
|
|
|
|
struct_
|
|
|
|
});
|
|
|
|
map
|
|
|
|
});
|
|
|
|
tuple
|
|
|
|
});
|
|
|
|
foo_patch.insert("g", composite);
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
foo.apply(&foo_patch);
|
|
|
|
|
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(1, 1);
|
|
|
|
hash_map.insert(2, 3);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
let mut hash_map_baz = HashMap::default();
|
|
|
|
hash_map_baz.insert(1, Bar { x: 7 });
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
let expected_foo = Foo {
|
|
|
|
a: 2,
|
|
|
|
_b: 1,
|
|
|
|
c: vec![3, 4, 5],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 2 },
|
2021-01-08 03:50:09 +00:00
|
|
|
f: (2, vec![3, 4, 5], Bar { x: 2 }),
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
g: vec![(Baz("new_string".to_string()), hash_map_baz.clone())],
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(foo, expected_foo);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
let new_foo = Foo::from_reflect(&foo_patch)
|
|
|
|
.expect("error while creating a concrete type from a dynamic type");
|
|
|
|
|
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(2, 3);
|
|
|
|
|
|
|
|
let expected_new_foo = Foo {
|
|
|
|
a: 2,
|
|
|
|
_b: 0,
|
|
|
|
c: vec![3, 4, 5],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 2 },
|
|
|
|
f: (2, vec![3, 4, 5], Bar { x: 2 }),
|
|
|
|
g: vec![(Baz("new_string".to_string()), hash_map_baz)],
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(new_foo, expected_new_foo);
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_serialize() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
_b: u32,
|
|
|
|
c: Vec<isize>,
|
|
|
|
d: HashMap<usize, i8>,
|
|
|
|
e: Bar,
|
|
|
|
f: String,
|
2021-01-08 03:50:09 +00:00
|
|
|
g: (i32, Vec<isize>, Bar),
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(1, 1);
|
|
|
|
hash_map.insert(2, 2);
|
|
|
|
let foo = Foo {
|
|
|
|
a: 1,
|
|
|
|
_b: 1,
|
|
|
|
c: vec![1, 2],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 1 },
|
|
|
|
f: "hi".to_string(),
|
2021-01-08 03:50:09 +00:00
|
|
|
g: (1, vec![1, 2], Bar { x: 1 }),
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut registry = TypeRegistry::default();
|
|
|
|
registry.register::<u32>();
|
|
|
|
registry.register::<isize>();
|
|
|
|
registry.register::<usize>();
|
|
|
|
registry.register::<Bar>();
|
|
|
|
registry.register::<String>();
|
|
|
|
registry.register::<i8>();
|
2021-01-08 03:50:09 +00:00
|
|
|
registry.register::<i32>();
|
2020-11-28 00:39:59 +00:00
|
|
|
|
|
|
|
let serializer = ReflectSerializer::new(&foo, ®istry);
|
|
|
|
let serialized = to_string_pretty(&serializer, PrettyConfig::default()).unwrap();
|
|
|
|
|
|
|
|
let mut deserializer = Deserializer::from_str(&serialized).unwrap();
|
|
|
|
let reflect_deserializer = ReflectDeserializer::new(®istry);
|
|
|
|
let value = reflect_deserializer.deserialize(&mut deserializer).unwrap();
|
|
|
|
let dynamic_struct = value.take::<DynamicStruct>().unwrap();
|
|
|
|
|
2020-12-01 19:15:07 +00:00
|
|
|
assert!(foo.reflect_partial_eq(&dynamic_struct).unwrap());
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_take() {
|
|
|
|
#[derive(Reflect, Debug, PartialEq)]
|
Reflection cleanup (#1536)
This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed.
Supersedes #1533 and resolves #1528.
---
I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here):
- Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive.
- Same as above for `struct` and `#[reflect(...)]`, respectively.
- If a `struct` is used as a component, it should also have `#[reflect(Component)]`
- All reflected types should be registered in their plugins
I treated the following as components (added `#[reflect(Component)]` if necessary):
- `bevy_render`
- `struct RenderLayers`
- `bevy_transform`
- `struct GlobalTransform`
- `struct Parent`
- `struct Transform`
- `bevy_ui`
- `struct Style`
Not treated as components:
- `bevy_math`
- `struct Size<T>`
- `struct Rect<T>`
- Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin.
- `bevy_render`
- `struct Color`
- `struct PipelineSpecialization`
- `struct ShaderSpecialization`
- `enum PrimitiveTopology`
- `enum IndexFormat`
Not Addressed:
- I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR.
- I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
|
|
|
#[reflect(PartialEq)]
|
2020-11-28 00:39:59 +00:00
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let x: Box<dyn Reflect> = Box::new(Bar { x: 2 });
|
|
|
|
let y = x.take::<Bar>().unwrap();
|
|
|
|
assert_eq!(y, Bar { x: 2 });
|
|
|
|
}
|
2021-02-02 21:57:26 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn dynamic_names() {
|
|
|
|
let list = Vec::<usize>::new();
|
|
|
|
let dyn_list = list.clone_dynamic();
|
|
|
|
assert_eq!(dyn_list.type_name(), std::any::type_name::<Vec<usize>>());
|
|
|
|
|
|
|
|
let map = HashMap::<usize, String>::default();
|
|
|
|
let dyn_map = map.clone_dynamic();
|
|
|
|
assert_eq!(
|
|
|
|
dyn_map.type_name(),
|
|
|
|
std::any::type_name::<HashMap<usize, String>>()
|
|
|
|
);
|
|
|
|
|
|
|
|
let tuple = (0usize, "1".to_string(), 2.0f32);
|
|
|
|
let mut dyn_tuple = tuple.clone_dynamic();
|
|
|
|
dyn_tuple.insert::<usize>(3);
|
|
|
|
assert_eq!(
|
|
|
|
dyn_tuple.type_name(),
|
|
|
|
std::any::type_name::<(usize, String, f32, usize)>()
|
|
|
|
);
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct TestStruct {
|
|
|
|
a: usize,
|
|
|
|
}
|
|
|
|
let struct_ = TestStruct { a: 0 };
|
|
|
|
let dyn_struct = struct_.clone_dynamic();
|
|
|
|
assert_eq!(dyn_struct.type_name(), std::any::type_name::<TestStruct>());
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct TestTupleStruct(usize);
|
|
|
|
let tuple_struct = TestTupleStruct(0);
|
|
|
|
let dyn_tuple_struct = tuple_struct.clone_dynamic();
|
|
|
|
assert_eq!(
|
|
|
|
dyn_tuple_struct.type_name(),
|
|
|
|
std::any::type_name::<TestTupleStruct>()
|
|
|
|
);
|
|
|
|
}
|
2022-04-25 13:54:48 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn as_reflect() {
|
|
|
|
trait TestTrait: Reflect {}
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct TestStruct;
|
|
|
|
|
|
|
|
impl TestTrait for TestStruct {}
|
|
|
|
|
|
|
|
let trait_object: Box<dyn TestTrait> = Box::new(TestStruct);
|
|
|
|
|
|
|
|
// Should compile:
|
|
|
|
let _ = trait_object.as_reflect();
|
|
|
|
}
|
2022-05-09 16:32:15 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
mod glam {
|
|
|
|
use super::*;
|
bevy_reflect_derive: Tidying up the code (#4712)
# Objective
The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work.
## Solution
Split `bevy_reflect_derive` into many more submodules. The submodules include:
* `container_attributes` - Code relating to container attributes
* `derive_data` - Code relating to reflection-based derive metadata
* `field_attributes` - Code relating to field attributes
* `impls` - Code containing actual reflection implementations
* `reflect_value` - Code relating to reflection-based value metadata
* `registration` - Code relating to type registration
* `utility` - General-purpose utility functions
This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines).
By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions.
### Metadata Structs
This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs.
Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute).
We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one.
### Out of Scope
_Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR.
## 🔍 Focus Points (for reviewers)
I know it's a lot to sift through, so here is a list of **key points for reviewers**:
- The following files contain code that was mostly just relocated:
- `reflect_value.rs`
- `registration.rs`
- `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments
- Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication.
- `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention.
- Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus.
- There was no change to the `lib.rs` exports so the end-user API should be the same.
## Prior Work
This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year!
---
## Changelog
* **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules
* **[INTERNAL]** Add `ReflectFieldAttr`
* **[INTERNAL]** Add `ReflectDeriveData`
* Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`)
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
|
|
|
use ::serde::Serialize;
|
2022-05-09 16:32:15 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_serialization() {
|
|
|
|
let v = vec3(12.0, 3.0, -6.9);
|
|
|
|
|
|
|
|
let mut registry = TypeRegistry::default();
|
|
|
|
registry.add_registration(Vec3::get_type_registration());
|
|
|
|
|
|
|
|
let ser = ReflectSerializer::new(&v, ®istry);
|
|
|
|
|
|
|
|
let mut dest = vec![];
|
|
|
|
let mut serializer = ron::ser::Serializer::new(&mut dest, None, false)
|
|
|
|
.expect("Failed to acquire serializer");
|
|
|
|
|
|
|
|
ser.serialize(&mut serializer).expect("Failed to serialize");
|
|
|
|
|
|
|
|
let result = String::from_utf8(dest).expect("Failed to convert to string");
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
result,
|
|
|
|
r#"{"type":"glam::vec3::Vec3","struct":{"x":{"type":"f32","value":12},"y":{"type":"f32","value":3},"z":{"type":"f32","value":-6.9}}}"#
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_deserialization() {
|
|
|
|
let data = r#"{"type":"glam::vec3::Vec3","struct":{"x":{"type":"f32","value":12},"y":{"type":"f32","value":3},"z":{"type":"f32","value":-6.9}}}"#;
|
|
|
|
|
|
|
|
let mut registry = TypeRegistry::default();
|
|
|
|
registry.add_registration(Vec3::get_type_registration());
|
|
|
|
registry.add_registration(f32::get_type_registration());
|
|
|
|
|
|
|
|
let de = ReflectDeserializer::new(®istry);
|
|
|
|
|
|
|
|
let mut deserializer =
|
|
|
|
ron::de::Deserializer::from_str(data).expect("Failed to acquire deserializer");
|
|
|
|
|
|
|
|
let dynamic_struct = de
|
|
|
|
.deserialize(&mut deserializer)
|
|
|
|
.expect("Failed to deserialize");
|
|
|
|
|
|
|
|
let mut result = Vec3::default();
|
|
|
|
|
|
|
|
result.apply(&*dynamic_struct);
|
|
|
|
|
|
|
|
assert_eq!(result, vec3(12.0, 3.0, -6.9));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_field_access() {
|
|
|
|
let mut v = vec3(1.0, 2.0, 3.0);
|
|
|
|
|
|
|
|
assert_eq!(*v.get_field::<f32>("x").unwrap(), 1.0);
|
|
|
|
|
|
|
|
*v.get_field_mut::<f32>("y").unwrap() = 6.0;
|
|
|
|
|
|
|
|
assert_eq!(v.y, 6.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_path_access() {
|
|
|
|
let mut v = vec3(1.0, 2.0, 3.0);
|
|
|
|
|
|
|
|
assert_eq!(*v.path("x").unwrap().downcast_ref::<f32>().unwrap(), 1.0);
|
|
|
|
|
|
|
|
*v.path_mut("y").unwrap().downcast_mut::<f32>().unwrap() = 6.0;
|
|
|
|
|
|
|
|
assert_eq!(v.y, 6.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_apply_dynamic() {
|
|
|
|
let mut v = vec3(3.0, 3.0, 3.0);
|
|
|
|
|
|
|
|
let mut d = DynamicStruct::default();
|
|
|
|
d.insert("x", 4.0f32);
|
|
|
|
d.insert("y", 2.0f32);
|
|
|
|
d.insert("z", 1.0f32);
|
|
|
|
|
|
|
|
v.apply(&d);
|
|
|
|
|
|
|
|
assert_eq!(v, vec3(4.0, 2.0, 1.0));
|
|
|
|
}
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|