Skip to content

Commit

Permalink
Derive PartialEq, Serialize, Deserialize and Reflect on primitives (b…
Browse files Browse the repository at this point in the history
…evyengine#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.
  • Loading branch information
NiseVoid authored and tjamaan committed Feb 6, 2024
1 parent 1258aa3 commit d85f114
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 25 deletions.
38 changes: 26 additions & 12 deletions crates/bevy_math/src/primitives/dim2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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> {}
Expand All @@ -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]>,
Expand All @@ -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],
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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> {}
Expand All @@ -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]>,
Expand All @@ -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,
Expand Down
35 changes: 24 additions & 11 deletions crates/bevy_math/src/primitives/dim3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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> {}
Expand All @@ -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]>,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_math/src/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down
63 changes: 63 additions & 0 deletions crates/bevy_math/src/primitives/serde.rs
Original file line number Diff line number Diff line change
@@ -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))
}
}
4 changes: 3 additions & 1 deletion crates/bevy_reflect/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
Loading

0 comments on commit d85f114

Please sign in to comment.