Skip to content

Commit 755917f

Browse files
authored
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.
1 parent 01ce75d commit 755917f

File tree

9 files changed

+322
-25
lines changed

9 files changed

+322
-25
lines changed

crates/bevy_math/src/primitives/dim2.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ impl std::ops::Neg for Direction2d {
8080
}
8181

8282
/// A circle primitive
83-
#[derive(Clone, Copy, Debug)]
83+
#[derive(Clone, Copy, Debug, PartialEq)]
84+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
8485
pub struct Circle {
8586
/// The radius of the circle
8687
pub radius: f32,
@@ -115,7 +116,8 @@ impl Circle {
115116
}
116117

117118
/// An ellipse primitive
118-
#[derive(Clone, Copy, Debug)]
119+
#[derive(Clone, Copy, Debug, PartialEq)]
120+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
119121
pub struct Ellipse {
120122
/// Half of the width and height of the ellipse.
121123
///
@@ -160,7 +162,8 @@ impl Ellipse {
160162

161163
/// An unbounded plane in 2D space. It forms a separating surface through the origin,
162164
/// stretching infinitely far
163-
#[derive(Clone, Copy, Debug)]
165+
#[derive(Clone, Copy, Debug, PartialEq)]
166+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
164167
pub struct Plane2d {
165168
/// The normal of the plane. The plane will be placed perpendicular to this direction
166169
pub normal: Direction2d,
@@ -184,7 +187,8 @@ impl Plane2d {
184187
/// An infinite line along a direction in 2D space.
185188
///
186189
/// For a finite line: [`Segment2d`]
187-
#[derive(Clone, Copy, Debug)]
190+
#[derive(Clone, Copy, Debug, PartialEq)]
191+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
188192
pub struct Line2d {
189193
/// The direction of the line. The line extends infinitely in both the given direction
190194
/// and its opposite direction
@@ -194,7 +198,8 @@ impl Primitive2d for Line2d {}
194198

195199
/// A segment of a line along a direction in 2D space.
196200
#[doc(alias = "LineSegment2d")]
197-
#[derive(Clone, Debug)]
201+
#[derive(Clone, Copy, Debug, PartialEq)]
202+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
198203
pub struct Segment2d {
199204
/// The direction of the line segment
200205
pub direction: Direction2d,
@@ -241,9 +246,11 @@ impl Segment2d {
241246
/// A series of connected line segments in 2D space.
242247
///
243248
/// For a version without generics: [`BoxedPolyline2d`]
244-
#[derive(Clone, Debug)]
249+
#[derive(Clone, Debug, PartialEq)]
250+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
245251
pub struct Polyline2d<const N: usize> {
246252
/// The vertices of the polyline
253+
#[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
247254
pub vertices: [Vec2; N],
248255
}
249256
impl<const N: usize> Primitive2d for Polyline2d<N> {}
@@ -270,7 +277,8 @@ impl<const N: usize> Polyline2d<N> {
270277
/// in a `Box<[Vec2]>`.
271278
///
272279
/// For a version without alloc: [`Polyline2d`]
273-
#[derive(Clone, Debug)]
280+
#[derive(Clone, Debug, PartialEq)]
281+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
274282
pub struct BoxedPolyline2d {
275283
/// The vertices of the polyline
276284
pub vertices: Box<[Vec2]>,
@@ -294,7 +302,8 @@ impl BoxedPolyline2d {
294302
}
295303

296304
/// A triangle in 2D space
297-
#[derive(Clone, Debug, PartialEq)]
305+
#[derive(Clone, Copy, Debug, PartialEq)]
306+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
298307
pub struct Triangle2d {
299308
/// The vertices of the triangle
300309
pub vertices: [Vec2; 3],
@@ -367,7 +376,8 @@ impl Triangle2d {
367376

368377
/// A rectangle primitive
369378
#[doc(alias = "Quad")]
370-
#[derive(Clone, Copy, Debug)]
379+
#[derive(Clone, Copy, Debug, PartialEq)]
380+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
371381
pub struct Rectangle {
372382
/// Half of the width and height of the rectangle
373383
pub half_size: Vec2,
@@ -400,9 +410,11 @@ impl Rectangle {
400410
/// A polygon with N vertices.
401411
///
402412
/// For a version without generics: [`BoxedPolygon`]
403-
#[derive(Clone, Debug)]
413+
#[derive(Clone, Debug, PartialEq)]
414+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
404415
pub struct Polygon<const N: usize> {
405416
/// The vertices of the `Polygon`
417+
#[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
406418
pub vertices: [Vec2; N],
407419
}
408420
impl<const N: usize> Primitive2d for Polygon<N> {}
@@ -429,7 +441,8 @@ impl<const N: usize> Polygon<N> {
429441
/// in a `Box<[Vec2]>`.
430442
///
431443
/// For a version without alloc: [`Polygon`]
432-
#[derive(Clone, Debug)]
444+
#[derive(Clone, Debug, PartialEq)]
445+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
433446
pub struct BoxedPolygon {
434447
/// The vertices of the `BoxedPolygon`
435448
pub vertices: Box<[Vec2]>,
@@ -453,7 +466,8 @@ impl BoxedPolygon {
453466
}
454467

455468
/// A polygon where all vertices lie on a circle, equally far apart.
456-
#[derive(Clone, Copy, Debug)]
469+
#[derive(Clone, Copy, Debug, PartialEq)]
470+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
457471
pub struct RegularPolygon {
458472
/// The circumcircle on which all vertices lie
459473
pub circumcircle: Circle,

crates/bevy_math/src/primitives/dim3.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ impl std::ops::Neg for Direction3d {
8484
}
8585

8686
/// A sphere primitive
87-
#[derive(Clone, Copy, Debug)]
87+
#[derive(Clone, Copy, Debug, PartialEq)]
88+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
8889
pub struct Sphere {
8990
/// The radius of the sphere
9091
pub radius: f32,
@@ -120,7 +121,8 @@ impl Sphere {
120121

121122
/// An unbounded plane in 3D space. It forms a separating surface through the origin,
122123
/// stretching infinitely far
123-
#[derive(Clone, Copy, Debug)]
124+
#[derive(Clone, Copy, Debug, PartialEq)]
125+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
124126
pub struct Plane3d {
125127
/// The normal of the plane. The plane will be placed perpendicular to this direction
126128
pub normal: Direction3d,
@@ -144,7 +146,8 @@ impl Plane3d {
144146
/// An infinite line along a direction in 3D space.
145147
///
146148
/// For a finite line: [`Segment3d`]
147-
#[derive(Clone, Copy, Debug)]
149+
#[derive(Clone, Copy, Debug, PartialEq)]
150+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
148151
pub struct Line3d {
149152
/// The direction of the line
150153
pub direction: Direction3d,
@@ -153,7 +156,8 @@ impl Primitive3d for Line3d {}
153156

154157
/// A segment of a line along a direction in 3D space.
155158
#[doc(alias = "LineSegment3d")]
156-
#[derive(Clone, Debug)]
159+
#[derive(Clone, Copy, Debug, PartialEq)]
160+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
157161
pub struct Segment3d {
158162
/// The direction of the line
159163
pub direction: Direction3d,
@@ -200,9 +204,11 @@ impl Segment3d {
200204
/// A series of connected line segments in 3D space.
201205
///
202206
/// For a version without generics: [`BoxedPolyline3d`]
203-
#[derive(Clone, Debug)]
207+
#[derive(Clone, Debug, PartialEq)]
208+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
204209
pub struct Polyline3d<const N: usize> {
205210
/// The vertices of the polyline
211+
#[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
206212
pub vertices: [Vec3; N],
207213
}
208214
impl<const N: usize> Primitive3d for Polyline3d<N> {}
@@ -229,7 +235,8 @@ impl<const N: usize> Polyline3d<N> {
229235
/// in a `Box<[Vec3]>`.
230236
///
231237
/// For a version without alloc: [`Polyline3d`]
232-
#[derive(Clone, Debug)]
238+
#[derive(Clone, Debug, PartialEq)]
239+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
233240
pub struct BoxedPolyline3d {
234241
/// The vertices of the polyline
235242
pub vertices: Box<[Vec3]>,
@@ -253,7 +260,8 @@ impl BoxedPolyline3d {
253260
}
254261

255262
/// A cuboid primitive, more commonly known as a box.
256-
#[derive(Clone, Copy, Debug)]
263+
#[derive(Clone, Copy, Debug, PartialEq)]
264+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
257265
pub struct Cuboid {
258266
/// Half of the width, height and depth of the cuboid
259267
pub half_size: Vec3,
@@ -285,7 +293,8 @@ impl Cuboid {
285293
}
286294

287295
/// A cylinder primitive
288-
#[derive(Clone, Copy, Debug)]
296+
#[derive(Clone, Copy, Debug, PartialEq)]
297+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
289298
pub struct Cylinder {
290299
/// The radius of the cylinder
291300
pub radius: f32,
@@ -306,7 +315,8 @@ impl Cylinder {
306315

307316
/// A capsule primitive.
308317
/// A capsule is defined as a surface at a distance (radius) from a line
309-
#[derive(Clone, Copy, Debug)]
318+
#[derive(Clone, Copy, Debug, PartialEq)]
319+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
310320
pub struct Capsule {
311321
/// The radius of the capsule
312322
pub radius: f32,
@@ -327,7 +337,8 @@ impl Capsule {
327337
}
328338

329339
/// A cone primitive.
330-
#[derive(Clone, Copy, Debug)]
340+
#[derive(Clone, Copy, Debug, PartialEq)]
341+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
331342
pub struct Cone {
332343
/// The radius of the base
333344
pub radius: f32,
@@ -339,7 +350,8 @@ impl Primitive3d for Cone {}
339350
/// A conical frustum primitive.
340351
/// A conical frustum can be created
341352
/// by slicing off a section of a cone.
342-
#[derive(Clone, Copy, Debug)]
353+
#[derive(Clone, Copy, Debug, PartialEq)]
354+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
343355
pub struct ConicalFrustum {
344356
/// The radius of the top of the frustum
345357
pub radius_top: f32,
@@ -370,6 +382,7 @@ pub enum TorusKind {
370382

371383
/// A torus primitive, often representing a ring or donut shape
372384
#[derive(Clone, Copy, Debug, PartialEq)]
385+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
373386
pub struct Torus {
374387
/// The radius of the tube of the torus
375388
#[doc(

crates/bevy_math/src/primitives/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ mod dim2;
66
pub use dim2::*;
77
mod dim3;
88
pub use dim3::*;
9+
#[cfg(feature = "serialize")]
10+
mod serde;
911

1012
/// A marker trait for 2D primitives
1113
pub trait Primitive2d {}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//! This module defines serialization/deserialization for const generic arrays.
2+
//! Unlike serde's default behavior, it supports arbitrarily large arrays.
3+
//! The code is based on this github comment:
4+
//! <https://github.com/serde-rs/serde/issues/1937#issuecomment-812137971>
5+
6+
pub(crate) mod array {
7+
use serde::{
8+
de::{SeqAccess, Visitor},
9+
ser::SerializeTuple,
10+
Deserialize, Deserializer, Serialize, Serializer,
11+
};
12+
use std::marker::PhantomData;
13+
14+
pub fn serialize<S: Serializer, T: Serialize, const N: usize>(
15+
data: &[T; N],
16+
ser: S,
17+
) -> Result<S::Ok, S::Error> {
18+
let mut s = ser.serialize_tuple(N)?;
19+
for item in data {
20+
s.serialize_element(item)?;
21+
}
22+
s.end()
23+
}
24+
25+
struct GenericArrayVisitor<T, const N: usize>(PhantomData<T>);
26+
27+
impl<'de, T, const N: usize> Visitor<'de> for GenericArrayVisitor<T, N>
28+
where
29+
T: Deserialize<'de>,
30+
{
31+
type Value = [T; N];
32+
33+
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
34+
formatter.write_str(&format!("an array of length {}", N))
35+
}
36+
37+
#[inline]
38+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
39+
where
40+
A: SeqAccess<'de>,
41+
{
42+
let mut data = Vec::with_capacity(N);
43+
for _ in 0..N {
44+
match (seq.next_element())? {
45+
Some(val) => data.push(val),
46+
None => return Err(serde::de::Error::invalid_length(N, &self)),
47+
}
48+
}
49+
match data.try_into() {
50+
Ok(arr) => Ok(arr),
51+
Err(_) => unreachable!(),
52+
}
53+
}
54+
}
55+
56+
pub fn deserialize<'de, D, T, const N: usize>(deserializer: D) -> Result<[T; N], D::Error>
57+
where
58+
D: Deserializer<'de>,
59+
T: Deserialize<'de>,
60+
{
61+
deserializer.deserialize_tuple(N, GenericArrayVisitor::<T, N>(PhantomData))
62+
}
63+
}

crates/bevy_reflect/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ readme = "README.md"
1212
[features]
1313
default = []
1414
# When enabled, provides Bevy-related reflection implementations
15-
bevy = ["glam", "smallvec", "bevy_math", "smol_str"]
15+
bevy = ["smallvec", "bevy_math", "smol_str"]
16+
glam = ["dep:glam"]
17+
bevy_math = ["glam", "dep:bevy_math"]
1618
smallvec = []
1719
# When enabled, allows documentation comments to be accessed via reflection
1820
documentation = ["bevy_reflect_derive/documentation"]

0 commit comments

Comments
 (0)