diff --git a/src/core/dimension.rs b/src/core/dimension.rs index d3bd819d..ec8f3c57 100644 --- a/src/core/dimension.rs +++ b/src/core/dimension.rs @@ -7,9 +7,11 @@ use std::any::Any; use std::ops::{Add, Sub, Mul, Div}; use typenum::{self, Unsigned, UInt, B1, Bit, UTerm, Sum, Prod, Diff, Quot}; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + /// Dim of dynamically-sized algebraic entities. #[derive(Clone, Copy, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct Dynamic { value: usize } @@ -24,6 +26,24 @@ impl Dynamic { } } +#[cfg(feature = "serde-serialize")] +impl Serialize for Dynamic { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where S: Serializer + { + self.value.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de> Deserialize<'de> for Dynamic { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where D: Deserializer<'de> + { + usize::deserialize(deserializer).map(|x| Dynamic { value: x }) + } +} + /// Trait implemented by `Dynamic`. pub trait IsDynamic { } /// Trait implemented by `Dynamic` and type-level integers different from `U1`. diff --git a/src/core/matrix.rs b/src/core/matrix.rs index 661fb329..6deb899f 100644 --- a/src/core/matrix.rs +++ b/src/core/matrix.rs @@ -7,6 +7,9 @@ use std::any::TypeId; use std::mem; use approx::ApproxEq; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + use alga::general::{Ring, Real}; use core::{Scalar, Unit}; @@ -83,16 +86,42 @@ Matrix<NNew, R, C, <<S as Storage<NOld, R, C>>::Alloc as Allocator<NNew, R, C>>: /// some concrete types for `N` and a compatible data storage type `S`). #[repr(C)] #[derive(Hash, Debug, Clone, Copy)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct Matrix<N: Scalar, R: Dim, C: Dim, S> { /// The data storage that contains all the matrix components and informations about its number /// of rows and column (if needed). pub data: S, - #[cfg_attr(feature = "serde-serialize", serde(skip_serializing, skip_deserializing))] _phantoms: PhantomData<(N, R, C)> } +#[cfg(feature = "serde-serialize")] +impl<N, R, C, S> Serialize for Matrix<N, R, C, S> + where N: Scalar, + R: Dim, + C: Dim, + S: Serialize, +{ + fn serialize<T>(&self, serializer: T) -> Result<T::Ok, T::Error> + where T: Serializer + { + self.data.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de, N, R, C, S> Deserialize<'de> for Matrix<N, R, C, S> + where N: Scalar, + R: Dim, + C: Dim, + S: Deserialize<'de>, +{ + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where D: Deserializer<'de> + { + S::deserialize(deserializer).map(|x| Matrix { data: x, _phantoms: PhantomData }) + } +} + impl<N: Scalar, R: Dim, C: Dim, S> Matrix<N, R, C, S> { /// Creates a new matrix with the given data without statically checking that the matrix /// dimension matches the storage dimension. diff --git a/src/core/unit.rs b/src/core/unit.rs index cc7ea1df..6051165d 100644 --- a/src/core/unit.rs +++ b/src/core/unit.rs @@ -2,6 +2,9 @@ use std::mem; use std::ops::{Neg, Deref}; use approx::ApproxEq; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + use alga::general::SubsetOf; use alga::linear::NormedSpace; @@ -11,11 +14,28 @@ use alga::linear::NormedSpace; /// Use `.as_ref()` or `.unwrap()` to obtain the undelying value by-reference or by-move. #[repr(C)] #[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct Unit<T> { value: T } +#[cfg(feature = "serde-serialize")] +impl<T: Serialize> Serialize for Unit<T> { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where S: Serializer + { + self.value.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de, T: Deserialize<'de>> Deserialize<'de> for Unit<T> { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where D: Deserializer<'de> + { + T::deserialize(deserializer).map(|x| Unit { value: x }) + } +} + impl<T: NormedSpace> Unit<T> { /// Normalize the given value and return it wrapped on a `Unit` structure. #[inline] diff --git a/src/geometry/orthographic.rs b/src/geometry/orthographic.rs index 3936ccef..c907de6a 100644 --- a/src/geometry/orthographic.rs +++ b/src/geometry/orthographic.rs @@ -2,6 +2,9 @@ use quickcheck::{Arbitrary, Gen}; use rand::{Rand, Rng}; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + use alga::general::Real; use core::{Scalar, SquareMatrix, OwnedSquareMatrix, ColumnVector, OwnedColumnVector, MatrixArray}; @@ -14,11 +17,36 @@ use geometry::{PointBase, OwnedPoint}; /// A 3D orthographic projection stored as an homogeneous 4x4 matrix. #[derive(Debug, Clone, Copy)] // FIXME: Hash -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct OrthographicBase<N: Scalar, S: Storage<N, U4, U4>> { matrix: SquareMatrix<N, U4, S> } +#[cfg(feature = "serde-serialize")] +impl<N, S> Serialize for OrthographicBase<N, S> + where N: Scalar, + S: Storage<N, U4, U4>, + SquareMatrix<N, U4, S>: Serialize, +{ + fn serialize<T>(&self, serializer: T) -> Result<T::Ok, T::Error> + where T: Serializer + { + self.matrix.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de, N, S> Deserialize<'de> for OrthographicBase<N, S> + where N: Scalar, + S: Storage<N, U4, U4>, + SquareMatrix<N, U4, S>: Deserialize<'de>, +{ + fn deserialize<T>(deserializer: T) -> Result<Self, T::Error> + where T: Deserializer<'de> + { + SquareMatrix::deserialize(deserializer).map(|x| OrthographicBase { matrix: x }) + } +} + /// A 3D orthographic projection stored as a static homogeneous 4x4 matrix. pub type Orthographic3<N> = OrthographicBase<N, MatrixArray<N, U4, U4>>; diff --git a/src/geometry/perspective.rs b/src/geometry/perspective.rs index 9c518546..ba1eced4 100644 --- a/src/geometry/perspective.rs +++ b/src/geometry/perspective.rs @@ -2,6 +2,9 @@ use quickcheck::{Arbitrary, Gen}; use rand::{Rand, Rng}; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + use alga::general::Real; use core::{Scalar, SquareMatrix, OwnedSquareMatrix, ColumnVector, OwnedColumnVector, MatrixArray}; @@ -14,11 +17,36 @@ use geometry::{PointBase, OwnedPoint}; /// A 3D perspective projection stored as an homogeneous 4x4 matrix. #[derive(Debug, Clone, Copy)] // FIXME: Hash -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct PerspectiveBase<N: Scalar, S: Storage<N, U4, U4>> { matrix: SquareMatrix<N, U4, S> } +#[cfg(feature = "serde-serialize")] +impl<N, S> Serialize for PerspectiveBase<N, S> + where N: Scalar, + S: Storage<N, U4, U4>, + SquareMatrix<N, U4, S>: Serialize, +{ + fn serialize<T>(&self, serializer: T) -> Result<T::Ok, T::Error> + where T: Serializer + { + self.matrix.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de, N, S> Deserialize<'de> for PerspectiveBase<N, S> + where N: Scalar, + S: Storage<N, U4, U4>, + SquareMatrix<N, U4, S>: Deserialize<'de>, +{ + fn deserialize<T>(deserializer: T) -> Result<Self, T::Error> + where T: Deserializer<'de> + { + SquareMatrix::deserialize(deserializer).map(|x| PerspectiveBase { matrix: x }) + } +} + /// A 3D perspective projection stored as a static homogeneous 4x4 matrix. pub type Perspective3<N> = PerspectiveBase<N, MatrixArray<N, U4, U4>>; diff --git a/src/geometry/point.rs b/src/geometry/point.rs index 370fc3db..53f914a2 100644 --- a/src/geometry/point.rs +++ b/src/geometry/point.rs @@ -3,6 +3,9 @@ use std::fmt; use std::cmp::Ordering; use approx::ApproxEq; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + use core::{Scalar, ColumnVector, OwnedColumnVector}; use core::iter::{MatrixIter, MatrixIterMut}; use core::dimension::{DimName, DimNameSum, DimNameAdd, U1}; @@ -24,7 +27,6 @@ pub type OwnedPoint<N, D, A> = PointBase<N, D, <A as Allocator<N, D, U1>>::Buffe /// A point in a n-dimensional euclidean space. #[repr(C)] #[derive(Hash, Debug)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct PointBase<N: Scalar, D: DimName, S: Storage<N, D, U1>> { /// The coordinates of this point, i.e., the shift from the origin. pub coords: ColumnVector<N, D, S> @@ -45,6 +47,34 @@ impl<N, D, S> Clone for PointBase<N, D, S> } } +#[cfg(feature = "serde-serialize")] +impl<N, D, S> Serialize for PointBase<N, D, S> + where N: Scalar, + D: DimName, + S: Storage<N, D, U1>, + ColumnVector<N, D, S>: Serialize, +{ + fn serialize<T>(&self, serializer: T) -> Result<T::Ok, T::Error> + where T: Serializer + { + self.coords.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de, N, D, S> Deserialize<'de> for PointBase<N, D, S> + where N: Scalar, + D: DimName, + S: Storage<N, D, U1>, + ColumnVector<N, D, S>: Deserialize<'de>, +{ + fn deserialize<T>(deserializer: T) -> Result<Self, T::Error> + where T: Deserializer<'de> + { + ColumnVector::deserialize(deserializer).map(|x| PointBase { coords: x }) + } +} + impl<N: Scalar, D: DimName, S: Storage<N, D, U1>> PointBase<N, D, S> { /// Creates a new point with the given coordinates. #[inline] diff --git a/src/geometry/quaternion.rs b/src/geometry/quaternion.rs index 784dc20a..4a99acc3 100644 --- a/src/geometry/quaternion.rs +++ b/src/geometry/quaternion.rs @@ -2,6 +2,9 @@ use std::fmt; use num::Zero; use approx::ApproxEq; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + use alga::general::Real; use core::{Unit, ColumnVector, OwnedColumnVector, MatrixSlice, MatrixSliceMut, SquareMatrix, @@ -22,12 +25,38 @@ pub type OwnedUnitQuaternionBase<N, A> = UnitQuaternionBase<N, <A as Allocator<N /// that may be used as a rotation. #[repr(C)] #[derive(Hash, Debug, Copy, Clone)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct QuaternionBase<N: Real, S: Storage<N, U4, U1>> { /// This quaternion as a 4D vector of coordinates in the `[ x, y, z, w ]` storage order. pub coords: ColumnVector<N, U4, S> } +#[cfg(feature = "serde-serialize")] +impl<N, S> Serialize for QuaternionBase<N, S> + where N: Real, + S: Storage<N, U4, U1>, + ColumnVector<N, U4, S>: Serialize, +{ + fn serialize<T>(&self, serializer: T) -> Result<T::Ok, T::Error> + where T: Serializer + { + self.coords.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de, N, S> Deserialize<'de> for QuaternionBase<N, S> + where N: Real, + S: Storage<N, U4, U1>, + ColumnVector<N, U4, S>: Deserialize<'de>, +{ + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where D: Deserializer<'de> + { + ColumnVector::deserialize(deserializer).map(|x| QuaternionBase { coords: x }) + } +} + + impl<N, S> Eq for QuaternionBase<N, S> where N: Real + Eq, S: Storage<N, U4, U1> { diff --git a/src/geometry/rotation.rs b/src/geometry/rotation.rs index a1063cbb..1429830a 100644 --- a/src/geometry/rotation.rs +++ b/src/geometry/rotation.rs @@ -2,6 +2,9 @@ use num::{Zero, One}; use std::fmt; use approx::ApproxEq; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + use alga::general::Real; use core::{SquareMatrix, Scalar, OwnedSquareMatrix}; @@ -16,11 +19,36 @@ pub type OwnedRotation<N, D, A> = RotationBase<N, D, <A as Allocator<N, D, D>>:: /// A rotation matrix. #[repr(C)] #[derive(Hash, Debug, Clone, Copy)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct RotationBase<N: Scalar, D: DimName, S> { matrix: SquareMatrix<N, D, S> } +#[cfg(feature = "serde-serialize")] +impl<N, D, S> Serialize for RotationBase<N, D, S> + where N: Scalar, + D: DimName, + SquareMatrix<N, D, S>: Serialize, +{ + fn serialize<T>(&self, serializer: T) -> Result<T::Ok, T::Error> + where T: Serializer + { + self.matrix.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de, N, D, S> Deserialize<'de> for RotationBase<N, D, S> + where N: Scalar, + D: DimName, + SquareMatrix<N, D, S>: Deserialize<'de>, +{ + fn deserialize<T>(deserializer: T) -> Result<Self, T::Error> + where T: Deserializer<'de> + { + SquareMatrix::deserialize(deserializer).map(|x| RotationBase { matrix: x }) + } +} + impl<N: Scalar, D: DimName, S: Storage<N, D, D>> RotationBase<N, D, S> where N: Scalar, S: Storage<N, D, D> { diff --git a/src/geometry/transform.rs b/src/geometry/transform.rs index 23563d80..8d311375 100644 --- a/src/geometry/transform.rs +++ b/src/geometry/transform.rs @@ -3,6 +3,9 @@ use std::fmt::Debug; use std::marker::PhantomData; use approx::ApproxEq; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + use alga::general::Field; use core::{Scalar, SquareMatrix, OwnedSquareMatrix}; @@ -53,17 +56,14 @@ where T1: TCategory, /// Tag representing the most general (not necessarily inversible) `Transform` type. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub enum TGeneral { } /// Tag representing the most general inversible `Transform` type. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub enum TProjective { } /// Tag representing an affine `Transform`. Its bottom-row is equal to `(0, 0 ... 0, 1)`. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub enum TAffine { } impl TCategory for TGeneral { @@ -159,14 +159,40 @@ pub type OwnedTransform<N, D, A, C> /// 3D transformation. #[repr(C)] #[derive(Debug, Clone, Copy)] // FIXME: Hash -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct TransformBase<N: Scalar, D: DimNameAdd<U1>, S, C: TCategory> { matrix: SquareMatrix<N, DimNameSum<D, U1>, S>, - #[cfg_attr(feature = "serde-serialize", serde(skip_serializing, skip_deserializing))] _phantom: PhantomData<C> } +#[cfg(feature = "serde-serialize")] +impl<N, D, S, C> Serialize for TransformBase<N, D, S, C> + where N: Scalar, + D: DimNameAdd<U1>, + C: TCategory, + SquareMatrix<N, DimNameSum<D, U1>, S>: Serialize, +{ + fn serialize<T>(&self, serializer: T) -> Result<T::Ok, T::Error> + where T: Serializer + { + self.matrix.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de, N, D, S, C> Deserialize<'de> for TransformBase<N, D, S, C> + where N: Scalar, + D: DimNameAdd<U1>, + C: TCategory, + SquareMatrix<N, DimNameSum<D, U1>, S>: Deserialize<'de>, +{ + fn deserialize<T>(deserializer: T) -> Result<Self, T::Error> + where T: Deserializer<'de> + { + SquareMatrix::deserialize(deserializer).map(|x| TransformBase { matrix: x, _phantom: PhantomData }) + } +} + // XXX: for some reasons, implementing Clone and Copy manually causes an ICEā¦ impl<N, D, S, C: TCategory> Eq for TransformBase<N, D, S, C> diff --git a/src/geometry/translation.rs b/src/geometry/translation.rs index ac7b1fd8..1580bcb7 100644 --- a/src/geometry/translation.rs +++ b/src/geometry/translation.rs @@ -2,6 +2,9 @@ use num::{Zero, One}; use std::fmt; use approx::ApproxEq; +#[cfg(feature = "serde-serialize")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + use alga::general::{Real, ClosedNeg}; use core::{Scalar, ColumnVector, OwnedSquareMatrix}; @@ -15,13 +18,38 @@ pub type OwnedTranslation<N, D, S> = TranslationBase<N, D, Owned<N, D, U1, <S as /// A translation. #[repr(C)] #[derive(Hash, Debug, Clone, Copy)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct TranslationBase<N: Scalar, D: DimName, S/*: Storage<N, D, U1>*/> { /// The translation coordinates, i.e., how much is added to a point's coordinates when it is /// translated. pub vector: ColumnVector<N, D, S> } +#[cfg(feature = "serde-serialize")] +impl<N, D, S> Serialize for TranslationBase<N, D, S> + where N: Scalar, + D: DimName, + ColumnVector<N, D, S>: Serialize, +{ + fn serialize<T>(&self, serializer: T) -> Result<T::Ok, T::Error> + where T: Serializer + { + self.vector.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'de, N, D, S> Deserialize<'de> for TranslationBase<N, D, S> + where N: Scalar, + D: DimName, + ColumnVector<N, D, S>: Deserialize<'de>, +{ + fn deserialize<T>(deserializer: T) -> Result<Self, T::Error> + where T: Deserializer<'de> + { + ColumnVector::deserialize(deserializer).map(|x| TranslationBase { vector: x }) + } +} + impl<N, D: DimName, S> TranslationBase<N, D, S> where N: Scalar, S: Storage<N, D, U1> { diff --git a/tests/serde.rs b/tests/serde.rs index f55512c5..fadcc694 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -13,7 +13,8 @@ use na::{ IsometryMatrix3, Similarity3, SimilarityMatrix3, - Quaternion + Quaternion, + Unit, }; macro_rules! test_serde( @@ -45,3 +46,11 @@ test_serde!( serde_similarity_matrix3, SimilarityMatrix3; serde_quaternion, Quaternion; ); + +#[test] +fn serde_flat() { + // The actual storage is hidden behind three layers of wrapper types that shouldn't appear in serialized form. + let v = Unit::new_normalize(Quaternion::new(0., 0., 0., 1.)); + let serialized = serde_json::to_string(&v).unwrap(); + assert_eq!(serialized, "[0.0,0.0,1.0,0.0]"); +}