diff --git a/.github/workflows/nalgebra-ci-build.yml b/.github/workflows/nalgebra-ci-build.yml index d302c908a..fd3ec2734 100644 --- a/.github/workflows/nalgebra-ci-build.yml +++ b/.github/workflows/nalgebra-ci-build.yml @@ -69,6 +69,12 @@ jobs: - name: test nalgebra-sparse (slow tests) # Unfortunately, the "slow-tests" take so much time that we need to run them with --release run: PROPTEST_CASES=10000 cargo test --release --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,slow-tests slow + test-nalgebra-macros: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: test nalgebra-macros + run: cargo test -p nalgebra-macros build-wasm: runs-on: ubuntu-latest # env: diff --git a/CHANGELOG.md b/CHANGELOG.md index 54e7c0fed..2783416b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,38 @@ documented here. This project adheres to [Semantic Versioning](https://semver.org/). +## [0.27.0] +This removes the `convert-glam` and `convert-glam-unchecked` optional features. +Instead, this adds the `convert-glam013`, `convert-glam014`, and `convert-glam015` optional features for +conversions targeting the versions 0.13, 0.14, and 0.15 of `glam`. + +### Added +- Add macros `matrix!`, `dmatrix!`, `vector!`, `dvector!`, `point!` for constructing matrices/vectors/points in a + more convenient way. See [#886](https://github.com/dimforge/nalgebra/pull/886) and [#899](https://github.com/dimforge/nalgebra/pull/899). +- Add `CooMatrix::reserve` to `nalgebra-sparse`. +- Add basic support for serialization using `rkyv`. Can be enabled with the features `rkyv-serialize` or + `rkyv-serialize-no-std`. + + +### Fixed +- Fixed a potential unsoundness issue after deserializing an invalid `DVector` using `serde`. + +## [0.26.2] +### Added +- Conversion from an array `[T; D]` to an isometry `Isometry` (as a translation). +- Conversion from a static vector `SVector` to an isometry `Isometry` (as a translation). +- Conversion from a point `Point` to an isometry `Isometry` (as a translation). +- Conversion of an array `[T; D]` from/to a translation `Translation`. +- Conversion of a point `Point` to a translation `Translation`. +- Conversion of the tuple of glam types `(Vec3, Quat)` from/to an `Isometry2` or `Isometry3`. +- Conversion of a glam type `Vec2/3/4` from/to a `Translation2/3/4`. + +## [0.26.1] +Fix a regression introduced in 0.26.0 preventing `DVector` from being serialized with `serde`. + ## [0.26.0] This releases integrates `min-const-generics` to nalgebra. See -[our blog post](https://dimforge.com/blog/2021/04/12/nalgebra-const-generics) +[our blog post](https://www.dimforge.com/blog/2021/04/12/integrating-const-generics-to-nalgebra) for details about this release. ### Added diff --git a/Cargo.toml b/Cargo.toml index 3bab7b93f..5c2ad286b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra" -version = "0.26.0" +version = "0.27.0" authors = [ "Sébastien Crozet " ] description = "General-purpose linear algebra library with transformations and statically-sized or dynamically-sized matrices." @@ -22,7 +22,7 @@ name = "nalgebra" path = "src/lib.rs" [features] -default = [ "std" ] +default = [ "std", "macros" ] std = [ "matrixmultiply", "simba/std" ] sparse = [ ] debug = [ "approx/num-complex", "rand" ] @@ -32,12 +32,14 @@ compare = [ "matrixcompare-core" ] libm = [ "simba/libm" ] libm-force = [ "simba/libm_force" ] no_unsound_assume_init = [ ] +macros = [ "nalgebra-macros" ] # Conversion convert-mint = [ "mint" ] -convert-glam = [ "glam" ] -convert-glam-unchecked = [ "convert-glam" ] # Enable edgy conversions like Mat4 -> Isometry3 convert-bytemuck = [ "bytemuck" ] +convert-glam013 = [ "glam013" ] +convert-glam014 = [ "glam014" ] +convert-glam015 = [ "glam015" ] # Serialization ## To use serde in a #[no-std] environment, enable the @@ -47,6 +49,8 @@ convert-bytemuck = [ "bytemuck" ] serde-serialize-no-std = [ "serde", "num-complex/serde" ] serde-serialize = [ "serde-serialize-no-std", "serde/std" ] abomonation-serialize = [ "abomonation" ] +rkyv-serialize-no-std = [ "rkyv" ] +rkyv-serialize = [ "rkyv-serialize-no-std", "rkyv/std" ] # Randomness ## To use rand in a #[no-std] environment, enable the @@ -60,39 +64,44 @@ proptest-support = [ "proptest" ] slow-tests = [] [dependencies] +nalgebra-macros = { version = "0.1", path = "nalgebra-macros", optional = true } typenum = "1.12" rand-package = { package = "rand", version = "0.8", optional = true, default-features = false } num-traits = { version = "0.2", default-features = false } -num-complex = { version = "0.3", default-features = false } -num-rational = { version = "0.3", default-features = false } -approx = { version = "0.4", default-features = false } -simba = { version = "0.4", default-features = false } +num-complex = { version = "0.4", default-features = false } +num-rational = { version = "0.4", default-features = false } +approx = { version = "0.5", default-features = false } +simba = { version = "0.5", default-features = false } alga = { version = "0.9", default-features = false, optional = true } rand_distr = { version = "0.4", default-features = false, optional = true } matrixmultiply = { version = "0.3", optional = true } serde = { version = "1.0", default-features = false, features = [ "derive" ], optional = true } abomonation = { version = "0.7", optional = true } +rkyv = { version = "~0.6.4", default-features = false, features = ["const_generics"], optional = true } mint = { version = "0.5", optional = true } -glam = { version = "0.13", optional = true } quickcheck = { version = "1", optional = true } pest = { version = "2", optional = true } pest_derive = { version = "2", optional = true } bytemuck = { version = "1.5", optional = true } matrixcompare-core = { version = "0.1", optional = true } -proptest = { version = "1", optional = true, default-features = false, features = ["std"] } +proptest = { version = "1", optional = true, default-features = false, features = ["std"] } +glam013 = { package = "glam", version = "0.13", optional = true } +glam014 = { package = "glam", version = "0.14", optional = true } +glam015 = { package = "glam", version = "0.15", optional = true } + [dev-dependencies] serde_json = "1.0" rand_xorshift = "0.3" rand_isaac = "0.3" -criterion = "0.2.10" +criterion = { version = "0.3", features = ["html_reports"] } # For matrix comparison macro -matrixcompare = "0.2.0" +matrixcompare = "0.3.0" itertools = "0.10" [workspace] -members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse" ] +members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse", "nalgebra-macros" ] resolver = "2" [[example]] @@ -103,10 +112,11 @@ required-features = ["compare"] name = "nalgebra_bench" harness = false path = "benches/lib.rs" +required-features = ["rand"] [profile.bench] lto = true [package.metadata.docs.rs] # Enable certain features when building docs for docs.rs -features = [ "proptest-support", "compare" ] +features = [ "proptest-support", "compare", "macros" ] diff --git a/benches/core/matrix.rs b/benches/core/matrix.rs index df4a5a5be..3c483c35a 100644 --- a/benches/core/matrix.rs +++ b/benches/core/matrix.rs @@ -188,7 +188,7 @@ fn tr_mul_to(bench: &mut criterion::Criterion) { let b = DVector::::new_random(1000); let mut c = DVector::from_element(1000, 0.0); - bench.bench_function("", move |bh| bh.iter(|| a.tr_mul_to(&b, &mut c))); + bench.bench_function("tr_mul_to", move |bh| bh.iter(|| a.tr_mul_to(&b, &mut c))); } fn mat_mul_mat(bench: &mut criterion::Criterion) { diff --git a/benches/lib.rs b/benches/lib.rs index a77cc743e..1f75ff7e4 100644 --- a/benches/lib.rs +++ b/benches/lib.rs @@ -1,4 +1,4 @@ -#![feature(test)] +#![feature(bench_black_box)] #![allow(unused_macros)] extern crate nalgebra as na; diff --git a/benches/linalg/eigen.rs b/benches/linalg/eigen.rs index 0b1e6b266..ec775d86a 100644 --- a/benches/linalg/eigen.rs +++ b/benches/linalg/eigen.rs @@ -1,4 +1,3 @@ -use test::Bencher; use na::{DMatrix, Eigen}; fn eigen_100x100(bh: &mut criterion::Criterion) { diff --git a/examples/cargo/Cargo.toml b/examples/cargo/Cargo.toml index 10bcf3754..e0dd983cb 100644 --- a/examples/cargo/Cargo.toml +++ b/examples/cargo/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" authors = [ "You" ] [dependencies] -nalgebra = "0.26.0" +nalgebra = "0.27.0" [[bin]] name = "example" diff --git a/nalgebra-glm/Cargo.toml b/nalgebra-glm/Cargo.toml index 0d8b5bd85..f97536d02 100644 --- a/nalgebra-glm/Cargo.toml +++ b/nalgebra-glm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-glm" -version = "0.12.0" +version = "0.13.0" authors = ["sebcrozet "] description = "A computer-graphics oriented API for nalgebra, inspired by the C++ GLM library." @@ -25,6 +25,6 @@ abomonation-serialize = [ "nalgebra/abomonation-serialize" ] [dependencies] num-traits = { version = "0.2", default-features = false } -approx = { version = "0.4", default-features = false } -simba = { version = "0.4", default-features = false } -nalgebra = { path = "..", version = "0.26", default-features = false } +approx = { version = "0.5", default-features = false } +simba = { version = "0.5", default-features = false } +nalgebra = { path = "..", version = "0.27", default-features = false } diff --git a/nalgebra-lapack/Cargo.toml b/nalgebra-lapack/Cargo.toml index 45a469ac1..c368a3c69 100644 --- a/nalgebra-lapack/Cargo.toml +++ b/nalgebra-lapack/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-lapack" -version = "0.17.0" +version = "0.18.0" authors = [ "Sébastien Crozet ", "Andrew Straw " ] description = "Matrix decompositions using nalgebra matrices and Lapack bindings." @@ -29,18 +29,18 @@ accelerate = ["lapack-src/accelerate"] intel-mkl = ["lapack-src/intel-mkl"] [dependencies] -nalgebra = { version = "0.26", path = ".." } +nalgebra = { version = "0.27", path = ".." } num-traits = "0.2" -num-complex = { version = "0.3", default-features = false } -simba = "0.4" +num-complex = { version = "0.4", default-features = false } +simba = "0.5" serde = { version = "1.0", features = [ "derive" ], optional = true } -lapack = { version = "0.17", default-features = false } -lapack-src = { version = "0.6", default-features = false } +lapack = { version = "0.19", default-features = false } +lapack-src = { version = "0.8", default-features = false } # clippy = "*" [dev-dependencies] -nalgebra = { version = "0.26", features = [ "arbitrary", "rand" ], path = ".." } +nalgebra = { version = "0.27", features = [ "arbitrary", "rand" ], path = ".." } proptest = { version = "1", default-features = false, features = ["std"] } quickcheck = "1" -approx = "0.4" +approx = "0.5" rand = "0.8" diff --git a/nalgebra-macros/Cargo.toml b/nalgebra-macros/Cargo.toml new file mode 100644 index 000000000..59eb5ba17 --- /dev/null +++ b/nalgebra-macros/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "nalgebra-macros" +version = "0.1.0" +authors = [ "Andreas Longva", "Sébastien Crozet " ] +edition = "2018" +description = "Procedural macros for nalgebra" +documentation = "https://www.nalgebra.org/docs" +homepage = "https://nalgebra.org" +repository = "https://github.com/dimforge/nalgebra" +readme = "../README.md" +categories = [ "science", "mathematics" ] +keywords = [ "linear", "algebra", "matrix", "vector", "math" ] +license = "Apache-2.0" + +[lib] +proc-macro = true + +[dependencies] +syn = { version="1.0", features = ["full"] } +quote = "1.0" +proc-macro2 = "1.0" + +[dev-dependencies] +nalgebra = { version = "0.27.0", path = ".." } +trybuild = "1.0.42" diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs new file mode 100644 index 000000000..beddfc749 --- /dev/null +++ b/nalgebra-macros/src/lib.rs @@ -0,0 +1,313 @@ +//! Macros for `nalgebra`. +//! +//! This crate is not intended for direct consumption. Instead, the macros are re-exported by +//! `nalgebra` if the `macros` feature is enabled (enabled by default). + +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::{quote, ToTokens, TokenStreamExt}; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::punctuated::Punctuated; +use syn::Expr; +use syn::{parse_macro_input, Token}; + +use proc_macro2::{Delimiter, Spacing, TokenStream as TokenStream2, TokenTree}; +use proc_macro2::{Group, Punct}; + +struct Matrix { + // Represent the matrix as a row-major vector of vectors of expressions + rows: Vec>, + ncols: usize, +} + +impl Matrix { + fn nrows(&self) -> usize { + self.rows.len() + } + + fn ncols(&self) -> usize { + self.ncols + } + + /// Produces a stream of tokens representing this matrix as a column-major nested array. + fn to_col_major_nested_array_tokens(&self) -> TokenStream2 { + let mut result = TokenStream2::new(); + for j in 0..self.ncols() { + let mut col = TokenStream2::new(); + let col_iter = (0..self.nrows()).map(move |i| &self.rows[i][j]); + col.append_separated(col_iter, Punct::new(',', Spacing::Alone)); + result.append(Group::new(Delimiter::Bracket, col)); + result.append(Punct::new(',', Spacing::Alone)); + } + TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, result))) + } + + /// Produces a stream of tokens representing this matrix as a column-major flat array + /// (suitable for representing e.g. a `DMatrix`). + fn to_col_major_flat_array_tokens(&self) -> TokenStream2 { + let mut data = TokenStream2::new(); + for j in 0..self.ncols() { + for i in 0..self.nrows() { + self.rows[i][j].to_tokens(&mut data); + data.append(Punct::new(',', Spacing::Alone)); + } + } + TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, data))) + } +} + +type MatrixRowSyntax = Punctuated; + +impl Parse for Matrix { + fn parse(input: ParseStream) -> Result { + let mut rows = Vec::new(); + let mut ncols = None; + + while !input.is_empty() { + let row_span = input.span(); + let row = MatrixRowSyntax::parse_separated_nonempty(input)?; + + if let Some(ncols) = ncols { + if row.len() != ncols { + let row_idx = rows.len(); + let error_msg = format!( + "Unexpected number of entries in row {}. Expected {}, found {} entries.", + row_idx, + ncols, + row.len() + ); + return Err(Error::new(row_span, error_msg)); + } + } else { + ncols = Some(row.len()); + } + rows.push(row.into_iter().collect()); + + // We've just read a row, so if there are more tokens, there must be a semi-colon, + // otherwise the input is malformed + if !input.is_empty() { + input.parse::()?; + } + } + + Ok(Self { + rows, + ncols: ncols.unwrap_or(0), + }) + } +} + +/// Construct a fixed-size matrix directly from data. +/// +/// **Note: Requires the `macro` feature to be enabled (enabled by default)**. +/// +/// This macro facilitates easy construction of matrices when the entries of the matrix are known +/// (either as constants or expressions). This macro produces an instance of `SMatrix`. This means +/// that the data of the matrix is stored on the stack, and its dimensions are fixed at +/// compile-time. If you want to construct a dynamic matrix, use [`dmatrix!`] instead. +/// +/// `matrix!` is intended to be both the simplest and most efficient way to construct (small) +/// matrices, and can also be used in *const fn* contexts. +/// +/// The syntax is MATLAB-like. Column elements are separated by a comma (`,`), and a semi-colon +/// (`;`) designates that a new row begins. +/// +/// # Examples +/// +/// ``` +/// use nalgebra::matrix; +/// +/// // Produces a Matrix3<_> == SMatrix<_, 3, 3> +/// let a = matrix![1, 2, 3; +/// 4, 5, 6; +/// 7, 8, 9]; +/// ``` +/// +/// You can construct matrices with arbitrary expressions for its elements: +/// +/// ``` +/// use nalgebra::{matrix, Matrix2}; +/// let theta = 0.45f64; +/// +/// let r = matrix![theta.cos(), - theta.sin(); +/// theta.sin(), theta.cos()]; +/// ``` +#[proc_macro] +pub fn matrix(stream: TokenStream) -> TokenStream { + let matrix = parse_macro_input!(stream as Matrix); + + let row_dim = matrix.nrows(); + let col_dim = matrix.ncols(); + + let array_tokens = matrix.to_col_major_nested_array_tokens(); + + // TODO: Use quote_spanned instead?? + let output = quote! { + nalgebra::SMatrix::<_, #row_dim, #col_dim> + ::from_array_storage(nalgebra::ArrayStorage(#array_tokens)) + }; + + proc_macro::TokenStream::from(output) +} + +/// Construct a dynamic matrix directly from data. +/// +/// **Note: Requires the `macro` feature to be enabled (enabled by default)**. +/// +/// The syntax is exactly the same as for [`matrix!`], but instead of producing instances of +/// `SMatrix`, it produces instances of `DMatrix`. At the moment it is not usable +/// in `const fn` contexts. +/// +/// ``` +/// use nalgebra::dmatrix; +/// +/// // Produces a DMatrix<_> +/// let a = dmatrix![1, 2, 3; +/// 4, 5, 6; +/// 7, 8, 9]; +/// ``` +#[proc_macro] +pub fn dmatrix(stream: TokenStream) -> TokenStream { + let matrix = parse_macro_input!(stream as Matrix); + + let row_dim = matrix.nrows(); + let col_dim = matrix.ncols(); + + let array_tokens = matrix.to_col_major_flat_array_tokens(); + + // TODO: Use quote_spanned instead?? + let output = quote! { + nalgebra::DMatrix::<_> + ::from_vec_storage(nalgebra::VecStorage::new( + nalgebra::Dynamic::new(#row_dim), + nalgebra::Dynamic::new(#col_dim), + vec!#array_tokens)) + }; + + proc_macro::TokenStream::from(output) +} + +struct Vector { + elements: Vec, +} + +impl Vector { + fn to_array_tokens(&self) -> TokenStream2 { + let mut data = TokenStream2::new(); + data.append_separated(&self.elements, Punct::new(',', Spacing::Alone)); + TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, data))) + } + + fn len(&self) -> usize { + self.elements.len() + } +} + +impl Parse for Vector { + fn parse(input: ParseStream) -> Result { + // The syntax of a vector is just the syntax of a single matrix row + if input.is_empty() { + Ok(Self { + elements: Vec::new(), + }) + } else { + let elements = MatrixRowSyntax::parse_separated_nonempty(input)? + .into_iter() + .collect(); + Ok(Self { elements }) + } + } +} + +/// Construct a fixed-size column vector directly from data. +/// +/// **Note: Requires the `macro` feature to be enabled (enabled by default)**. +/// +/// Similarly to [`matrix!`], this macro facilitates easy construction of fixed-size vectors. +/// However, whereas the [`matrix!`] macro expects each row to be separated by a semi-colon, +/// the syntax of this macro is instead similar to `vec!`, in that the elements of the vector +/// are simply listed consecutively. +/// +/// `vector!` is intended to be the most readable and performant way of constructing small, +/// fixed-size vectors, and it is usable in `const fn` contexts. +/// +/// ## Examples +/// +/// ``` +/// use nalgebra::vector; +/// +/// // Produces a Vector3<_> == SVector<_, 3> +/// let v = vector![1, 2, 3]; +/// ``` +#[proc_macro] +pub fn vector(stream: TokenStream) -> TokenStream { + let vector = parse_macro_input!(stream as Vector); + let len = vector.len(); + let array_tokens = vector.to_array_tokens(); + let output = quote! { + nalgebra::SVector::<_, #len> + ::from_array_storage(nalgebra::ArrayStorage([#array_tokens])) + }; + proc_macro::TokenStream::from(output) +} + +/// Construct a dynamic column vector directly from data. +/// +/// **Note: Requires the `macro` feature to be enabled (enabled by default)**. +/// +/// The syntax is exactly the same as for [`vector!`], but instead of producing instances of +/// `SVector`, it produces instances of `DVector`. At the moment it is not usable +/// in `const fn` contexts. +/// +/// ``` +/// use nalgebra::dvector; +/// +/// // Produces a DVector<_> +/// let v = dvector![1, 2, 3]; +/// ``` +#[proc_macro] +pub fn dvector(stream: TokenStream) -> TokenStream { + let vector = parse_macro_input!(stream as Vector); + let len = vector.len(); + let array_tokens = vector.to_array_tokens(); + let output = quote! { + nalgebra::DVector::<_> + ::from_vec_storage(nalgebra::VecStorage::new( + nalgebra::Dynamic::new(#len), + nalgebra::Const::<1>, + vec!#array_tokens)) + }; + proc_macro::TokenStream::from(output) +} + +/// Construct a fixed-size point directly from data. +/// +/// **Note: Requires the `macro` feature to be enabled (enabled by default)**. +/// +/// Similarly to [`vector!`], this macro facilitates easy construction of points. +/// +/// `point!` is intended to be the most readable and performant way of constructing small, +/// points, and it is usable in `const fn` contexts. +/// +/// ## Examples +/// +/// ``` +/// use nalgebra::point; +/// +/// // Produces a Point3<_> +/// let v = point![1, 2, 3]; +/// ``` +#[proc_macro] +pub fn point(stream: TokenStream) -> TokenStream { + let vector = parse_macro_input!(stream as Vector); + let len = vector.len(); + let array_tokens = vector.to_array_tokens(); + let output = quote! { + nalgebra::Point::<_, #len> { + coords: nalgebra::SVector::<_, #len> + ::from_array_storage(nalgebra::ArrayStorage([#array_tokens])) + } + }; + proc_macro::TokenStream::from(output) +} diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs new file mode 100644 index 000000000..0e52da1fa --- /dev/null +++ b/nalgebra-macros/tests/tests.rs @@ -0,0 +1,307 @@ +use nalgebra::{ + DMatrix, DVector, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2, Matrix2x1, Matrix2x3, Matrix2x4, + Matrix3, Matrix3x1, Matrix3x2, Matrix3x4, Matrix4, Matrix4x1, Matrix4x2, Matrix4x3, Point, + Point1, Point2, Point3, Point4, Point5, Point6, SMatrix, SVector, Vector1, Vector2, Vector3, + Vector4, Vector5, Vector6, +}; +use nalgebra_macros::{dmatrix, dvector, matrix, point, vector}; + +fn check_statically_same_type(_: &T, _: &T) {} + +/// Wrapper for `assert_eq` that also asserts that the types are the same +macro_rules! assert_eq_and_type { + ($left:expr, $right:expr $(,)?) => { + check_statically_same_type(&$left, &$right); + assert_eq!($left, $right); + }; +} + +// Skip rustfmt because it just makes the test bloated without making it more readable +#[rustfmt::skip] +#[test] +fn matrix_small_dims_exhaustive() { + // 0x0 + assert_eq_and_type!(matrix![], SMatrix::::zeros()); + + // 1xN + assert_eq_and_type!(matrix![1], SMatrix::::new(1)); + assert_eq_and_type!(matrix![1, 2], Matrix1x2::new(1, 2)); + assert_eq_and_type!(matrix![1, 2, 3], Matrix1x3::new(1, 2, 3)); + assert_eq_and_type!(matrix![1, 2, 3, 4], Matrix1x4::new(1, 2, 3, 4)); + + // 2xN + assert_eq_and_type!(matrix![1; 2], Matrix2x1::new(1, 2)); + assert_eq_and_type!(matrix![1, 2; 3, 4], Matrix2::new(1, 2, 3, 4)); + assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6], Matrix2x3::new(1, 2, 3, 4, 5, 6)); + assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8], Matrix2x4::new(1, 2, 3, 4, 5, 6, 7, 8)); + + // 3xN + assert_eq_and_type!(matrix![1; 2; 3], Matrix3x1::new(1, 2, 3)); + assert_eq_and_type!(matrix![1, 2; 3, 4; 5, 6], Matrix3x2::new(1, 2, 3, 4, 5, 6)); + assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9], Matrix3::new(1, 2, 3, 4, 5, 6, 7, 8, 9)); + assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12], + Matrix3x4::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)); + + // 4xN + assert_eq_and_type!(matrix![1; 2; 3; 4], Matrix4x1::new(1, 2, 3, 4)); + assert_eq_and_type!(matrix![1, 2; 3, 4; 5, 6; 7, 8], Matrix4x2::new(1, 2, 3, 4, 5, 6, 7, 8)); + assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12], + Matrix4x3::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)); + assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16], + Matrix4::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)); +} + +#[test] +fn matrix_const_fn() { + // Ensure that matrix! can be used in const contexts + const _: SMatrix = matrix![]; + const _: SMatrix = matrix![1, 2]; + const _: SMatrix = matrix![1, 2, 3; 4, 5, 6]; +} + +// Skip rustfmt because it just makes the test bloated without making it more readable +#[rustfmt::skip] +#[test] +fn dmatrix_small_dims_exhaustive() { + // 0x0 + assert_eq_and_type!(dmatrix![], DMatrix::::zeros(0, 0)); + + // 1xN + assert_eq_and_type!(dmatrix![1], DMatrix::from_row_slice(1, 1, &[1])); + assert_eq_and_type!(dmatrix![1, 2], DMatrix::from_row_slice(1, 2, &[1, 2])); + assert_eq_and_type!(dmatrix![1, 2, 3], DMatrix::from_row_slice(1, 3, &[1, 2, 3])); + assert_eq_and_type!(dmatrix![1, 2, 3, 4], DMatrix::from_row_slice(1, 4, &[1, 2, 3, 4])); + + // 2xN + assert_eq_and_type!(dmatrix![1; 2], DMatrix::from_row_slice(2, 1, &[1, 2])); + assert_eq_and_type!(dmatrix![1, 2; 3, 4], DMatrix::from_row_slice(2, 2, &[1, 2, 3, 4])); + assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6], DMatrix::from_row_slice(2, 3, &[1, 2, 3, 4, 5, 6])); + assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8], DMatrix::from_row_slice(2, 4, &[1, 2, 3, 4, 5, 6, 7, 8])); + + // 3xN + assert_eq_and_type!(dmatrix![1; 2; 3], DMatrix::from_row_slice(3, 1, &[1, 2, 3])); + assert_eq_and_type!(dmatrix![1, 2; 3, 4; 5, 6], DMatrix::from_row_slice(3, 2, &[1, 2, 3, 4, 5, 6])); + assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9], DMatrix::from_row_slice(3, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9])); + assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12], + DMatrix::from_row_slice(3, 4, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])); + + // 4xN + assert_eq_and_type!(dmatrix![1; 2; 3; 4], DMatrix::from_row_slice(4, 1, &[1, 2, 3, 4])); + assert_eq_and_type!(dmatrix![1, 2; 3, 4; 5, 6; 7, 8], DMatrix::from_row_slice(4, 2, &[1, 2, 3, 4, 5, 6, 7, 8])); + assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12], + DMatrix::from_row_slice(4, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])); + assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16], + DMatrix::from_row_slice(4, 4, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])); +} + +// Skip rustfmt because it just makes the test bloated without making it more readable +#[rustfmt::skip] +#[test] +fn vector_small_dims_exhaustive() { + assert_eq_and_type!(vector![], SVector::::zeros()); + assert_eq_and_type!(vector![1], Vector1::::new(1)); + assert_eq_and_type!(vector![1, 2], Vector2::new(1, 2)); + assert_eq_and_type!(vector![1, 2, 3], Vector3::new(1, 2, 3)); + assert_eq_and_type!(vector![1, 2, 3, 4], Vector4::new(1, 2, 3, 4)); + assert_eq_and_type!(vector![1, 2, 3, 4, 5], Vector5::new(1, 2, 3, 4, 5)); + assert_eq_and_type!(vector![1, 2, 3, 4, 5, 6], Vector6::new(1, 2, 3, 4, 5, 6)); +} + +// Skip rustfmt because it just makes the test bloated without making it more readable +#[rustfmt::skip] +#[test] +fn point_small_dims_exhaustive() { + assert_eq_and_type!(point![], Point::::origin()); + assert_eq_and_type!(point![1], Point1::::new(1)); + assert_eq_and_type!(point![1, 2], Point2::new(1, 2)); + assert_eq_and_type!(point![1, 2, 3], Point3::new(1, 2, 3)); + assert_eq_and_type!(point![1, 2, 3, 4], Point4::new(1, 2, 3, 4)); + assert_eq_and_type!(point![1, 2, 3, 4, 5], Point5::new(1, 2, 3, 4, 5)); + assert_eq_and_type!(point![1, 2, 3, 4, 5, 6], Point6::new(1, 2, 3, 4, 5, 6)); +} + +#[test] +fn vector_const_fn() { + // Ensure that vector! can be used in const contexts + const _: SVector = vector![]; + const _: Vector1 = vector![1]; + const _: Vector2 = vector![1, 2]; + const _: Vector6 = vector![1, 2, 3, 4, 5, 6]; +} + +#[test] +fn point_const_fn() { + // Ensure that vector! can be used in const contexts + const _: Point = point![]; + const _: Point1 = point![1]; + const _: Point2 = point![1, 2]; + const _: Point6 = point![1, 2, 3, 4, 5, 6]; +} + +// Skip rustfmt because it just makes the test bloated without making it more readable +#[rustfmt::skip] +#[test] +fn dvector_small_dims_exhaustive() { + assert_eq_and_type!(dvector![], DVector::::zeros(0)); + assert_eq_and_type!(dvector![1], DVector::from_column_slice(&[1])); + assert_eq_and_type!(dvector![1, 2], DVector::from_column_slice(&[1, 2])); + assert_eq_and_type!(dvector![1, 2, 3], DVector::from_column_slice(&[1, 2, 3])); + assert_eq_and_type!(dvector![1, 2, 3, 4], DVector::from_column_slice(&[1, 2, 3, 4])); + assert_eq_and_type!(dvector![1, 2, 3, 4, 5], DVector::from_column_slice(&[1, 2, 3, 4, 5])); + assert_eq_and_type!(dvector![1, 2, 3, 4, 5, 6], DVector::from_column_slice(&[1, 2, 3, 4, 5, 6])); +} + +#[test] +fn matrix_trybuild_tests() { + let t = trybuild::TestCases::new(); + + // Verify error message when we give a matrix with mismatched dimensions + t.compile_fail("tests/trybuild/matrix_mismatched_dimensions.rs"); +} + +#[test] +fn dmatrix_trybuild_tests() { + let t = trybuild::TestCases::new(); + + // Verify error message when we give a matrix with mismatched dimensions + t.compile_fail("tests/trybuild/dmatrix_mismatched_dimensions.rs"); +} + +#[test] +fn matrix_builtin_types() { + // Check that matrix! compiles for all built-in types + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0.0, 1.0; 2.0, 3.0]; + const _: SMatrix = matrix![0.0, 1.0; 2.0, 3.0]; +} + +#[test] +fn vector_builtin_types() { + // Check that vector! compiles for all built-in types + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0.0, 1.0, 2.0, 3.0]; + const _: SVector = vector![0.0, 1.0, 2.0, 3.0]; +} + +#[test] +fn dmatrix_builtin_types() { + // Check that dmatrix! compiles for all built-in types + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0.0, 1.0; 2.0, 3.0]; + let _: DMatrix = dmatrix![0.0, 1.0; 2.0, 3.0]; +} + +#[test] +fn point_builtin_types() { + // Check that point! compiles for all built-in types + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0, 1, 2, 3]; + const _: Point = point![0.0, 1.0, 2.0, 3.0]; + const _: Point = point![0.0, 1.0, 2.0, 3.0]; +} + +#[test] +fn dvector_builtin_types() { + // Check that dvector! compiles for all built-in types + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0.0, 1.0, 2.0, 3.0]; + let _: DVector = dvector![0.0, 1.0, 2.0, 3.0]; +} + +/// Black box function that's just used for testing macros with function call expressions. +fn f(x: T) -> T { + x +} + +#[rustfmt::skip] +#[test] +fn matrix_arbitrary_expressions() { + // Test that matrix! supports arbitrary expressions for its elements + let a = matrix![1 + 2 , 2 * 3; + 4 * f(5 + 6), 7 - 8 * 9]; + let a_expected = Matrix2::new(1 + 2 , 2 * 3, + 4 * f(5 + 6), 7 - 8 * 9); + assert_eq_and_type!(a, a_expected); +} + +#[rustfmt::skip] +#[test] +fn dmatrix_arbitrary_expressions() { + // Test that dmatrix! supports arbitrary expressions for its elements + let a = dmatrix![1 + 2 , 2 * 3; + 4 * f(5 + 6), 7 - 8 * 9]; + let a_expected = DMatrix::from_row_slice(2, 2, &[1 + 2 , 2 * 3, + 4 * f(5 + 6), 7 - 8 * 9]); + assert_eq_and_type!(a, a_expected); +} + +#[rustfmt::skip] +#[test] +fn vector_arbitrary_expressions() { + // Test that vector! supports arbitrary expressions for its elements + let a = vector![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]; + let a_expected = Vector4::new(1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9); + assert_eq_and_type!(a, a_expected); +} + +#[rustfmt::skip] +#[test] +fn point_arbitrary_expressions() { + // Test that point! supports arbitrary expressions for its elements + let a = point![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]; + let a_expected = Point4::new(1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9); + assert_eq_and_type!(a, a_expected); +} + +#[rustfmt::skip] +#[test] +fn dvector_arbitrary_expressions() { + // Test that dvector! supports arbitrary expressions for its elements + let a = dvector![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]; + let a_expected = DVector::from_column_slice(&[1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]); + assert_eq_and_type!(a, a_expected); +} diff --git a/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.rs b/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.rs new file mode 100644 index 000000000..786b58498 --- /dev/null +++ b/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.rs @@ -0,0 +1,6 @@ +use nalgebra_macros::dmatrix; + +fn main() { + dmatrix![1, 2, 3; + 4, 5]; +} \ No newline at end of file diff --git a/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.stderr b/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.stderr new file mode 100644 index 000000000..eaedc650e --- /dev/null +++ b/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.stderr @@ -0,0 +1,5 @@ +error: Unexpected number of entries in row 1. Expected 3, found 2 entries. + --> $DIR/dmatrix_mismatched_dimensions.rs:5:13 + | +5 | 4, 5]; + | ^ diff --git a/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.rs b/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.rs new file mode 100644 index 000000000..c5eb87b7a --- /dev/null +++ b/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.rs @@ -0,0 +1,6 @@ +use nalgebra_macros::matrix; + +fn main() { + matrix![1, 2, 3; + 4, 5]; +} \ No newline at end of file diff --git a/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.stderr b/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.stderr new file mode 100644 index 000000000..c83e8d0cb --- /dev/null +++ b/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.stderr @@ -0,0 +1,5 @@ +error: Unexpected number of entries in row 1. Expected 3, found 2 entries. + --> $DIR/matrix_mismatched_dimensions.rs:5:13 + | +5 | 4, 5]; + | ^ diff --git a/nalgebra-sparse/Cargo.toml b/nalgebra-sparse/Cargo.toml index 36dc8f30a..4af053c22 100644 --- a/nalgebra-sparse/Cargo.toml +++ b/nalgebra-sparse/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-sparse" -version = "0.2.0" +version = "0.3.0" authors = [ "Andreas Longva", "Sébastien Crozet " ] edition = "2018" description = "Sparse matrix computation based on nalgebra." @@ -20,15 +20,15 @@ compare = [ "matrixcompare-core" ] slow-tests = [] [dependencies] -nalgebra = { version="0.26", path = "../" } +nalgebra = { version="0.27", path = "../" } num-traits = { version = "0.2", default-features = false } proptest = { version = "1.0", optional = true } matrixcompare-core = { version = "0.1.0", optional = true } [dev-dependencies] itertools = "0.10" -matrixcompare = { version = "0.2.0", features = [ "proptest-support" ] } -nalgebra = { version="0.26", path = "../", features = ["compare"] } +matrixcompare = { version = "0.3.0", features = [ "proptest-support" ] } +nalgebra = { version="0.27", path = "../", features = ["compare"] } [package.metadata.docs.rs] # Enable certain features when building docs for docs.rs diff --git a/nalgebra-sparse/src/coo.rs b/nalgebra-sparse/src/coo.rs index 50c686773..364083a9f 100644 --- a/nalgebra-sparse/src/coo.rs +++ b/nalgebra-sparse/src/coo.rs @@ -130,6 +130,30 @@ impl CooMatrix { .map(|((i, j), v)| (*i, *j, v)) } + /// Reserves capacity for COO matrix by at least `additional` elements. + /// + /// This increase the capacities of triplet holding arrays by reserving more space to avoid + /// frequent reallocations in `push` operations. + /// + /// ## Panics + /// + /// Panics if any of the individual allocation of triplet arrays fails. + /// + /// ## Example + /// + /// ``` + /// # use nalgebra_sparse::coo::CooMatrix; + /// let mut coo = CooMatrix::new(4, 4); + /// // Reserve capacity in advance + /// coo.reserve(10); + /// coo.push(1, 0, 3.0); + /// ``` + pub fn reserve(&mut self, additional: usize) { + self.row_indices.reserve(additional); + self.col_indices.reserve(additional); + self.values.reserve(additional); + } + /// Push a single triplet to the matrix. /// /// This adds the value `v` to the `i`th row and `j`th column in the matrix. diff --git a/nalgebra-sparse/src/csc.rs b/nalgebra-sparse/src/csc.rs index 3a4d3f6f9..6a88c1d58 100644 --- a/nalgebra-sparse/src/csc.rs +++ b/nalgebra-sparse/src/csc.rs @@ -440,12 +440,12 @@ impl CscMatrix { .expect("Out of bounds matrix indices encountered") } - /// Returns a triplet of slices `(row_offsets, col_indices, values)` that make up the CSC data. + /// Returns a triplet of slices `(col_offsets, row_indices, values)` that make up the CSC data. pub fn csc_data(&self) -> (&[usize], &[usize], &[T]) { self.cs.cs_data() } - /// Returns a triplet of slices `(row_offsets, col_indices, values)` that make up the CSC data, + /// Returns a triplet of slices `(col_offsets, row_indices, values)` that make up the CSC data, /// where the `values` array is mutable. pub fn csc_data_mut(&mut self) -> (&[usize], &[usize], &mut [T]) { self.cs.cs_data_mut() diff --git a/src/base/array_storage.rs b/src/base/array_storage.rs index 68da4c517..6d681ed57 100644 --- a/src/base/array_storage.rs +++ b/src/base/array_storage.rs @@ -299,3 +299,45 @@ where self.as_slice().iter().fold(0, |acc, e| acc + e.extent()) } } + +#[cfg(feature = "rkyv-serialize-no-std")] +mod rkyv_impl { + use super::ArrayStorage; + use rkyv::{offset_of, project_struct, Archive, Deserialize, Fallible, Serialize}; + + impl Archive for ArrayStorage { + type Archived = ArrayStorage; + type Resolver = <[[T; R]; C] as Archive>::Resolver; + + fn resolve( + &self, + pos: usize, + resolver: Self::Resolver, + out: &mut core::mem::MaybeUninit, + ) { + self.0.resolve( + pos + offset_of!(Self::Archived, 0), + resolver, + project_struct!(out: Self::Archived => 0), + ); + } + } + + impl, S: Fallible + ?Sized, const R: usize, const C: usize> Serialize + for ArrayStorage + { + fn serialize(&self, serializer: &mut S) -> Result { + Ok(self.0.serialize(serializer)?) + } + } + + impl + Deserialize, D> for ArrayStorage + where + T::Archived: Deserialize, + { + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(ArrayStorage(self.0.deserialize(deserializer)?)) + } + } +} diff --git a/src/base/componentwise.rs b/src/base/componentwise.rs index b7030f612..2ba49b2ab 100644 --- a/src/base/componentwise.rs +++ b/src/base/componentwise.rs @@ -240,6 +240,16 @@ impl> Matrix ); /// Computes the infimum (aka. componentwise min) of two matrices/vectors. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2; + /// let u = Matrix2::new(4.0, 2.0, 1.0, -2.0); + /// let v = Matrix2::new(2.0, 4.0, -2.0, 1.0); + /// let expected = Matrix2::new(2.0, 2.0, -2.0, -2.0); + /// assert_eq!(u.inf(&v), expected) + /// ``` #[inline] pub fn inf(&self, other: &Self) -> OMatrix where @@ -250,6 +260,16 @@ impl> Matrix } /// Computes the supremum (aka. componentwise max) of two matrices/vectors. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2; + /// let u = Matrix2::new(4.0, 2.0, 1.0, -2.0); + /// let v = Matrix2::new(2.0, 4.0, -2.0, 1.0); + /// let expected = Matrix2::new(4.0, 4.0, 1.0, 1.0); + /// assert_eq!(u.sup(&v), expected) + /// ``` #[inline] pub fn sup(&self, other: &Self) -> OMatrix where @@ -260,6 +280,16 @@ impl> Matrix } /// Computes the (infimum, supremum) of two matrices/vectors. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2; + /// let u = Matrix2::new(4.0, 2.0, 1.0, -2.0); + /// let v = Matrix2::new(2.0, 4.0, -2.0, 1.0); + /// let expected = (Matrix2::new(2.0, 2.0, -2.0, -2.0), Matrix2::new(4.0, 4.0, 1.0, 1.0)); + /// assert_eq!(u.inf_sup(&v), expected) + /// ``` #[inline] pub fn inf_sup(&self, other: &Self) -> (OMatrix, OMatrix) where @@ -271,6 +301,16 @@ impl> Matrix } /// Adds a scalar to `self`. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2; + /// let u = Matrix2::new(1.0, 2.0, 3.0, 4.0); + /// let s = 10.0; + /// let expected = Matrix2::new(11.0, 12.0, 13.0, 14.0); + /// assert_eq!(u.add_scalar(s), expected) + /// ``` #[inline] #[must_use = "Did you mean to use add_scalar_mut()?"] pub fn add_scalar(&self, rhs: T) -> OMatrix @@ -284,6 +324,17 @@ impl> Matrix } /// Adds a scalar to `self` in-place. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2; + /// let mut u = Matrix2::new(1.0, 2.0, 3.0, 4.0); + /// let s = 10.0; + /// u.add_scalar_mut(s); + /// let expected = Matrix2::new(11.0, 12.0, 13.0, 14.0); + /// assert_eq!(u, expected) + /// ``` #[inline] pub fn add_scalar_mut(&mut self, rhs: T) where diff --git a/src/base/conversion.rs b/src/base/conversion.rs index 24a088a29..532ab8ed6 100644 --- a/src/base/conversion.rs +++ b/src/base/conversion.rs @@ -3,7 +3,6 @@ use alloc::vec::Vec; use simba::scalar::{SubsetOf, SupersetOf}; use std::convert::{AsMut, AsRef, From, Into}; use std::mem; -use std::ptr; use simba::simd::{PrimitiveSimdValue, SimdValue}; @@ -24,8 +23,9 @@ use crate::base::{ use crate::base::{DVector, VecStorage}; use crate::base::{SliceStorage, SliceStorageMut}; use crate::constraint::DimEq; +use crate::{IsNotStaticOne, RowSVector, SMatrix, SVector}; -// TODO: too bad this won't work allo slice conversions. +// TODO: too bad this won't work for slice conversions. impl SubsetOf> for OMatrix where R1: Dim, @@ -103,35 +103,43 @@ impl<'a, T: Scalar, R: Dim, C: Dim, S: StorageMut> IntoIterator } } -macro_rules! impl_from_into_asref_1D( - ($(($NRows: ident, $NCols: ident) => $SZ: expr);* $(;)*) => {$( - impl From<[T; $SZ]> for OMatrix - where T: Scalar, - DefaultAllocator: Allocator { - #[inline] - fn from(arr: [T; $SZ]) -> Self { - unsafe { - let mut res = Self::new_uninitialized(); - ptr::copy_nonoverlapping(&arr[0], (*res.as_mut_ptr()).data.ptr_mut(), $SZ); - - res.assume_init() - } - } - } +impl From<[T; D]> for SVector { + #[inline] + fn from(arr: [T; D]) -> Self { + unsafe { Self::from_data_statically_unchecked(ArrayStorage([arr; 1])) } + } +} - impl Into<[T; $SZ]> for Matrix - where T: Scalar, - S: ContiguousStorage { - #[inline] - fn into(self) -> [T; $SZ] { - let mut res = mem::MaybeUninit::<[T; $SZ]>::uninit(); +impl Into<[T; D]> for SVector { + #[inline] + fn into(self) -> [T; D] { + // TODO: unfortunately, we must clone because we can move out of an array. + self.data.0[0].clone() + } +} - unsafe { ptr::copy_nonoverlapping(self.data.ptr(), res.as_mut_ptr() as *mut T, $SZ) }; +impl From<[T; D]> for RowSVector +where + Const: IsNotStaticOne, +{ + #[inline] + fn from(arr: [T; D]) -> Self { + SVector::::from(arr).transpose() + } +} - unsafe { res.assume_init() } - } - } +impl Into<[T; D]> for RowSVector +where + Const: IsNotStaticOne, +{ + #[inline] + fn into(self) -> [T; D] { + self.transpose().into() + } +} +macro_rules! impl_from_into_asref_1D( + ($(($NRows: ident, $NCols: ident) => $SZ: expr);* $(;)*) => {$( impl AsRef<[T; $SZ]> for Matrix where T: Scalar, S: ContiguousStorage { @@ -171,33 +179,22 @@ impl_from_into_asref_1D!( (U13, U1) => 13; (U14, U1) => 14; (U15, U1) => 15; (U16, U1) => 16; ); -macro_rules! impl_from_into_asref_2D( - ($(($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr));* $(;)*) => {$( - impl From<[[T; $SZRows]; $SZCols]> for OMatrix - where DefaultAllocator: Allocator { - #[inline] - fn from(arr: [[T; $SZRows]; $SZCols]) -> Self { - unsafe { - let mut res = Self::new_uninitialized(); - ptr::copy_nonoverlapping(&arr[0][0], (*res.as_mut_ptr()).data.ptr_mut(), $SZRows * $SZCols); - - res.assume_init() - } - } - } - - impl Into<[[T; $SZRows]; $SZCols]> for Matrix - where S: ContiguousStorage { - #[inline] - fn into(self) -> [[T; $SZRows]; $SZCols] { - let mut res = mem::MaybeUninit::<[[T; $SZRows]; $SZCols]>::uninit(); - - unsafe { ptr::copy_nonoverlapping(self.data.ptr(), res.as_mut_ptr() as *mut T, $SZRows * $SZCols) }; +impl From<[[T; R]; C]> for SMatrix { + #[inline] + fn from(arr: [[T; R]; C]) -> Self { + unsafe { Self::from_data_statically_unchecked(ArrayStorage(arr)) } + } +} - unsafe { res.assume_init() } - } - } +impl Into<[[T; R]; C]> for SMatrix { + #[inline] + fn into(self) -> [[T; R]; C] { + self.data.0 + } +} +macro_rules! impl_from_into_asref_2D( + ($(($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr));* $(;)*) => {$( impl AsRef<[[T; $SZRows]; $SZCols]> for Matrix where S: ContiguousStorage { #[inline] diff --git a/src/base/dimension.rs b/src/base/dimension.rs index ec70dc4c3..2b5ccec3f 100644 --- a/src/base/dimension.rs +++ b/src/base/dimension.rs @@ -211,6 +211,57 @@ pub trait DimName: Dim { fn dim() -> usize; } +#[cfg(feature = "serde-serialize-no-std")] +impl Serialize for Const { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + ().serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize-no-std")] +impl<'de, const D: usize> Deserialize<'de> for Const { + fn deserialize(deserializer: Des) -> Result + where + Des: Deserializer<'de>, + { + <()>::deserialize(deserializer).map(|_| Const::) + } +} + +#[cfg(feature = "rkyv-serialize-no-std")] +mod rkyv_impl { + use super::Const; + use rkyv::{Archive, Deserialize, Fallible, Serialize}; + + impl Archive for Const { + type Archived = Self; + type Resolver = (); + + fn resolve( + &self, + _: usize, + _: Self::Resolver, + _: &mut core::mem::MaybeUninit, + ) { + } + } + + impl Serialize for Const { + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } + } + + impl Deserialize for Const { + fn deserialize(&self, _: &mut D) -> Result { + Ok(Const) + } + } +} + pub trait ToConst { type Const: DimName; } diff --git a/src/base/matrix.rs b/src/base/matrix.rs index 3b4bc50c7..bc9942be6 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -29,7 +29,10 @@ use crate::base::storage::{ ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut, }; use crate::base::{Const, DefaultAllocator, OMatrix, OVector, Scalar, Unit}; -use crate::SimdComplexField; +use crate::{ArrayStorage, SMatrix, SimdComplexField}; + +#[cfg(any(feature = "std", feature = "alloc"))] +use crate::{DMatrix, DVector, Dynamic, VecStorage}; /// A square matrix. pub type SquareMatrix = Matrix; @@ -305,6 +308,53 @@ where { } +#[cfg(feature = "rkyv-serialize-no-std")] +mod rkyv_impl { + use super::Matrix; + use core::marker::PhantomData; + use rkyv::{offset_of, project_struct, Archive, Deserialize, Fallible, Serialize}; + + impl Archive for Matrix { + type Archived = Matrix; + type Resolver = S::Resolver; + + fn resolve( + &self, + pos: usize, + resolver: Self::Resolver, + out: &mut core::mem::MaybeUninit, + ) { + self.data.resolve( + pos + offset_of!(Self::Archived, data), + resolver, + project_struct!(out: Self::Archived => data), + ); + } + } + + impl, _S: Fallible + ?Sized> Serialize<_S> + for Matrix + { + fn serialize(&self, serializer: &mut _S) -> Result { + Ok(self.data.serialize(serializer)?) + } + } + + impl + Deserialize, D> + for Matrix + where + S::Archived: Deserialize, + { + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(Matrix { + data: self.data.deserialize(deserializer)?, + _phantoms: PhantomData, + }) + } + } +} + impl Matrix { /// Creates a new matrix with the given data without statically checking that the matrix /// dimension matches the storage dimension. @@ -317,6 +367,49 @@ impl Matrix { } } +impl SMatrix { + /// Creates a new statically-allocated matrix from the given [ArrayStorage]. + /// + /// This method exists primarily as a workaround for the fact that `from_data` can not + /// work in `const fn` contexts. + #[inline(always)] + pub const fn from_array_storage(storage: ArrayStorage) -> Self { + // This is sound because the row and column types are exactly the same as that of the + // storage, so there can be no mismatch + unsafe { Self::from_data_statically_unchecked(storage) } + } +} + +// TODO: Consider removing/deprecating `from_vec_storage` once we are able to make +// `from_data` const fn compatible +#[cfg(any(feature = "std", feature = "alloc"))] +impl DMatrix { + /// Creates a new heap-allocated matrix from the given [VecStorage]. + /// + /// This method exists primarily as a workaround for the fact that `from_data` can not + /// work in `const fn` contexts. + pub const fn from_vec_storage(storage: VecStorage) -> Self { + // This is sound because the dimensions of the matrix and the storage are guaranteed + // to be the same + unsafe { Self::from_data_statically_unchecked(storage) } + } +} + +// TODO: Consider removing/deprecating `from_vec_storage` once we are able to make +// `from_data` const fn compatible +#[cfg(any(feature = "std", feature = "alloc"))] +impl DVector { + /// Creates a new heap-allocated matrix from the given [VecStorage]. + /// + /// This method exists primarily as a workaround for the fact that `from_data` can not + /// work in `const fn` contexts. + pub const fn from_vec_storage(storage: VecStorage) -> Self { + // This is sound because the dimensions of the matrix and the storage are guaranteed + // to be the same + unsafe { Self::from_data_statically_unchecked(storage) } + } +} + impl> Matrix { /// Creates a new matrix with the given data. #[inline(always)] diff --git a/src/base/unit.rs b/src/base/unit.rs index c2e60da39..b5a4096f4 100644 --- a/src/base/unit.rs +++ b/src/base/unit.rs @@ -71,6 +71,47 @@ impl Abomonation for Unit { } } +#[cfg(feature = "rkyv-serialize-no-std")] +mod rkyv_impl { + use super::Unit; + use rkyv::{offset_of, project_struct, Archive, Deserialize, Fallible, Serialize}; + + impl Archive for Unit { + type Archived = Unit; + type Resolver = T::Resolver; + + fn resolve( + &self, + pos: usize, + resolver: Self::Resolver, + out: &mut ::core::mem::MaybeUninit, + ) { + self.value.resolve( + pos + offset_of!(Self::Archived, value), + resolver, + project_struct!(out: Self::Archived => value), + ); + } + } + + impl, S: Fallible + ?Sized> Serialize for Unit { + fn serialize(&self, serializer: &mut S) -> Result { + Ok(self.value.serialize(serializer)?) + } + } + + impl Deserialize, D> for Unit + where + T::Archived: Deserialize, + { + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(Unit { + value: self.value.deserialize(deserializer)?, + }) + } + } +} + impl PartialEq for Unit> where T: Scalar + PartialEq, diff --git a/src/base/vec_storage.rs b/src/base/vec_storage.rs index 29da127d8..8b2d2ef23 100644 --- a/src/base/vec_storage.rs +++ b/src/base/vec_storage.rs @@ -13,6 +13,12 @@ use crate::base::storage::{ }; use crate::base::{Scalar, Vector}; +#[cfg(feature = "serde-serialize-no-std")] +use serde::{ + de::{Deserialize, Deserializer, Error}, + ser::{Serialize, Serializer}, +}; + #[cfg(feature = "abomonation-serialize")] use abomonation::Abomonation; @@ -24,13 +30,54 @@ use abomonation::Abomonation; /// A Vec-based matrix data storage. It may be dynamically-sized. #[repr(C)] #[derive(Eq, Debug, Clone, PartialEq)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct VecStorage { data: Vec, nrows: R, ncols: C, } +#[cfg(feature = "serde-serialize")] +impl Serialize for VecStorage +where + T: Serialize, + R: Serialize, + C: Serialize, +{ + fn serialize(&self, serializer: Ser) -> Result + where + Ser: Serializer, + { + (&self.data, &self.nrows, &self.ncols).serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'a, T, R: Dim, C: Dim> Deserialize<'a> for VecStorage +where + T: Deserialize<'a>, + R: Deserialize<'a>, + C: Deserialize<'a>, +{ + fn deserialize(deserializer: Des) -> Result + where + Des: Deserializer<'a>, + { + let (data, nrows, ncols): (Vec, R, C) = Deserialize::deserialize(deserializer)?; + + // SAFETY: make sure the data we deserialize have the + // correct number of elements. + if nrows.value() * ncols.value() != data.len() { + return Err(Des::Error::custom(format!( + "Expected {} components, found {}", + nrows.value() * ncols.value(), + data.len() + ))); + } + + Ok(Self { data, nrows, ncols }) + } +} + #[deprecated(note = "renamed to `VecStorage`")] /// Renamed to [VecStorage]. pub type MatrixVec = VecStorage; diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs index c5fd844cd..d307c393d 100755 --- a/src/geometry/isometry.rs +++ b/src/geometry/isometry.rs @@ -98,6 +98,66 @@ where } } +#[cfg(feature = "rkyv-serialize-no-std")] +mod rkyv_impl { + use super::Isometry; + use crate::{base::Scalar, geometry::Translation}; + use rkyv::{offset_of, project_struct, Archive, Deserialize, Fallible, Serialize}; + + impl Archive for Isometry + where + T::Archived: Scalar, + { + type Archived = Isometry; + type Resolver = (R::Resolver, as Archive>::Resolver); + + fn resolve( + &self, + pos: usize, + resolver: Self::Resolver, + out: &mut core::mem::MaybeUninit, + ) { + self.rotation.resolve( + pos + offset_of!(Self::Archived, rotation), + resolver.0, + project_struct!(out: Self::Archived => rotation), + ); + self.translation.resolve( + pos + offset_of!(Self::Archived, translation), + resolver.1, + project_struct!(out: Self::Archived => translation), + ); + } + } + + impl, R: Serialize, S: Fallible + ?Sized, const D: usize> + Serialize for Isometry + where + T::Archived: Scalar, + { + fn serialize(&self, serializer: &mut S) -> Result { + Ok(( + self.rotation.serialize(serializer)?, + self.translation.serialize(serializer)?, + )) + } + } + + impl + Deserialize, _D> for Isometry + where + T::Archived: Scalar + Deserialize, + R::Archived: Scalar + Deserialize, + { + fn deserialize(&self, deserializer: &mut _D) -> Result, _D::Error> { + Ok(Isometry { + rotation: self.rotation.deserialize(deserializer)?, + translation: self.translation.deserialize(deserializer)?, + }) + } + } +} + impl hash::Hash for Isometry where Owned>: hash::Hash, diff --git a/src/geometry/isometry_conversion.rs b/src/geometry/isometry_conversion.rs index b8cc4568b..75c0d98d5 100644 --- a/src/geometry/isometry_conversion.rs +++ b/src/geometry/isometry_conversion.rs @@ -9,6 +9,7 @@ use crate::geometry::{ AbstractRotation, Isometry, Isometry3, Similarity, SuperTCategoryOf, TAffine, Transform, Translation, UnitDualQuaternion, UnitQuaternion, }; +use crate::{Point, SVector}; /* * This file provides the following conversions: @@ -198,6 +199,35 @@ where } } +impl From<[T; D]> for Isometry +where + R: AbstractRotation, +{ + #[inline] + fn from(coords: [T; D]) -> Self { + Self::from_parts(coords.into(), R::identity()) + } +} + +impl From> for Isometry +where + R: AbstractRotation, +{ + #[inline] + fn from(coords: SVector) -> Self { + Self::from_parts(coords.into(), R::identity()) + } +} +impl From> for Isometry +where + R: AbstractRotation, +{ + #[inline] + fn from(coords: Point) -> Self { + Self::from_parts(coords.into(), R::identity()) + } +} + impl From<[Isometry; 2]> for Isometry where diff --git a/src/geometry/point_construction.rs b/src/geometry/point_construction.rs index f50a3fc17..7e34137c6 100644 --- a/src/geometry/point_construction.rs +++ b/src/geometry/point_construction.rs @@ -225,17 +225,3 @@ componentwise_constructors_impl!( "# use nalgebra::Point6;\nlet p = Point6::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);\nassert!(p.x == 1.0 && p.y == 2.0 && p.z == 3.0 && p.w == 4.0 && p.a == 5.0 && p.b == 6.0);"; Point6, Vector6, x:0, y:1, z:2, w:3, a:4, b:5; ); - -macro_rules! from_array_impl( - ($($Point: ident, $len: expr);*) => {$( - impl From<[T; $len]> for $Point { - fn from(coords: [T; $len]) -> Self { - Self { - coords: coords.into() - } - } - } - )*} -); - -from_array_impl!(Point1, 1; Point2, 2; Point3, 3; Point4, 4; Point5, 5; Point6, 6); diff --git a/src/geometry/point_conversion.rs b/src/geometry/point_conversion.rs index 08438ec0a..91cd62ce2 100644 --- a/src/geometry/point_conversion.rs +++ b/src/geometry/point_conversion.rs @@ -81,6 +81,22 @@ where } } +impl From<[T; D]> for Point { + #[inline] + fn from(coords: [T; D]) -> Self { + Point { + coords: coords.into(), + } + } +} + +impl Into<[T; D]> for Point { + #[inline] + fn into(self) -> [T; D] { + self.coords.into() + } +} + impl From>> for Point { #[inline] fn from(coords: OVector>) -> Self { diff --git a/src/geometry/quaternion.rs b/src/geometry/quaternion.rs index 365095b83..6852335bf 100755 --- a/src/geometry/quaternion.rs +++ b/src/geometry/quaternion.rs @@ -113,6 +113,48 @@ where } } +#[cfg(feature = "rkyv-serialize-no-std")] +mod rkyv_impl { + use super::Quaternion; + use crate::base::Vector4; + use rkyv::{offset_of, project_struct, Archive, Deserialize, Fallible, Serialize}; + + impl Archive for Quaternion { + type Archived = Quaternion; + type Resolver = as Archive>::Resolver; + + fn resolve( + &self, + pos: usize, + resolver: Self::Resolver, + out: &mut core::mem::MaybeUninit, + ) { + self.coords.resolve( + pos + offset_of!(Self::Archived, coords), + resolver, + project_struct!(out: Self::Archived => coords), + ); + } + } + + impl, S: Fallible + ?Sized> Serialize for Quaternion { + fn serialize(&self, serializer: &mut S) -> Result { + Ok(self.coords.serialize(serializer)?) + } + } + + impl Deserialize, D> for Quaternion + where + T::Archived: Deserialize, + { + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(Quaternion { + coords: self.coords.deserialize(deserializer)?, + }) + } + } +} + impl Quaternion where T::Element: SimdRealField, diff --git a/src/geometry/translation.rs b/src/geometry/translation.rs index 1a8f4b043..dcb7b6036 100755 --- a/src/geometry/translation.rs +++ b/src/geometry/translation.rs @@ -97,6 +97,49 @@ where } } +#[cfg(feature = "rkyv-serialize-no-std")] +mod rkyv_impl { + use super::Translation; + use crate::base::SVector; + use rkyv::{offset_of, project_struct, Archive, Deserialize, Fallible, Serialize}; + + impl Archive for Translation { + type Archived = Translation; + type Resolver = as Archive>::Resolver; + + fn resolve( + &self, + pos: usize, + resolver: Self::Resolver, + out: &mut core::mem::MaybeUninit, + ) { + self.vector.resolve( + pos + offset_of!(Self::Archived, vector), + resolver, + project_struct!(out: Self::Archived => vector), + ); + } + } + + impl, S: Fallible + ?Sized, const D: usize> Serialize for Translation { + fn serialize(&self, serializer: &mut S) -> Result { + Ok(self.vector.serialize(serializer)?) + } + } + + impl Deserialize, _D> + for Translation + where + T::Archived: Deserialize, + { + fn deserialize(&self, deserializer: &mut _D) -> Result, _D::Error> { + Ok(Translation { + vector: self.vector.deserialize(deserializer)?, + }) + } + } +} + impl Translation { /// Creates a new translation from the given vector. #[inline] diff --git a/src/geometry/translation_conversion.rs b/src/geometry/translation_conversion.rs index 8c8f860f7..ff8e797f3 100644 --- a/src/geometry/translation_conversion.rs +++ b/src/geometry/translation_conversion.rs @@ -11,6 +11,7 @@ use crate::geometry::{ AbstractRotation, Isometry, Similarity, SuperTCategoryOf, TAffine, Transform, Translation, Translation3, UnitDualQuaternion, UnitQuaternion, }; +use crate::Point; /* * This file provides the following conversions: @@ -199,6 +200,31 @@ impl From>> for Translation } } +impl From<[T; D]> for Translation { + #[inline] + fn from(coords: [T; D]) -> Self { + Translation { + vector: coords.into(), + } + } +} + +impl From> for Translation { + #[inline] + fn from(pt: Point) -> Self { + Translation { + vector: pt.coords.into(), + } + } +} + +impl Into<[T; D]> for Translation { + #[inline] + fn into(self) -> [T; D] { + self.vector.into() + } +} + impl From<[Translation; 2]> for Translation where diff --git a/src/lib.rs b/src/lib.rs index bc6d17187..8378b2720 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,6 +136,9 @@ pub use crate::sparse::*; )] pub use base as core; +#[cfg(feature = "macros")] +pub use nalgebra_macros::{dmatrix, dvector, matrix, point, vector}; + use simba::scalar::SupersetOf; use std::cmp::{self, Ordering, PartialOrd}; diff --git a/src/third_party/glam/common/glam_isometry.rs b/src/third_party/glam/common/glam_isometry.rs new file mode 100644 index 000000000..6bd970d29 --- /dev/null +++ b/src/third_party/glam/common/glam_isometry.rs @@ -0,0 +1,193 @@ +use super::glam::{DMat3, DMat4, DQuat, DVec2, DVec3, Mat3, Mat4, Quat, Vec2, Vec3}; +use crate::{Isometry2, Isometry3, Matrix3, Matrix4}; +use std::convert::TryFrom; + +impl From> for Mat3 { + fn from(iso: Isometry2) -> Mat3 { + iso.to_homogeneous().into() + } +} +impl From> for Mat4 { + fn from(iso: Isometry3) -> Mat4 { + iso.to_homogeneous().into() + } +} + +impl From> for DMat3 { + fn from(iso: Isometry2) -> DMat3 { + iso.to_homogeneous().into() + } +} +impl From> for DMat4 { + fn from(iso: Isometry3) -> DMat4 { + iso.to_homogeneous().into() + } +} + +impl From> for (Vec3, Quat) { + fn from(iso: Isometry3) -> (Vec3, Quat) { + (iso.translation.into(), iso.rotation.into()) + } +} + +impl From> for (DVec3, DQuat) { + fn from(iso: Isometry3) -> (DVec3, DQuat) { + (iso.translation.into(), iso.rotation.into()) + } +} + +impl From> for (Vec3, Quat) { + fn from(iso: Isometry2) -> (Vec3, Quat) { + let tra = Vec3::new(iso.translation.x, iso.translation.y, 0.0); + let rot = Quat::from_axis_angle(Vec3::Z, iso.rotation.angle()); + (tra, rot) + } +} + +impl From> for (DVec3, DQuat) { + fn from(iso: Isometry2) -> (DVec3, DQuat) { + let tra = DVec3::new(iso.translation.x, iso.translation.y, 0.0); + let rot = DQuat::from_axis_angle(DVec3::Z, iso.rotation.angle()); + (tra, rot) + } +} + +impl From<(Vec3, Quat)> for Isometry3 { + fn from((tra, rot): (Vec3, Quat)) -> Self { + Isometry3::from_parts(tra.into(), rot.into()) + } +} + +impl From<(DVec3, DQuat)> for Isometry3 { + fn from((tra, rot): (DVec3, DQuat)) -> Self { + Isometry3::from_parts(tra.into(), rot.into()) + } +} + +impl From<(Vec3, Quat)> for Isometry2 { + fn from((tra, rot): (Vec3, Quat)) -> Self { + Isometry2::new([tra.x, tra.y].into(), rot.to_axis_angle().1) + } +} + +impl From<(DVec3, DQuat)> for Isometry2 { + fn from((tra, rot): (DVec3, DQuat)) -> Self { + Isometry2::new([tra.x, tra.y].into(), rot.to_axis_angle().1) + } +} + +impl From<(Vec2, Quat)> for Isometry2 { + fn from((tra, rot): (Vec2, Quat)) -> Self { + Isometry2::new(tra.into(), rot.to_axis_angle().1) + } +} + +impl From<(DVec2, DQuat)> for Isometry2 { + fn from((tra, rot): (DVec2, DQuat)) -> Self { + Isometry2::new(tra.into(), rot.to_axis_angle().1) + } +} + +impl From<(Vec2, f32)> for Isometry2 { + fn from((tra, rot): (Vec2, f32)) -> Self { + Isometry2::new(tra.into(), rot) + } +} + +impl From<(DVec2, f64)> for Isometry2 { + fn from((tra, rot): (DVec2, f64)) -> Self { + Isometry2::new(tra.into(), rot) + } +} + +impl From for Isometry3 { + fn from(rot: Quat) -> Self { + Isometry3::from_parts(crate::one(), rot.into()) + } +} + +impl From for Isometry3 { + fn from(rot: DQuat) -> Self { + Isometry3::from_parts(crate::one(), rot.into()) + } +} + +impl From for Isometry2 { + fn from(rot: Quat) -> Self { + Isometry2::new(crate::zero(), rot.to_axis_angle().1) + } +} + +impl From for Isometry2 { + fn from(rot: DQuat) -> Self { + Isometry2::new(crate::zero(), rot.to_axis_angle().1) + } +} + +impl From for Isometry3 { + fn from(tra: Vec3) -> Self { + Isometry3::from_parts(tra.into(), crate::one()) + } +} + +impl From for Isometry3 { + fn from(tra: DVec3) -> Self { + Isometry3::from_parts(tra.into(), crate::one()) + } +} + +impl From for Isometry2 { + fn from(tra: Vec2) -> Self { + Isometry2::new(tra.into(), crate::one()) + } +} + +impl From for Isometry2 { + fn from(tra: DVec2) -> Self { + Isometry2::new(tra.into(), crate::one()) + } +} + +impl From for Isometry2 { + fn from(tra: Vec3) -> Self { + Isometry2::new([tra.x, tra.y].into(), crate::one()) + } +} + +impl From for Isometry2 { + fn from(tra: DVec3) -> Self { + Isometry2::new([tra.x, tra.y].into(), crate::one()) + } +} + +impl TryFrom for Isometry2 { + type Error = (); + + fn try_from(mat3: Mat3) -> Result, Self::Error> { + crate::try_convert(Matrix3::from(mat3)).ok_or(()) + } +} + +impl TryFrom for Isometry3 { + type Error = (); + + fn try_from(mat4: Mat4) -> Result, Self::Error> { + crate::try_convert(Matrix4::from(mat4)).ok_or(()) + } +} + +impl TryFrom for Isometry2 { + type Error = (); + + fn try_from(mat3: DMat3) -> Result, Self::Error> { + crate::try_convert(Matrix3::from(mat3)).ok_or(()) + } +} + +impl TryFrom for Isometry3 { + type Error = (); + + fn try_from(mat4: DMat4) -> Result, Self::Error> { + crate::try_convert(Matrix4::from(mat4)).ok_or(()) + } +} diff --git a/src/third_party/glam/glam_matrix.rs b/src/third_party/glam/common/glam_matrix.rs similarity index 99% rename from src/third_party/glam/glam_matrix.rs rename to src/third_party/glam/common/glam_matrix.rs index bbb2bd17d..77b68b5e2 100644 --- a/src/third_party/glam/glam_matrix.rs +++ b/src/third_party/glam/common/glam_matrix.rs @@ -1,9 +1,9 @@ -use crate::storage::Storage; -use crate::{Matrix, Matrix2, Matrix3, Matrix4, Vector, Vector2, Vector3, Vector4, U2, U3, U4}; -use glam::{ +use super::glam::{ BVec2, BVec3, BVec4, DMat2, DMat3, DMat4, DVec2, DVec3, DVec4, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec3A, Vec4, }; +use crate::storage::Storage; +use crate::{Matrix, Matrix2, Matrix3, Matrix4, Vector, Vector2, Vector3, Vector4, U2, U3, U4}; macro_rules! impl_vec_conversion( ($N: ty, $Vec2: ty, $Vec3: ty, $Vec4: ty) => { diff --git a/src/third_party/glam/glam_point.rs b/src/third_party/glam/common/glam_point.rs similarity index 98% rename from src/third_party/glam/glam_point.rs rename to src/third_party/glam/common/glam_point.rs index 823dc806d..b15a6c6df 100644 --- a/src/third_party/glam/glam_point.rs +++ b/src/third_party/glam/common/glam_point.rs @@ -1,8 +1,8 @@ -use crate::{Point2, Point3, Point4}; -use glam::{ +use super::glam::{ BVec2, BVec3, BVec4, DVec2, DVec3, DVec4, IVec2, IVec3, IVec4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec3A, Vec4, }; +use crate::{Point2, Point3, Point4}; macro_rules! impl_point_conversion( ($N: ty, $Vec2: ty, $Vec3: ty, $Vec4: ty) => { diff --git a/src/third_party/glam/glam_quaternion.rs b/src/third_party/glam/common/glam_quaternion.rs similarity index 64% rename from src/third_party/glam/glam_quaternion.rs rename to src/third_party/glam/common/glam_quaternion.rs index e57b77149..e0432ead9 100644 --- a/src/third_party/glam/glam_quaternion.rs +++ b/src/third_party/glam/common/glam_quaternion.rs @@ -1,5 +1,5 @@ +use super::glam::{DQuat, Quat}; use crate::{Quaternion, UnitQuaternion}; -use glam::{DQuat, Quat}; impl From for Quaternion { #[inline] @@ -43,22 +43,16 @@ impl From> for DQuat { } } -#[cfg(feature = "convert-glam-unchecked")] -mod unchecked { - use crate::{Quaternion, UnitQuaternion}; - use glam::{DQuat, Quat}; - - impl From for UnitQuaternion { - #[inline] - fn from(e: Quat) -> UnitQuaternion { - UnitQuaternion::new_unchecked(Quaternion::from(e)) - } +impl From for UnitQuaternion { + #[inline] + fn from(e: Quat) -> UnitQuaternion { + UnitQuaternion::new_normalize(Quaternion::from(e)) } +} - impl From for UnitQuaternion { - #[inline] - fn from(e: DQuat) -> UnitQuaternion { - UnitQuaternion::new_unchecked(Quaternion::from(e)) - } +impl From for UnitQuaternion { + #[inline] + fn from(e: DQuat) -> UnitQuaternion { + UnitQuaternion::new_normalize(Quaternion::from(e)) } } diff --git a/src/third_party/glam/common/glam_rotation.rs b/src/third_party/glam/common/glam_rotation.rs new file mode 100644 index 000000000..1e1fe8e15 --- /dev/null +++ b/src/third_party/glam/common/glam_rotation.rs @@ -0,0 +1,58 @@ +use super::glam::{DMat2, DQuat, Mat2, Quat}; +use crate::{Rotation2, Rotation3, UnitComplex, UnitQuaternion}; + +impl From> for Mat2 { + #[inline] + fn from(e: Rotation2) -> Mat2 { + e.into_inner().into() + } +} + +impl From> for DMat2 { + #[inline] + fn from(e: Rotation2) -> DMat2 { + e.into_inner().into() + } +} + +impl From> for Quat { + #[inline] + fn from(e: Rotation3) -> Quat { + UnitQuaternion::from(e).into() + } +} + +impl From> for DQuat { + #[inline] + fn from(e: Rotation3) -> DQuat { + UnitQuaternion::from(e).into() + } +} + +impl From for Rotation2 { + #[inline] + fn from(e: Mat2) -> Rotation2 { + UnitComplex::from(e).to_rotation_matrix() + } +} + +impl From for Rotation2 { + #[inline] + fn from(e: DMat2) -> Rotation2 { + UnitComplex::from(e).to_rotation_matrix() + } +} + +impl From for Rotation3 { + #[inline] + fn from(e: Quat) -> Rotation3 { + Rotation3::from(UnitQuaternion::from(e)) + } +} + +impl From for Rotation3 { + #[inline] + fn from(e: DQuat) -> Rotation3 { + Rotation3::from(UnitQuaternion::from(e)) + } +} diff --git a/src/third_party/glam/common/glam_similarity.rs b/src/third_party/glam/common/glam_similarity.rs new file mode 100644 index 000000000..f6679f08d --- /dev/null +++ b/src/third_party/glam/common/glam_similarity.rs @@ -0,0 +1,53 @@ +use super::glam::{DMat3, DMat4, Mat3, Mat4}; +use crate::{Matrix3, Matrix4, Similarity2, Similarity3}; +use std::convert::TryFrom; + +impl From> for Mat3 { + fn from(iso: Similarity2) -> Mat3 { + iso.to_homogeneous().into() + } +} +impl From> for Mat4 { + fn from(iso: Similarity3) -> Mat4 { + iso.to_homogeneous().into() + } +} + +impl From> for DMat3 { + fn from(iso: Similarity2) -> DMat3 { + iso.to_homogeneous().into() + } +} +impl From> for DMat4 { + fn from(iso: Similarity3) -> DMat4 { + iso.to_homogeneous().into() + } +} + +impl TryFrom for Similarity2 { + type Error = (); + fn try_from(mat3: Mat3) -> Result, ()> { + crate::try_convert(Matrix3::from(mat3)).ok_or(()) + } +} + +impl TryFrom for Similarity3 { + type Error = (); + fn try_from(mat4: Mat4) -> Result, ()> { + crate::try_convert(Matrix4::from(mat4)).ok_or(()) + } +} + +impl TryFrom for Similarity2 { + type Error = (); + fn try_from(mat3: DMat3) -> Result, ()> { + crate::try_convert(Matrix3::from(mat3)).ok_or(()) + } +} + +impl TryFrom for Similarity3 { + type Error = (); + fn try_from(mat4: DMat4) -> Result, ()> { + crate::try_convert(Matrix4::from(mat4)).ok_or(()) + } +} diff --git a/src/third_party/glam/common/glam_translation.rs b/src/third_party/glam/common/glam_translation.rs new file mode 100644 index 000000000..6dbbf602c --- /dev/null +++ b/src/third_party/glam/common/glam_translation.rs @@ -0,0 +1,65 @@ +use super::glam::{DVec2, DVec3, DVec4, Vec2, Vec3, Vec3A, Vec4}; +use crate::{Translation2, Translation3, Translation4}; + +macro_rules! impl_translation_conversion( + ($N: ty, $Vec2: ty, $Vec3: ty, $Vec4: ty) => { + impl From<$Vec2> for Translation2<$N> { + #[inline] + fn from(e: $Vec2) -> Translation2<$N> { + (*e.as_ref()).into() + } + } + + impl From> for $Vec2 { + #[inline] + fn from(e: Translation2<$N>) -> $Vec2 { + e.vector.into() + } + } + + impl From<$Vec3> for Translation3<$N> { + #[inline] + fn from(e: $Vec3) -> Translation3<$N> { + (*e.as_ref()).into() + } + } + + impl From> for $Vec3 { + #[inline] + fn from(e: Translation3<$N>) -> $Vec3 { + e.vector.into() + } + } + + impl From<$Vec4> for Translation4<$N> { + #[inline] + fn from(e: $Vec4) -> Translation4<$N> { + (*e.as_ref()).into() + } + } + + impl From> for $Vec4 { + #[inline] + fn from(e: Translation4<$N>) -> $Vec4 { + e.vector.into() + } + } + } +); + +impl_translation_conversion!(f32, Vec2, Vec3, Vec4); +impl_translation_conversion!(f64, DVec2, DVec3, DVec4); + +impl From for Translation3 { + #[inline] + fn from(e: Vec3A) -> Translation3 { + (*e.as_ref()).into() + } +} + +impl From> for Vec3A { + #[inline] + fn from(e: Translation3) -> Vec3A { + e.vector.into() + } +} diff --git a/src/third_party/glam/common/glam_unit_complex.rs b/src/third_party/glam/common/glam_unit_complex.rs new file mode 100644 index 000000000..7ee6fc65a --- /dev/null +++ b/src/third_party/glam/common/glam_unit_complex.rs @@ -0,0 +1,30 @@ +use super::glam::{DMat2, Mat2}; +use crate::{Complex, UnitComplex}; + +impl From> for Mat2 { + #[inline] + fn from(e: UnitComplex) -> Mat2 { + e.to_rotation_matrix().into_inner().into() + } +} + +impl From> for DMat2 { + #[inline] + fn from(e: UnitComplex) -> DMat2 { + e.to_rotation_matrix().into_inner().into() + } +} + +impl From for UnitComplex { + #[inline] + fn from(e: Mat2) -> UnitComplex { + UnitComplex::new_normalize(Complex::new(e.x_axis.x, e.x_axis.y)) + } +} + +impl From for UnitComplex { + #[inline] + fn from(e: DMat2) -> UnitComplex { + UnitComplex::new_normalize(Complex::new(e.x_axis.x, e.x_axis.y)) + } +} diff --git a/src/third_party/glam/glam_isometry.rs b/src/third_party/glam/glam_isometry.rs deleted file mode 100644 index 52c36e378..000000000 --- a/src/third_party/glam/glam_isometry.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::{Isometry2, Isometry3}; -use glam::{DMat3, DMat4, Mat3, Mat4}; - -impl From> for Mat3 { - fn from(iso: Isometry2) -> Mat3 { - iso.to_homogeneous().into() - } -} -impl From> for Mat4 { - fn from(iso: Isometry3) -> Mat4 { - iso.to_homogeneous().into() - } -} - -impl From> for DMat3 { - fn from(iso: Isometry2) -> DMat3 { - iso.to_homogeneous().into() - } -} -impl From> for DMat4 { - fn from(iso: Isometry3) -> DMat4 { - iso.to_homogeneous().into() - } -} - -#[cfg(feature = "convert-glam-unchecked")] -mod unchecked { - use crate::{Isometry2, Isometry3, Matrix3, Matrix4}; - use glam::{DMat3, DMat4, Mat3, Mat4}; - - impl From for Isometry2 { - fn from(mat3: Mat3) -> Isometry2 { - crate::convert_unchecked(Matrix3::from(mat3)) - } - } - - impl From for Isometry3 { - fn from(mat4: Mat4) -> Isometry3 { - crate::convert_unchecked(Matrix4::from(mat4)) - } - } - - impl From for Isometry2 { - fn from(mat3: DMat3) -> Isometry2 { - crate::convert_unchecked(Matrix3::from(mat3)) - } - } - - impl From for Isometry3 { - fn from(mat4: DMat4) -> Isometry3 { - crate::convert_unchecked(Matrix4::from(mat4)) - } - } -} diff --git a/src/third_party/glam/glam_rotation.rs b/src/third_party/glam/glam_rotation.rs deleted file mode 100644 index 2db9f50e4..000000000 --- a/src/third_party/glam/glam_rotation.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::{Rotation2, Rotation3, UnitQuaternion}; -use glam::{DMat2, DQuat, Mat2, Quat}; - -impl From> for Mat2 { - #[inline] - fn from(e: Rotation2) -> Mat2 { - e.into_inner().into() - } -} - -impl From> for DMat2 { - #[inline] - fn from(e: Rotation2) -> DMat2 { - e.into_inner().into() - } -} - -impl From> for Quat { - #[inline] - fn from(e: Rotation3) -> Quat { - UnitQuaternion::from(e).into() - } -} - -impl From> for DQuat { - #[inline] - fn from(e: Rotation3) -> DQuat { - UnitQuaternion::from(e).into() - } -} - -#[cfg(feature = "convert-glam-unchecked")] -mod unchecked { - use crate::{Rotation2, Rotation3, UnitQuaternion}; - use glam::{DMat2, DQuat, Mat2, Quat}; - - impl From for Rotation2 { - #[inline] - fn from(e: Mat2) -> Rotation2 { - Rotation2::from_matrix_unchecked(e.into()) - } - } - - impl From for Rotation2 { - #[inline] - fn from(e: DMat2) -> Rotation2 { - Rotation2::from_matrix_unchecked(e.into()) - } - } - - impl From for Rotation3 { - #[inline] - fn from(e: Quat) -> Rotation3 { - Rotation3::from(UnitQuaternion::from(e)) - } - } - - impl From for Rotation3 { - #[inline] - fn from(e: DQuat) -> Rotation3 { - Rotation3::from(UnitQuaternion::from(e)) - } - } -} diff --git a/src/third_party/glam/glam_similarity.rs b/src/third_party/glam/glam_similarity.rs deleted file mode 100644 index 921070746..000000000 --- a/src/third_party/glam/glam_similarity.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::{Similarity2, Similarity3}; -use glam::{DMat3, DMat4, Mat3, Mat4}; - -impl From> for Mat3 { - fn from(iso: Similarity2) -> Mat3 { - iso.to_homogeneous().into() - } -} -impl From> for Mat4 { - fn from(iso: Similarity3) -> Mat4 { - iso.to_homogeneous().into() - } -} - -impl From> for DMat3 { - fn from(iso: Similarity2) -> DMat3 { - iso.to_homogeneous().into() - } -} -impl From> for DMat4 { - fn from(iso: Similarity3) -> DMat4 { - iso.to_homogeneous().into() - } -} - -#[cfg(feature = "convert-glam-unchecked")] -mod unchecked { - use crate::{Matrix3, Matrix4, Similarity2, Similarity3}; - use glam::{DMat3, DMat4, Mat3, Mat4}; - - impl From for Similarity2 { - fn from(mat3: Mat3) -> Similarity2 { - crate::convert_unchecked(Matrix3::from(mat3)) - } - } - - impl From for Similarity3 { - fn from(mat4: Mat4) -> Similarity3 { - crate::convert_unchecked(Matrix4::from(mat4)) - } - } - - impl From for Similarity2 { - fn from(mat3: DMat3) -> Similarity2 { - crate::convert_unchecked(Matrix3::from(mat3)) - } - } - - impl From for Similarity3 { - fn from(mat4: DMat4) -> Similarity3 { - crate::convert_unchecked(Matrix4::from(mat4)) - } - } -} diff --git a/src/third_party/glam/glam_unit_complex.rs b/src/third_party/glam/glam_unit_complex.rs deleted file mode 100644 index c5d9cc6a1..000000000 --- a/src/third_party/glam/glam_unit_complex.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::UnitComplex; -use glam::{DMat2, Mat2}; - -impl From> for Mat2 { - #[inline] - fn from(e: UnitComplex) -> Mat2 { - e.to_rotation_matrix().into_inner().into() - } -} - -impl From> for DMat2 { - #[inline] - fn from(e: UnitComplex) -> DMat2 { - e.to_rotation_matrix().into_inner().into() - } -} - -#[cfg(feature = "convert-glam-unchecked")] -mod unchecked { - use crate::{Rotation2, UnitComplex}; - use glam::{DMat2, Mat2}; - - impl From for UnitComplex { - #[inline] - fn from(e: Mat2) -> UnitComplex { - Rotation2::from_matrix_unchecked(e.into()).into() - } - } - - impl From for UnitComplex { - #[inline] - fn from(e: DMat2) -> UnitComplex { - Rotation2::from_matrix_unchecked(e.into()).into() - } - } -} diff --git a/src/third_party/glam/mod.rs b/src/third_party/glam/mod.rs index d4f8b6435..a09e37ca3 100644 --- a/src/third_party/glam/mod.rs +++ b/src/third_party/glam/mod.rs @@ -1,7 +1,6 @@ -mod glam_isometry; -mod glam_matrix; -mod glam_point; -mod glam_quaternion; -mod glam_rotation; -mod glam_similarity; -mod glam_unit_complex; +#[cfg(feature = "glam013")] +mod v013; +#[cfg(feature = "glam014")] +mod v014; +#[cfg(feature = "glam015")] +mod v015; diff --git a/src/third_party/glam/v013/mod.rs b/src/third_party/glam/v013/mod.rs new file mode 100644 index 000000000..4787fb216 --- /dev/null +++ b/src/third_party/glam/v013/mod.rs @@ -0,0 +1,18 @@ +#[path = "../common/glam_isometry.rs"] +mod glam_isometry; +#[path = "../common/glam_matrix.rs"] +mod glam_matrix; +#[path = "../common/glam_point.rs"] +mod glam_point; +#[path = "../common/glam_quaternion.rs"] +mod glam_quaternion; +#[path = "../common/glam_rotation.rs"] +mod glam_rotation; +#[path = "../common/glam_similarity.rs"] +mod glam_similarity; +#[path = "../common/glam_translation.rs"] +mod glam_translation; +#[path = "../common/glam_unit_complex.rs"] +mod glam_unit_complex; + +pub(self) use glam013 as glam; diff --git a/src/third_party/glam/v014/mod.rs b/src/third_party/glam/v014/mod.rs new file mode 100644 index 000000000..3f9b8286d --- /dev/null +++ b/src/third_party/glam/v014/mod.rs @@ -0,0 +1,18 @@ +#[path = "../common/glam_isometry.rs"] +mod glam_isometry; +#[path = "../common/glam_matrix.rs"] +mod glam_matrix; +#[path = "../common/glam_point.rs"] +mod glam_point; +#[path = "../common/glam_quaternion.rs"] +mod glam_quaternion; +#[path = "../common/glam_rotation.rs"] +mod glam_rotation; +#[path = "../common/glam_similarity.rs"] +mod glam_similarity; +#[path = "../common/glam_translation.rs"] +mod glam_translation; +#[path = "../common/glam_unit_complex.rs"] +mod glam_unit_complex; + +pub(self) use glam014 as glam; diff --git a/src/third_party/glam/v015/mod.rs b/src/third_party/glam/v015/mod.rs new file mode 100644 index 000000000..83b79848c --- /dev/null +++ b/src/third_party/glam/v015/mod.rs @@ -0,0 +1,18 @@ +#[path = "../common/glam_isometry.rs"] +mod glam_isometry; +#[path = "../common/glam_matrix.rs"] +mod glam_matrix; +#[path = "../common/glam_point.rs"] +mod glam_point; +#[path = "../common/glam_quaternion.rs"] +mod glam_quaternion; +#[path = "../common/glam_rotation.rs"] +mod glam_rotation; +#[path = "../common/glam_similarity.rs"] +mod glam_similarity; +#[path = "../common/glam_translation.rs"] +mod glam_translation; +#[path = "../common/glam_unit_complex.rs"] +mod glam_unit_complex; + +pub(self) use glam015 as glam; diff --git a/src/third_party/mod.rs b/src/third_party/mod.rs index ce0fcaadc..218c6879b 100644 --- a/src/third_party/mod.rs +++ b/src/third_party/mod.rs @@ -1,6 +1,5 @@ #[cfg(feature = "alga")] mod alga; -#[cfg(feature = "glam")] mod glam; #[cfg(feature = "mint")] mod mint; diff --git a/tests/core/conversion.rs b/tests/core/conversion.rs index 58ac179a7..5374c3995 100644 --- a/tests/core/conversion.rs +++ b/tests/core/conversion.rs @@ -1,5 +1,4 @@ -#![cfg(all(feature = "proptest-support", feature = "alga"))] -use alga::linear::Transformation; +#![cfg(all(feature = "proptest-support"))] use na::{ self, Affine3, Isometry3, Matrix2, Matrix2x3, Matrix2x4, Matrix2x5, Matrix2x6, Matrix3, Matrix3x2, Matrix3x4, Matrix3x5, Matrix3x6, Matrix4, Matrix4x2, Matrix4x3, Matrix4x5, @@ -16,7 +15,7 @@ use proptest::{prop_assert, prop_assert_eq, proptest}; proptest! { #[test] - fn translation_conversion(t in translation3(), v in vector3(), p in point3()) { + fn translation_conversion(t in translation3(), p in point3()) { let iso: Isometry3 = na::convert(t); let sim: Similarity3 = na::convert(t); let aff: Affine3 = na::convert(t); @@ -29,12 +28,6 @@ proptest! { prop_assert_eq!(t, na::try_convert(prj).unwrap()); prop_assert_eq!(t, na::try_convert(tr).unwrap() ); - prop_assert_eq!(t.transform_vector(&v), iso * v); - prop_assert_eq!(t.transform_vector(&v), sim * v); - prop_assert_eq!(t.transform_vector(&v), aff * v); - prop_assert_eq!(t.transform_vector(&v), prj * v); - prop_assert_eq!(t.transform_vector(&v), tr * v); - prop_assert_eq!(t * p, iso * p); prop_assert_eq!(t * p, sim * p); prop_assert_eq!(t * p, aff * p); diff --git a/tests/core/macros.rs b/tests/core/macros.rs new file mode 100644 index 000000000..2987a41b5 --- /dev/null +++ b/tests/core/macros.rs @@ -0,0 +1,12 @@ +use nalgebra::{dmatrix, dvector, matrix, point, vector}; + +#[test] +fn sanity_test() { + // The macros are already tested in `nalgebra-macros`. Here we just test that they compile fine. + + let _ = matrix![1, 2, 3; 4, 5, 6]; + let _ = dmatrix![1, 2, 3; 4, 5, 6]; + let _ = point![1, 2, 3, 4, 5, 6]; + let _ = vector![1, 2, 3, 4, 5, 6]; + let _ = dvector![1, 2, 3, 4, 5, 6]; +} diff --git a/tests/core/mod.rs b/tests/core/mod.rs index 4f9bc745e..c03461dc4 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -16,3 +16,6 @@ mod matrixcompare; #[cfg(feature = "arbitrary")] pub mod helper; + +#[cfg(feature = "macros")] +mod macros; diff --git a/tests/core/serde.rs b/tests/core/serde.rs index 0a3be7e99..34429eeb0 100644 --- a/tests/core/serde.rs +++ b/tests/core/serde.rs @@ -1,8 +1,8 @@ #![cfg(feature = "serde-serialize")] use na::{ - DMatrix, Isometry2, Isometry3, IsometryMatrix2, IsometryMatrix3, Matrix3x4, Point2, Point3, - Quaternion, Rotation2, Rotation3, Similarity2, Similarity3, SimilarityMatrix2, + DMatrix, Isometry2, Isometry3, IsometryMatrix2, IsometryMatrix3, Matrix2x3, Matrix3x4, Point2, + Point3, Quaternion, Rotation2, Rotation3, Similarity2, Similarity3, SimilarityMatrix2, SimilarityMatrix3, Translation2, Translation3, Unit, Vector2, }; use rand; @@ -27,6 +27,32 @@ fn serde_dmatrix() { let serialized = serde_json::to_string(&v).unwrap(); let deserialized: DMatrix = serde_json::from_str(&serialized).unwrap(); assert_eq!(v, deserialized); + + let m = DMatrix::from_column_slice(2, 3, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); + let mat_str = "[[1.0, 2.0, 3.0, 4.0, 5.0, 6.0],2,3]"; + let deserialized: DMatrix = serde_json::from_str(&mat_str).unwrap(); + assert_eq!(m, deserialized); + + let m = Matrix2x3::from_column_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); + let mat_str = "[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]"; + let deserialized: Matrix2x3 = serde_json::from_str(&mat_str).unwrap(); + assert_eq!(m, deserialized); +} + +#[test] +#[should_panic] +fn serde_dmatrix_invalid_len() { + // This must fail: we attempt to deserialize a 2x3 with only 5 elements. + let mat_str = "[[1.0, 2.0, 3.0, 4.0, 5.0],2,3]"; + let _: DMatrix = serde_json::from_str(&mat_str).unwrap(); +} + +#[test] +#[should_panic] +fn serde_smatrix_invalid_len() { + // This must fail: we attempt to deserialize a 2x3 with only 5 elements. + let mat_str = "[1.0, 2.0, 3.0, 4.0, 5.0]"; + let _: Matrix2x3 = serde_json::from_str(&mat_str).unwrap(); } test_serde!( diff --git a/tests/lib.rs b/tests/lib.rs index 8ee85f075..20d38d7a5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,7 +1,12 @@ -#[cfg(not(all(feature = "debug", feature = "compare", feature = "rand")))] +#[cfg(not(all( + feature = "debug", + feature = "compare", + feature = "rand", + feature = "macros" +)))] compile_error!( - "Please enable the `debug`, `compare`, and `rand` features in order to compile and run the tests. - Example: `cargo test --features debug,compare,rand`" + "Please enable the `debug`, `compare`, `rand` and `macros` features in order to compile and run the tests. + Example: `cargo test --features debug,compare,rand,macros`" ); #[cfg(feature = "abomonation-serialize")]