diff --git a/.gitignore b/.gitignore index 80faedec..fa8d85ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ Cargo.lock -/target/ +target diff --git a/Cargo.toml b/Cargo.toml index bdbec9f0..31bf1fe7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ license = "MIT / Apache-2.0" unstable = [] [dependencies] +euclid_macros = { path = "./macros", version = "0.1" } num-traits = { version = "0.2" } serde = { version = "1.0", features = ["serde_derive"], optional = true } mint = {version = "0.5.1", optional = true} diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 00000000..3413fc81 --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "euclid_macros" +version = "0.1.0" +authors = ["Emilio Cobos Álvarez ", "The Servo project developers"] + +[lib] +path = "lib.rs" +proc-macro = true + +[dependencies] +proc-macro2 = "0.4" +quote = "0.6" +syn = { version = "0.15", features = ["visit"] } diff --git a/macros/euclid_matrix.rs b/macros/euclid_matrix.rs new file mode 100644 index 00000000..905b7193 --- /dev/null +++ b/macros/euclid_matrix.rs @@ -0,0 +1,251 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use proc_macro2::TokenStream; +use syn::{self, DeriveInput}; + +type Fields = syn::punctuated::Punctuated; + +fn derive_trait( + input: &DeriveInput, + trait_name: TokenStream, + generics: &syn::Generics, + body: F +) -> TokenStream +where + F: FnOnce() -> TokenStream, +{ + let struct_name = &input.ident; + + let (impl_generics, _, where_clause) = generics.split_for_impl(); + let (_, ty_generics, _) = input.generics.split_for_impl(); + + let body = body(); + quote! { + impl #impl_generics #trait_name for #struct_name #ty_generics #where_clause { + #body + } + } +} + +fn derive_simple_trait( + input: &DeriveInput, + trait_name: TokenStream, + t: &syn::TypeParam, + body: F, +) -> TokenStream +where + F: FnOnce() -> TokenStream, +{ + let mut generics = input.generics.clone(); + generics + .make_where_clause() + .predicates + .push(parse_quote!(#t: #trait_name)); + derive_trait(input, trait_name, &generics, body) +} + +fn each_field_except_unit( + fields: &Fields, + unit: &syn::Field, + mut field_expr: F, +) -> TokenStream +where + F: FnMut(&syn::Ident) -> TokenStream, +{ + fields.iter().filter(|f| f.ident != unit.ident).fold(quote! {}, |body, field| { + let name = field.ident.as_ref().unwrap(); + let expr = field_expr(name); + quote! { + #body + #expr + } + }) +} + + +fn derive_struct_body( + fields: &Fields, + unit: &syn::Field, + mut field_expr: F, +) -> TokenStream +where + F: FnMut(&syn::Ident) -> TokenStream, +{ + let body = each_field_except_unit(fields, unit, |name| { + let expr = field_expr(name); + quote! { + #name: #expr, + } + }); + + let unit_name = unit.ident.as_ref().unwrap(); + quote! { + Self { + #body + #unit_name: PhantomData, + } + } +} + +fn clone_impl(input: &DeriveInput, fields: &Fields, unit: &syn::Field, t: &syn::TypeParam) -> TokenStream { + derive_simple_trait(input, quote! { Clone }, t, || { + let body = derive_struct_body(fields, unit, |name| { + quote! { self.#name.clone() } + }); + quote! { + fn clone(&self) -> Self { + #body + } + } + }) +} + +fn copy_impl(input: &DeriveInput, t: &syn::TypeParam) -> TokenStream { + derive_simple_trait(input, quote!{ Copy }, t, || quote! {}) +} + +fn eq_impl(input: &DeriveInput, t: &syn::TypeParam) -> TokenStream { + derive_simple_trait(input, quote!{ ::core::cmp::Eq }, t, || quote! {}) +} + +fn partialeq_impl(input: &DeriveInput, fields: &Fields, unit: &syn::Field, t: &syn::TypeParam) -> TokenStream { + derive_simple_trait(input, quote!{ ::core::cmp::PartialEq }, t, || { + let body = each_field_except_unit(fields, unit, |name| { + quote! { && self.#name == other.#name } + }); + + quote! { + fn eq(&self, other: &Self) -> bool { + true #body + } + } + }) +} + +fn hash_impl(input: &DeriveInput, fields: &Fields, unit: &syn::Field, t: &syn::TypeParam) -> TokenStream { + derive_simple_trait(input, quote!{ ::core::hash::Hash }, t, || { + let body = each_field_except_unit(fields, unit, |name| { + quote! { self.#name.hash(h); } + }); + + quote! { + fn hash(&self, h: &mut H) { + #body + } + } + }) +} + +fn serde_impl( + input: &DeriveInput, + fields: &Fields, + unit: &syn::Field, + t: &syn::TypeParam, +) -> TokenStream { + let deserialize_impl = { + let mut generics = input.generics.clone(); + generics.params.insert(0, parse_quote!('de)); + generics + .make_where_clause() + .predicates + .push(parse_quote!(#t: ::serde::Deserialize<'de>)); + derive_trait(input, quote!{ ::serde::Deserialize<'de> }, &generics, || { + let tuple = each_field_except_unit(fields, unit, |name| { + quote! { #name, } + }); + let body = derive_struct_body(fields, unit, |name| quote! { #name }); + quote! { + fn deserialize(deserializer: D) -> Result + where + D: ::serde::Deserializer<'de>, + { + let (#tuple) = ::serde::Deserialize::deserialize(deserializer)?; + Ok(#body) + } + } + }) + }; + + let serialize_impl = derive_simple_trait(input, quote! { ::serde::Serialize }, t, || { + let tuple = each_field_except_unit(fields, unit, |name| { + quote! { &self.#name, } + }); + quote! { + fn serialize(&self, serializer: S) -> Result + where + S: ::serde::Serializer, + { + (#tuple).serialize(serializer) + } + } + }); + + quote! { + #[cfg(feature = "serde")] + #serialize_impl + #[cfg(feature = "serde")] + #deserialize_impl + } +} + +pub fn derive(input: DeriveInput) -> TokenStream { + let s = match input.data { + syn::Data::Struct(ref s) => s, + _ => panic!("Need to derive this on a struct"), + }; + + let fields = match s.fields { + syn::Fields::Named(ref named) => &named.named, + _ => panic!("Need to use named fields"), + }; + + assert!(!fields.is_empty()); + + let unit_field = fields.last().unwrap(); + assert_eq!( + unit_field.value().ident.as_ref().unwrap().to_string(), + "_unit", + "You need to have a _unit field to derive this trait", + ); + + assert!(match unit_field.value().vis { + syn::Visibility::Public(..) => true, + _ => false, + }, "Unit field should be public"); + + assert!(input.attrs.iter().filter_map(|attr| attr.interpret_meta()).any(|attr| { + match attr { + syn::Meta::Word(..) | + syn::Meta::NameValue(..) => false, + syn::Meta::List(ref list) => { + list.ident == "repr" && list.nested.iter().any(|meta| { + match *meta { + syn::NestedMeta::Meta(syn::Meta::Word(ref w)) => w == "C", + _ => false, + } + }) + } + } + }), "struct should be #[repr(C)]"); + + let type_param = + input.generics.type_params().next().cloned().expect("Need a T"); + + let clone = clone_impl(&input, fields, unit_field.value(), &type_param); + let copy = copy_impl(&input, &type_param); + let serde = serde_impl(&input, fields, unit_field.value(), &type_param); + let eq = eq_impl(&input, &type_param); + let partialeq = partialeq_impl(&input, fields, unit_field.value(), &type_param); + let hash = hash_impl(&input, fields, unit_field.value(), &type_param); + + quote! { + #clone + #copy + #serde + #eq + #partialeq + #hash + } +} diff --git a/macros/lib.rs b/macros/lib.rs new file mode 100644 index 00000000..e62d9676 --- /dev/null +++ b/macros/lib.rs @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +extern crate proc_macro; +extern crate proc_macro2; +#[macro_use] +extern crate quote; +#[macro_use] +extern crate syn; + +use proc_macro::TokenStream; + +mod euclid_matrix; + +#[proc_macro_derive(EuclidMatrix)] +pub fn derive_euclid_matrix(input: TokenStream) -> TokenStream { + let input = syn::parse(input).unwrap(); + euclid_matrix::derive(input).into() +} diff --git a/src/homogen.rs b/src/homogen.rs index b8448a9c..29a50735 100644 --- a/src/homogen.rs +++ b/src/homogen.rs @@ -17,14 +17,16 @@ use core::marker::PhantomData; use core::ops::Div; -define_matrix! { - /// Homogeneous vector in 3D space. - pub struct HomogeneousVector { - pub x: T, - pub y: T, - pub z: T, - pub w: T, - } +/// Homogeneous vector in 3D space. +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct HomogeneousVector { + pub x: T, + pub y: T, + pub z: T, + pub w: T, + #[doc(hidden)] + pub _unit: PhantomData, } diff --git a/src/lib.rs b/src/lib.rs index 2f080ab4..79873fd4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,8 @@ extern crate serde; #[cfg(feature = "mint")] pub extern crate mint; +#[macro_use] +extern crate euclid_macros; extern crate num_traits; #[cfg(test)] extern crate rand; diff --git a/src/macros.rs b/src/macros.rs index cf422a8f..9cc392eb 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -7,84 +7,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -macro_rules! define_matrix { - ( - $(#[$attr:meta])* - pub struct $name:ident { - $(pub $field:ident: T,)+ - } - ) => ( - #[repr(C)] - $(#[$attr])* - pub struct $name { - $(pub $field: T,)+ - - // Keep this (secretly) public for the few cases where we would like to - // create static constants which currently can't be initialized with a - // function. - #[doc(hidden)] - pub _unit: PhantomData<($($phantom),+)> - } - - impl Clone for $name { - fn clone(&self) -> Self { - $name { - $($field: self.$field.clone(),)+ - _unit: PhantomData, - } - } - } - - impl Copy for $name {} - - #[cfg(feature = "serde")] - impl<'de, T, $($phantom),+> ::serde::Deserialize<'de> for $name - where T: ::serde::Deserialize<'de> - { - fn deserialize(deserializer: D) -> Result - where D: ::serde::Deserializer<'de> - { - let ($($field,)+) = - try!(::serde::Deserialize::deserialize(deserializer)); - Ok($name { - $($field: $field,)+ - _unit: PhantomData, - }) - } - } - - #[cfg(feature = "serde")] - impl ::serde::Serialize for $name - where T: ::serde::Serialize - { - fn serialize(&self, serializer: S) -> Result - where S: ::serde::Serializer - { - ($(&self.$field,)+).serialize(serializer) - } - } - - impl ::core::cmp::Eq for $name - where T: ::core::cmp::Eq {} - - impl ::core::cmp::PartialEq for $name - where T: ::core::cmp::PartialEq - { - fn eq(&self, other: &Self) -> bool { - true $(&& self.$field == other.$field)+ - } - } - - impl ::core::hash::Hash for $name - where T: ::core::hash::Hash - { - fn hash(&self, h: &mut H) { - $(self.$field.hash(h);)+ - } - } - ) -} - macro_rules! mint_vec { ($name:ident [ $($field:ident),* ] = $std_name:ident) => { #[cfg(feature = "mint")] diff --git a/src/point.rs b/src/point.rs index 9168e151..b9df69d0 100644 --- a/src/point.rs +++ b/src/point.rs @@ -21,13 +21,16 @@ use core::fmt; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; use core::marker::PhantomData; -define_matrix! { - /// A 2d Point tagged with a unit. - pub struct TypedPoint2D { - pub x: T, - pub y: T, - } +/// A 2d Point tagged with a unit. +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedPoint2D { + pub x: T, + pub y: T, + #[doc(hidden)] + pub _unit: PhantomData, } + mint_vec!(TypedPoint2D[x, y] = Point2); /// Default 2d point type with no unit. @@ -408,14 +411,17 @@ impl From<[T; 2]> for TypedPoint2D { } } -define_matrix! { - /// A 3d Point tagged with a unit. - pub struct TypedPoint3D { - pub x: T, - pub y: T, - pub z: T, - } +/// A 3d Point tagged with a unit. +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedPoint3D { + pub x: T, + pub y: T, + pub z: T, + #[doc(hidden)] + pub _unit: PhantomData, } + mint_vec!(TypedPoint3D[x, y, z] = Point3); /// Default 3d point type with no unit. diff --git a/src/rotation.rs b/src/rotation.rs index 54056865..a76265eb 100644 --- a/src/rotation.rs +++ b/src/rotation.rs @@ -185,11 +185,13 @@ impl> Neg for Angle { } } -define_matrix! { - /// A transform that can represent rotations in 2d, represented as an angle in radians. - pub struct TypedRotation2D { - pub angle : T, - } +/// A transform that can represent rotations in 2d, represented as an angle in radians. +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedRotation2D { + pub angle : T, + #[doc(hidden)] + pub _unit: PhantomData<(Src, Dst)>, } /// The default 2d rotation type with no units. @@ -311,26 +313,28 @@ where } } -define_matrix! { - /// A transform that can represent rotations in 3d, represented as a quaternion. - /// - /// Most methods expect the quaternion to be normalized. - /// When in doubt, use `unit_quaternion` instead of `quaternion` to create - /// a rotation as the former will ensure that its result is normalized. - /// - /// Some people use the `x, y, z, w` (or `w, x, y, z`) notations. The equivalence is - /// as follows: `x -> i`, `y -> j`, `z -> k`, `w -> r`. - /// The memory layout of this type corresponds to the `x, y, z, w` notation - pub struct TypedRotation3D { - // Component multiplied by the imaginary number `i`. - pub i: T, - // Component multiplied by the imaginary number `j`. - pub j: T, - // Component multiplied by the imaginary number `k`. - pub k: T, - // The real part. - pub r: T, - } +/// A transform that can represent rotations in 3d, represented as a quaternion. +/// +/// Most methods expect the quaternion to be normalized. +/// When in doubt, use `unit_quaternion` instead of `quaternion` to create +/// a rotation as the former will ensure that its result is normalized. +/// +/// Some people use the `x, y, z, w` (or `w, x, y, z`) notations. The equivalence is +/// as follows: `x -> i`, `y -> j`, `z -> k`, `w -> r`. +/// The memory layout of this type corresponds to the `x, y, z, w` notation +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedRotation3D { + /// Component multiplied by the imaginary number `i`. + pub i: T, + /// Component multiplied by the imaginary number `j`. + pub j: T, + /// Component multiplied by the imaginary number `k`. + pub k: T, + /// The real part. + pub r: T, + #[doc(hidden)] + pub _unit: PhantomData<(Src, Dst)>, } /// The default 3d rotation type with no units. diff --git a/src/side_offsets.rs b/src/side_offsets.rs index 581aa28c..2d5c9932 100644 --- a/src/side_offsets.rs +++ b/src/side_offsets.rs @@ -19,13 +19,15 @@ use core::marker::PhantomData; /// A group of side offsets, which correspond to top/left/bottom/right for borders, padding, /// and margins in CSS, optionally tagged with a unit. -define_matrix! { - pub struct TypedSideOffsets2D { - pub top: T, - pub right: T, - pub bottom: T, - pub left: T, - } +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedSideOffsets2D { + pub top: T, + pub right: T, + pub bottom: T, + pub left: T, + #[doc(hidden)] + pub _unit: PhantomData, } impl fmt::Debug for TypedSideOffsets2D { diff --git a/src/size.rs b/src/size.rs index 068c4bb3..044ca8ed 100644 --- a/src/size.rs +++ b/src/size.rs @@ -21,11 +21,13 @@ use core::ops::{Add, Div, Mul, Sub}; use core::marker::PhantomData; /// A 2d size tagged with a unit. -define_matrix! { - pub struct TypedSize2D { - pub width: T, - pub height: T, - } +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedSize2D { + pub width: T, + pub height: T, + #[doc(hidden)] + pub _unit: PhantomData, } /// Default 2d size type with no unit. diff --git a/src/transform2d.rs b/src/transform2d.rs index 9ff19bdd..789e8667 100644 --- a/src/transform2d.rs +++ b/src/transform2d.rs @@ -24,23 +24,25 @@ use trig::Trig; use core::fmt; use num_traits::NumCast; -define_matrix! { - /// A 2d transform stored as a 3 by 2 matrix in row-major order in memory. - /// - /// Transforms can be parametrized over the source and destination units, to describe a - /// transformation from a space to another. - /// For example, `TypedTransform2D::transform_point4d` - /// takes a `TypedPoint2D` and returns a `TypedPoint2D`. - /// - /// Transforms expose a set of convenience methods for pre- and post-transformations. - /// A pre-transformation corresponds to adding an operation that is applied before - /// the rest of the transformation, while a post-transformation adds an operation - /// that is applied after. - pub struct TypedTransform2D { - pub m11: T, pub m12: T, - pub m21: T, pub m22: T, - pub m31: T, pub m32: T, - } +/// A 2d transform stored as a 3 by 2 matrix in row-major order in memory. +/// +/// Transforms can be parametrized over the source and destination units, to describe a +/// transformation from a space to another. +/// For example, `TypedTransform2D::transform_point4d` +/// takes a `TypedPoint2D` and returns a `TypedPoint2D`. +/// +/// Transforms expose a set of convenience methods for pre- and post-transformations. +/// A pre-transformation corresponds to adding an operation that is applied before +/// the rest of the transformation, while a post-transformation adds an operation +/// that is applied after. +#[repr(C)] +#[derive(EuclidMatrix)] +pub struct TypedTransform2D { + pub m11: T, pub m12: T, + pub m21: T, pub m22: T, + pub m31: T, pub m32: T, + #[doc(hidden)] + pub _unit: PhantomData<(Src, Dst)>, } /// The default 2d transform type with no units. diff --git a/src/transform3d.rs b/src/transform3d.rs index 224ae5fc..a681732b 100644 --- a/src/transform3d.rs +++ b/src/transform3d.rs @@ -26,24 +26,26 @@ use core::marker::PhantomData; use core::fmt; use num_traits::NumCast; -define_matrix! { - /// A 3d transform stored as a 4 by 4 matrix in row-major order in memory. - /// - /// Transforms can be parametrized over the source and destination units, to describe a - /// transformation from a space to another. - /// For example, `TypedTransform3D::transform_point3d` - /// takes a `TypedPoint3D` and returns a `TypedPoint3D`. - /// - /// Transforms expose a set of convenience methods for pre- and post-transformations. - /// A pre-transformation corresponds to adding an operation that is applied before - /// the rest of the transformation, while a post-transformation adds an operation - /// that is applied after. - pub struct TypedTransform3D { - pub m11: T, pub m12: T, pub m13: T, pub m14: T, - pub m21: T, pub m22: T, pub m23: T, pub m24: T, - pub m31: T, pub m32: T, pub m33: T, pub m34: T, - pub m41: T, pub m42: T, pub m43: T, pub m44: T, - } +/// A 3d transform stored as a 4 by 4 matrix in row-major order in memory. +/// +/// Transforms can be parametrized over the source and destination units, to describe a +/// transformation from a space to another. +/// For example, `TypedTransform3D::transform_point3d` +/// takes a `TypedPoint3D` and returns a `TypedPoint3D`. +/// +/// Transforms expose a set of convenience methods for pre- and post-transformations. +/// A pre-transformation corresponds to adding an operation that is applied before +/// the rest of the transformation, while a post-transformation adds an operation +/// that is applied after. +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedTransform3D { + pub m11: T, pub m12: T, pub m13: T, pub m14: T, + pub m21: T, pub m22: T, pub m23: T, pub m24: T, + pub m31: T, pub m32: T, pub m33: T, pub m34: T, + pub m41: T, pub m42: T, pub m43: T, pub m44: T, + #[doc(hidden)] + pub _unit: PhantomData<(Src, Dst)>, } /// The default 3d transform type with no units. diff --git a/src/translation.rs b/src/translation.rs index 0480882b..cf70c241 100644 --- a/src/translation.rs +++ b/src/translation.rs @@ -15,31 +15,33 @@ use core::ops::{Add, Sub, Neg, Mul, Div}; use core::marker::PhantomData; use core::fmt; -define_matrix! { - /// A 2d transformation from a space to another that can only express translations. - /// - /// The main benefit of this type over a TypedVector2D is the ability to cast - /// between a source and a destination spaces. - /// - /// Example: - /// - /// ``` - /// use euclid::{TypedTranslation2D, TypedPoint2D, point2}; - /// struct ParentSpace; - /// struct ChildSpace; - /// type ScrollOffset = TypedTranslation2D; - /// type ParentPoint = TypedPoint2D; - /// type ChildPoint = TypedPoint2D; - /// - /// let scrolling = ScrollOffset::new(0, 100); - /// let p1: ParentPoint = point2(0, 0); - /// let p2: ChildPoint = scrolling.transform_point(&p1); - /// ``` - /// - pub struct TypedTranslation2D { - pub x: T, - pub y: T, - } +/// A 2d transformation from a space to another that can only express translations. +/// +/// The main benefit of this type over a TypedVector2D is the ability to cast +/// between a source and a destination spaces. +/// +/// Example: +/// +/// ``` +/// use euclid::{TypedTranslation2D, TypedPoint2D, point2}; +/// struct ParentSpace; +/// struct ChildSpace; +/// type ScrollOffset = TypedTranslation2D; +/// type ParentPoint = TypedPoint2D; +/// type ChildPoint = TypedPoint2D; +/// +/// let scrolling = ScrollOffset::new(0, 100); +/// let p1: ParentPoint = point2(0, 0); +/// let p2: ChildPoint = scrolling.transform_point(&p1); +/// ``` +/// +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedTranslation2D { + pub x: T, + pub y: T, + #[doc(hidden)] + pub _unit: PhantomData<(Src, Dst)>, } impl TypedTranslation2D { @@ -228,16 +230,18 @@ where T: Copy + fmt::Debug { -define_matrix! { - /// A 3d transformation from a space to another that can only express translations. - /// - /// The main benefit of this type over a TypedVector3D is the ability to cast - /// between a source and a destination spaces. - pub struct TypedTranslation3D { - pub x: T, - pub y: T, - pub z: T, - } +/// A 3d transformation from a space to another that can only express translations. +/// +/// The main benefit of this type over a TypedVector3D is the ability to cast +/// between a source and a destination spaces. +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedTranslation3D { + pub x: T, + pub y: T, + pub z: T, + #[doc(hidden)] + pub _unit: PhantomData<(Src, Dst)>, } impl TypedTranslation3D { diff --git a/src/vector.rs b/src/vector.rs index 5fe7963c..91700800 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -23,13 +23,16 @@ use core::fmt; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use core::marker::PhantomData; -define_matrix! { - /// A 2d Vector tagged with a unit. - pub struct TypedVector2D { - pub x: T, - pub y: T, - } +/// A 2d Vector tagged with a unit. +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedVector2D { + pub x: T, + pub y: T, + #[doc(hidden)] + pub _unit: PhantomData, } + mint_vec!(TypedVector2D[x, y] = Vector2); /// Default 2d vector type with no unit. @@ -468,14 +471,17 @@ where } } -define_matrix! { - /// A 3d Vector tagged with a unit. - pub struct TypedVector3D { - pub x: T, - pub y: T, - pub z: T, - } +/// A 3d Vector tagged with a unit. +#[derive(EuclidMatrix)] +#[repr(C)] +pub struct TypedVector3D { + pub x: T, + pub y: T, + pub z: T, + #[doc(hidden)] + pub _unit: PhantomData, } + mint_vec!(TypedVector3D[x, y, z] = Vector3); /// Default 3d vector type with no unit.