Derive PartialEq, Serialize, Deserialize and Reflect on primitives (#11514)

# Objective

- Implement common traits on primitives

## Solution

- Derive PartialEq on types that were missing it.
- Derive Copy on small types that were missing it.
- Derive Serialize/Deserialize if the feature on bevy_math is enabled.
- Add a lot of cursed stuff to the bevy_reflect `impls` module.
This commit is contained in:
NiseVoid 2024-01-28 15:55:30 +01:00 committed by GitHub
parent 01ce75d7bf
commit 755917fe4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 322 additions and 25 deletions

View file

@ -80,7 +80,8 @@ impl std::ops::Neg for Direction2d {
}
/// A circle primitive
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Circle {
/// The radius of the circle
pub radius: f32,
@ -115,7 +116,8 @@ impl Circle {
}
/// An ellipse primitive
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Ellipse {
/// Half of the width and height of the ellipse.
///
@ -160,7 +162,8 @@ impl Ellipse {
/// An unbounded plane in 2D space. It forms a separating surface through the origin,
/// stretching infinitely far
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Plane2d {
/// The normal of the plane. The plane will be placed perpendicular to this direction
pub normal: Direction2d,
@ -184,7 +187,8 @@ impl Plane2d {
/// An infinite line along a direction in 2D space.
///
/// For a finite line: [`Segment2d`]
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Line2d {
/// The direction of the line. The line extends infinitely in both the given direction
/// and its opposite direction
@ -194,7 +198,8 @@ impl Primitive2d for Line2d {}
/// A segment of a line along a direction in 2D space.
#[doc(alias = "LineSegment2d")]
#[derive(Clone, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Segment2d {
/// The direction of the line segment
pub direction: Direction2d,
@ -241,9 +246,11 @@ impl Segment2d {
/// A series of connected line segments in 2D space.
///
/// For a version without generics: [`BoxedPolyline2d`]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Polyline2d<const N: usize> {
/// The vertices of the polyline
#[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
pub vertices: [Vec2; N],
}
impl<const N: usize> Primitive2d for Polyline2d<N> {}
@ -270,7 +277,8 @@ impl<const N: usize> Polyline2d<N> {
/// in a `Box<[Vec2]>`.
///
/// For a version without alloc: [`Polyline2d`]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct BoxedPolyline2d {
/// The vertices of the polyline
pub vertices: Box<[Vec2]>,
@ -294,7 +302,8 @@ impl BoxedPolyline2d {
}
/// A triangle in 2D space
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Triangle2d {
/// The vertices of the triangle
pub vertices: [Vec2; 3],
@ -367,7 +376,8 @@ impl Triangle2d {
/// A rectangle primitive
#[doc(alias = "Quad")]
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Rectangle {
/// Half of the width and height of the rectangle
pub half_size: Vec2,
@ -400,9 +410,11 @@ impl Rectangle {
/// A polygon with N vertices.
///
/// For a version without generics: [`BoxedPolygon`]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Polygon<const N: usize> {
/// The vertices of the `Polygon`
#[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
pub vertices: [Vec2; N],
}
impl<const N: usize> Primitive2d for Polygon<N> {}
@ -429,7 +441,8 @@ impl<const N: usize> Polygon<N> {
/// in a `Box<[Vec2]>`.
///
/// For a version without alloc: [`Polygon`]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct BoxedPolygon {
/// The vertices of the `BoxedPolygon`
pub vertices: Box<[Vec2]>,
@ -453,7 +466,8 @@ impl BoxedPolygon {
}
/// A polygon where all vertices lie on a circle, equally far apart.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct RegularPolygon {
/// The circumcircle on which all vertices lie
pub circumcircle: Circle,

View file

@ -84,7 +84,8 @@ impl std::ops::Neg for Direction3d {
}
/// A sphere primitive
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Sphere {
/// The radius of the sphere
pub radius: f32,
@ -120,7 +121,8 @@ impl Sphere {
/// An unbounded plane in 3D space. It forms a separating surface through the origin,
/// stretching infinitely far
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Plane3d {
/// The normal of the plane. The plane will be placed perpendicular to this direction
pub normal: Direction3d,
@ -144,7 +146,8 @@ impl Plane3d {
/// An infinite line along a direction in 3D space.
///
/// For a finite line: [`Segment3d`]
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Line3d {
/// The direction of the line
pub direction: Direction3d,
@ -153,7 +156,8 @@ impl Primitive3d for Line3d {}
/// A segment of a line along a direction in 3D space.
#[doc(alias = "LineSegment3d")]
#[derive(Clone, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Segment3d {
/// The direction of the line
pub direction: Direction3d,
@ -200,9 +204,11 @@ impl Segment3d {
/// A series of connected line segments in 3D space.
///
/// For a version without generics: [`BoxedPolyline3d`]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Polyline3d<const N: usize> {
/// The vertices of the polyline
#[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
pub vertices: [Vec3; N],
}
impl<const N: usize> Primitive3d for Polyline3d<N> {}
@ -229,7 +235,8 @@ impl<const N: usize> Polyline3d<N> {
/// in a `Box<[Vec3]>`.
///
/// For a version without alloc: [`Polyline3d`]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct BoxedPolyline3d {
/// The vertices of the polyline
pub vertices: Box<[Vec3]>,
@ -253,7 +260,8 @@ impl BoxedPolyline3d {
}
/// A cuboid primitive, more commonly known as a box.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Cuboid {
/// Half of the width, height and depth of the cuboid
pub half_size: Vec3,
@ -285,7 +293,8 @@ impl Cuboid {
}
/// A cylinder primitive
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Cylinder {
/// The radius of the cylinder
pub radius: f32,
@ -306,7 +315,8 @@ impl Cylinder {
/// A capsule primitive.
/// A capsule is defined as a surface at a distance (radius) from a line
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Capsule {
/// The radius of the capsule
pub radius: f32,
@ -327,7 +337,8 @@ impl Capsule {
}
/// A cone primitive.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Cone {
/// The radius of the base
pub radius: f32,
@ -339,7 +350,8 @@ impl Primitive3d for Cone {}
/// A conical frustum primitive.
/// A conical frustum can be created
/// by slicing off a section of a cone.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ConicalFrustum {
/// The radius of the top of the frustum
pub radius_top: f32,
@ -370,6 +382,7 @@ pub enum TorusKind {
/// A torus primitive, often representing a ring or donut shape
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Torus {
/// The radius of the tube of the torus
#[doc(

View file

@ -6,6 +6,8 @@ mod dim2;
pub use dim2::*;
mod dim3;
pub use dim3::*;
#[cfg(feature = "serialize")]
mod serde;
/// A marker trait for 2D primitives
pub trait Primitive2d {}

View file

@ -0,0 +1,63 @@
//! This module defines serialization/deserialization for const generic arrays.
//! Unlike serde's default behavior, it supports arbitrarily large arrays.
//! The code is based on this github comment:
//! <https://github.com/serde-rs/serde/issues/1937#issuecomment-812137971>
pub(crate) mod array {
use serde::{
de::{SeqAccess, Visitor},
ser::SerializeTuple,
Deserialize, Deserializer, Serialize, Serializer,
};
use std::marker::PhantomData;
pub fn serialize<S: Serializer, T: Serialize, const N: usize>(
data: &[T; N],
ser: S,
) -> Result<S::Ok, S::Error> {
let mut s = ser.serialize_tuple(N)?;
for item in data {
s.serialize_element(item)?;
}
s.end()
}
struct GenericArrayVisitor<T, const N: usize>(PhantomData<T>);
impl<'de, T, const N: usize> Visitor<'de> for GenericArrayVisitor<T, N>
where
T: Deserialize<'de>,
{
type Value = [T; N];
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str(&format!("an array of length {}", N))
}
#[inline]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut data = Vec::with_capacity(N);
for _ in 0..N {
match (seq.next_element())? {
Some(val) => data.push(val),
None => return Err(serde::de::Error::invalid_length(N, &self)),
}
}
match data.try_into() {
Ok(arr) => Ok(arr),
Err(_) => unreachable!(),
}
}
}
pub fn deserialize<'de, D, T, const N: usize>(deserializer: D) -> Result<[T; N], D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
deserializer.deserialize_tuple(N, GenericArrayVisitor::<T, N>(PhantomData))
}
}

View file

@ -12,7 +12,9 @@ readme = "README.md"
[features]
default = []
# When enabled, provides Bevy-related reflection implementations
bevy = ["glam", "smallvec", "bevy_math", "smol_str"]
bevy = ["smallvec", "bevy_math", "smol_str"]
glam = ["dep:glam"]
bevy_math = ["glam", "dep:bevy_math"]
smallvec = []
# When enabled, allows documentation comments to be accessed via reflection
documentation = ["bevy_reflect_derive/documentation"]

View file

@ -0,0 +1,93 @@
use crate as bevy_reflect;
use crate::{ReflectDeserialize, ReflectSerialize};
use bevy_math::{primitives::*, Vec2};
use bevy_reflect_derive::{impl_reflect_struct, impl_reflect_value};
impl_reflect_value!(::bevy_math::primitives::Direction2d(
Debug,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Circle {
radius: f32,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Ellipse {
pub half_size: Vec2,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Plane2d {
normal: Direction2d,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Line2d {
direction: Direction2d,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Segment2d {
direction: Direction2d,
half_length: f32,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq)]
#[type_path = "bevy_math::primitives"]
struct Polyline2d<const N: usize> {
vertices: [Vec2; N],
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Triangle2d {
vertices: [Vec2; 3],
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Rectangle {
half_size: Vec2,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq)]
#[type_path = "bevy_math::primitives"]
struct Polygon<const N: usize> {
vertices: [Vec2; N],
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct RegularPolygon {
circumcircle: Circle,
sides: usize,
}
);

View file

@ -0,0 +1,106 @@
use crate as bevy_reflect;
use crate::{ReflectDeserialize, ReflectSerialize};
use bevy_math::{primitives::*, Vec3};
use bevy_reflect_derive::{impl_reflect_struct, impl_reflect_value};
impl_reflect_value!(::bevy_math::primitives::Direction3d(
Debug,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Sphere {
radius: f32,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Plane3d {
normal: Direction3d,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Line3d {
direction: Direction3d,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Segment3d {
direction: Direction3d,
half_length: f32,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq)]
#[type_path = "bevy_math::primitives"]
struct Polyline3d<const N: usize> {
vertices: [Vec3; N],
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Cuboid {
half_size: Vec3,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Cylinder {
radius: f32,
half_height: f32,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Capsule {
radius: f32,
half_length: f32,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Cone {
radius: f32,
height: f32,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct ConicalFrustum {
radius_top: f32,
radius_bottom: f32,
height: f32,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Torus {
minor_radius: f32,
major_radius: f32,
}
);

View file

@ -482,7 +482,11 @@ mod impls {
#[cfg(feature = "glam")]
mod glam;
#[cfg(feature = "bevy_math")]
mod rect;
mod math {
mod primitives2d;
mod primitives3d;
mod rect;
}
#[cfg(feature = "smallvec")]
mod smallvec;
#[cfg(feature = "smol_str")]