diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bae7fa8..d76638f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,18 +7,53 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [0.17.0] - WIP ### Added - * Add swizzling up to dimension 3. For example, you can do `v.zxy()` as an equivalent to `Vector3::new(v.z, v.x, v.w)`. + * Add swizzling up to dimension 3 for vectors. For example, you can do `v.zxy()` as an equivalent to `Vector3::new(v.z, v.x, v.y)`. + * Add swizzling up to dimension 3 for points. For example, you can do `p.zxy()` as an equivalent to `Point3::new(p.z, p.x, p.y)`. * Add `.copy_from_slice` to copy matrix components from a slice in column-major order. * Add `.dot` to quaternions. * Add `.zip_zip_map` for iterating on three matrices simultaneously, and applying a closure to them. * Add `.slerp` and `.try_slerp` to unit vectors. + * Add `.lerp` to vectors. * Add `.to_projective` and `.as_projective` to `Perspective3` and `Orthographic3` in order to use them as `Projective3` structures. * Add `From/Into` impls to allow the conversion of any transformation type to a matrix. * Add `Into` impls to convert a matrix slice into an owned matrix. * Add `Point*::from_slice` to create a point from a slice. - * Add `.map_with_location` to matrices to apply a map which passes the component indices to the user-defined closure alongide + * Add `.map_with_location` to matrices to apply a map which passes the component indices to the user-defined closure alongside the component itself. + * Add impl `From` for `Point`. + * Add impl `From` for `Quaternion`. + * Add impl `From` for `Translation`. + * Add the `::from_vec` constructor to construct a matrix from a `Vec` (a `DMatrix` will reuse the original `Vec` + as-is for its storage). + * Add `.to_homogeneous` to square matrices (and with dimensions higher than 1x1). This will increase their number of row + and columns by 1. The new column and row are filled with 0, except for the diagonal element which is set to 1. + * Implement `Extend` for matrices with a dynamic storage. The provided `Vec` is assumed to represent a column-major + matrix with the same number of rows as the one being extended. This will effectively append new columns on the right of + the matrix being extended. + * Implement `Extend` for vectors with a dynamic storage. This will concatenate the vector with the given `Vec`. + * Implement `Extend>` for matrices with dynamic storage. This will concatenate the columns of both matrices. + * Implement `Into` for the `MatrixVec` storage. + * Implement `Hash` for all matrices. + * Add a `.len()` method to retrieve the size of a `MatrixVec`. + +### Modified + * The orthographic projection no longer require that `bottom < top`, that `left < right`, and that `znear < zfar`. The + only restriction now ith that they must not be equal (in which case the projection would be singular). + * The `Point::from_coordinates` methods is deprecated. Use `Point::from` instead. + * The `.transform_point` and `.transform_vector` methods are now inherent methods for matrices so that the user does not have to + explicitly import the `Transform` trait from the alga crate. + * Renamed the matrix storage types: `MatrixArray` -> `ArrayStorage` and `MatrixVec` -> `VecStorage`. + * Renamed `.unwrap()` to `.into_inner()` for geometric types that wrap another type. + This is for the case of `Unit`, `Transform`, `Orthographic3`, `Perspective3`, `Rotation`. + * Deprecate several functions at the root of the crate (replaced by methods). + +### Removed + * Remove the `Deref` impl for `MatrixVec` as it could cause hard-to-understand compilation errors. + +### nalgebra-glm + * Add several alternative projection computations, e.g., `ortho_lh`, `ortho_lh_no`, `perspective_lh`, etc. + * Add features matching those of nalgebra, in particular: `serde-serialize`, `abmonation-serialize`, std` (enabled by default). ## [0.16.0] All dependencies have been updated to their latest versions. diff --git a/Cargo.toml b/Cargo.toml index 7e2e21dd..048ec557 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra" -version = "0.16.12" +version = "0.16.13" authors = [ "Sébastien Crozet " ] description = "Linear algebra library with transformations and statically-sized or dynamically-sized matrices." @@ -30,23 +30,24 @@ io = [ "pest", "pest_derive" ] [dependencies] typenum = "1.10" -generic-array = "0.11" -rand = { version = "0.5", default-features = false } +generic-array = "0.12" +rand = { version = "0.6", default-features = false } num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.2", default-features = false } approx = { version = "0.3", default-features = false } alga = { version = "0.7", default-features = false } -matrixmultiply = { version = "0.1", optional = true } +matrixmultiply = { version = "0.2", optional = true } serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } -abomonation = { version = "0.5", optional = true } +abomonation = { version = "0.7", optional = true } mint = { version = "0.5", optional = true } -quickcheck = { version = "0.6", optional = true } +quickcheck = { version = "0.7", optional = true } pest = { version = "2.0", optional = true } pest_derive = { version = "2.0", optional = true } [dev-dependencies] serde_json = "1.0" +rand_xorshift = "0.1" [workspace] members = [ "nalgebra-lapack", "nalgebra-glm" ] diff --git a/README.md b/README.md index a4c84a68..17a312be 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ crates.io

+ + + Build status diff --git a/benches/common/macros.rs b/benches/common/macros.rs index 55d6ba3c..758336a8 100644 --- a/benches/common/macros.rs +++ b/benches/common/macros.rs @@ -4,7 +4,8 @@ macro_rules! bench_binop( ($name: ident, $t1: ty, $t2: ty, $binop: ident) => { #[bench] fn $name(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let a = rng.gen::<$t1>(); let b = rng.gen::<$t2>(); @@ -19,7 +20,8 @@ macro_rules! bench_binop_ref( ($name: ident, $t1: ty, $t2: ty, $binop: ident) => { #[bench] fn $name(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let a = rng.gen::<$t1>(); let b = rng.gen::<$t2>(); @@ -34,7 +36,8 @@ macro_rules! bench_binop_fn( ($name: ident, $t1: ty, $t2: ty, $binop: path) => { #[bench] fn $name(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let a = rng.gen::<$t1>(); let b = rng.gen::<$t2>(); @@ -51,7 +54,8 @@ macro_rules! bench_unop_na( fn $name(bh: &mut Bencher) { const LEN: usize = 1 << 13; - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let elems: Vec<$t> = (0usize .. LEN).map(|_| rng.gen::<$t>()).collect(); let mut i = 0; @@ -73,7 +77,8 @@ macro_rules! bench_unop( fn $name(bh: &mut Bencher) { const LEN: usize = 1 << 13; - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut elems: Vec<$t> = (0usize .. LEN).map(|_| rng.gen::<$t>()).collect(); let mut i = 0; @@ -95,7 +100,8 @@ macro_rules! bench_construction( fn $name(bh: &mut Bencher) { const LEN: usize = 1 << 13; - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); $(let $args: Vec<$types> = (0usize .. LEN).map(|_| rng.gen::<$types>()).collect();)* let mut i = 0; diff --git a/benches/core/vector.rs b/benches/core/vector.rs index 35e25e2d..837eb7ca 100644 --- a/benches/core/vector.rs +++ b/benches/core/vector.rs @@ -50,7 +50,8 @@ bench_binop_ref!(vec10000_dot_f32, VectorN, VectorN, d #[bench] fn vec10000_axpy_f64(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); @@ -60,7 +61,8 @@ fn vec10000_axpy_f64(bh: &mut Bencher) { #[bench] fn vec10000_axpy_beta_f64(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); @@ -71,7 +73,8 @@ fn vec10000_axpy_beta_f64(bh: &mut Bencher) { #[bench] fn vec10000_axpy_f64_slice(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); @@ -86,7 +89,8 @@ fn vec10000_axpy_f64_slice(bh: &mut Bencher) { #[bench] fn vec10000_axpy_f64_static(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = VectorN::::new_random(); let b = VectorN::::new_random(); let n = rng.gen::(); @@ -97,7 +101,8 @@ fn vec10000_axpy_f64_static(bh: &mut Bencher) { #[bench] fn vec10000_axpy_f32(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); @@ -107,7 +112,8 @@ fn vec10000_axpy_f32(bh: &mut Bencher) { #[bench] fn vec10000_axpy_beta_f32(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); diff --git a/benches/lib.rs b/benches/lib.rs index 1ad3a2be..f788c998 100644 --- a/benches/lib.rs +++ b/benches/lib.rs @@ -14,6 +14,7 @@ mod geometry; mod linalg; fn reproductible_dmatrix(nrows: usize, ncols: usize) -> DMatrix { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); DMatrix::::from_fn(nrows, ncols, |_, _| rng.gen()) } diff --git a/examples/linear_system_resolution.rs b/examples/linear_system_resolution.rs new file mode 100644 index 00000000..2d0dc183 --- /dev/null +++ b/examples/linear_system_resolution.rs @@ -0,0 +1,39 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] +#[macro_use] +extern crate approx; // for assert_relative_eq +extern crate nalgebra as na; +use na::{Matrix4, Matrix4x3, Vector4}; + +fn main() { + let a = Matrix4::new( + 1.0, 1.0, 2.0, -5.0, + 2.0, 5.0, -1.0, -9.0, + 2.0, 1.0, -1.0, 3.0, + 1.0, 3.0, 2.0, 7.0, + ); + let mut b = Vector4::new(3.0, -3.0, -11.0, -5.0); + let decomp = a.lu(); + let x = decomp.solve(&b).expect("Linear resolution failed."); + assert_relative_eq!(a * x, b); + + /* + * It is possible to perform the resolution in-place. + * This is particularly useful to avoid allocations when + * `b` is a `DVector` or a `DMatrix`. + */ + assert!(decomp.solve_mut(&mut b), "Linear resolution failed."); + assert_relative_eq!(x, b); + + /* + * It is possible to solve multiple systems + * simultaneously by using a matrix for `b`. + */ + let b = Matrix4x3::new( + 3.0, 2.0, 0.0, + -3.0, 0.0, 0.0, + -11.0, 5.0, -3.0, + -5.0, 10.0, 4.0, + ); + let x = decomp.solve(&b).expect("Linear resolution failed."); + assert_relative_eq!(a * x, b); +} diff --git a/examples/point_construction.rs b/examples/point_construction.rs index 7fc32838..19430997 100644 --- a/examples/point_construction.rs +++ b/examples/point_construction.rs @@ -8,7 +8,7 @@ fn main() { // Build from a coordinates vector. let coords = Vector3::new(2.0, 3.0, 4.0); - let p1 = Point3::from_coordinates(coords); + let p1 = Point3::from(coords); // Build by translating the origin. let translation = Vector3::new(2.0, 3.0, 4.0); diff --git a/examples/transform_matrix4.rs b/examples/transform_matrix4.rs index c877206e..a9e6cd6e 100644 --- a/examples/transform_matrix4.rs +++ b/examples/transform_matrix4.rs @@ -3,7 +3,6 @@ extern crate alga; extern crate approx; extern crate nalgebra as na; -use alga::linear::Transformation; use na::{Matrix4, Point3, Vector3}; fn main() { diff --git a/examples/transform_vector_point3.rs b/examples/transform_vector_point3.rs index cd36cad1..57a189cc 100644 --- a/examples/transform_vector_point3.rs +++ b/examples/transform_vector_point3.rs @@ -1,7 +1,6 @@ extern crate alga; extern crate nalgebra as na; -use alga::linear::Transformation; use na::{Matrix4, Point3, Vector3, Vector4}; fn main() { diff --git a/nalgebra-glm/Cargo.toml b/nalgebra-glm/Cargo.toml index 8e8b7130..0ba77cd6 100644 --- a/nalgebra-glm/Cargo.toml +++ b/nalgebra-glm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-glm" -version = "0.1.3" +version = "0.2.1" authors = ["sebcrozet "] description = "A computer-graphics oriented API for nalgebra, inspired by the C++ GLM library." @@ -12,8 +12,16 @@ categories = [ "science" ] keywords = [ "linear", "algebra", "matrix", "vector", "math" ] license = "BSD-3-Clause" +[features] +default = [ "std" ] +std = [ "nalgebra/std", "alga/std" ] +stdweb = [ "nalgebra/stdweb" ] +arbitrary = [ "nalgebra/arbitrary" ] +serde-serialize = [ "nalgebra/serde-serialize" ] +abomonation-serialize = [ "nalgebra/abomonation-serialize" ] + [dependencies] num-traits = { version = "0.2", default-features = false } approx = { version = "0.3", default-features = false } -alga = "0.7" -nalgebra = { path = "..", version = "^0.16.4" } +alga = { version = "0.7", default-features = false } +nalgebra = { path = "..", version = "^0.16.13", default-features = false } diff --git a/nalgebra-glm/src/common.rs b/nalgebra-glm/src/common.rs index e6a3dc0f..021ac3ef 100644 --- a/nalgebra-glm/src/common.rs +++ b/nalgebra-glm/src/common.rs @@ -315,13 +315,151 @@ where DefaultAllocator: Alloc { // x * (exp).exp2() //} -/// Returns `x * (1.0 - a) + y * a`, i.e., the linear blend of x and y using the floating-point value a. +/// Returns `x * (1.0 - a) + y * a`, i.e., the linear blend of the scalars x and y using the scalar value a. /// /// The value for a is not restricted to the range `[0, 1]`. -pub fn mix(x: N, y: N, a: N) -> N { +/// +/// # Examples: +/// +/// ``` +/// # use nalgebra_glm as glm; +/// assert_eq!(glm::mix_scalar(2.0, 20.0, 0.1), 3.8); +/// ``` +/// +/// # See also: +/// +/// * [`mix`](fn.mix.html) +/// * [`mix_vec`](fn.mix_vec.html) +pub fn mix_scalar(x: N, y: N, a: N) -> N { x * (N::one() - a) + y * a } +/// Returns `x * (1.0 - a) + y * a`, i.e., the linear blend of the vectors x and y using the scalar value a. +/// +/// The value for a is not restricted to the range `[0, 1]`. +/// +/// # Examples: +/// +/// ``` +/// # use nalgebra_glm as glm; +/// let x = glm::vec3(1.0, 2.0, 3.0); +/// let y = glm::vec3(10.0, 20.0, 30.0); +/// assert_eq!(glm::mix(&x, &y, 0.1), glm::vec3(1.9, 3.8, 5.7)); +/// ``` +/// +/// # See also: +/// +/// * [`mix_scalar`](fn.mix_scalar.html) +/// * [`mix_vec`](fn.mix_vec.html) +pub fn mix(x: &TVec, y: &TVec, a: N) -> TVec +where DefaultAllocator: Alloc { + x * (N::one() - a) + y * a +} + +/// Returns `x * (1.0 - a) + y * a`, i.e., the component-wise linear blend of `x` and `y` using the components of +/// the vector `a` as coefficients. +/// +/// The value for a is not restricted to the range `[0, 1]`. +/// +/// # Examples: +/// +/// ``` +/// # use nalgebra_glm as glm; +/// let x = glm::vec3(1.0, 2.0, 3.0); +/// let y = glm::vec3(10.0, 20.0, 30.0); +/// let a = glm::vec3(0.1, 0.2, 0.3); +/// assert_eq!(glm::mix_vec(&x, &y, &a), glm::vec3(1.9, 5.6, 11.1)); +/// ``` +/// +/// # See also: +/// +/// * [`mix_scalar`](fn.mix_scalar.html) +/// * [`mix`](fn.mix.html) +pub fn mix_vec( + x: &TVec, + y: &TVec, + a: &TVec, +) -> TVec +where + DefaultAllocator: Alloc, +{ + x.component_mul(&(TVec::::repeat(N::one()) - a)) + y.component_mul(&a) +} + +/// Returns `x * (1.0 - a) + y * a`, i.e., the linear blend of the scalars x and y using the scalar value a. +/// +/// The value for a is not restricted to the range `[0, 1]`. +/// This is an alias for `mix_scalar`. +/// +/// # Examples: +/// +/// ``` +/// # use nalgebra_glm as glm; +/// assert_eq!(glm::lerp_scalar(2.0, 20.0, 0.1), 3.8); +/// ``` +/// +/// # See also: +/// +/// * [`lerp`](fn.lerp.html) +/// * [`lerp_vec`](fn.lerp_vec.html) +pub fn lerp_scalar(x: N, y: N, a: N) -> N { + mix_scalar(x, y, a) +} + +/// Returns `x * (1.0 - a) + y * a`, i.e., the linear blend of the vectors x and y using the scalar value a. +/// +/// The value for a is not restricted to the range `[0, 1]`. +/// This is an alias for `mix`. +/// +/// # Examples: +/// +/// ``` +/// # use nalgebra_glm as glm; +/// let x = glm::vec3(1.0, 2.0, 3.0); +/// let y = glm::vec3(10.0, 20.0, 30.0); +/// assert_eq!(glm::lerp(&x, &y, 0.1), glm::vec3(1.9, 3.8, 5.7)); +/// ``` +/// +/// # See also: +/// +/// * [`lerp_scalar`](fn.lerp_scalar.html) +/// * [`lerp_vec`](fn.lerp_vec.html) +pub fn lerp(x: &TVec, y: &TVec, a: N) -> TVec +where DefaultAllocator: Alloc { + mix(x, y, a) +} + +/// Returns `x * (1.0 - a) + y * a`, i.e., the component-wise linear blend of `x` and `y` using the components of +/// the vector `a` as coefficients. +/// +/// The value for a is not restricted to the range `[0, 1]`. +/// This is an alias for `mix_vec`. +/// +/// # Examples: +/// +/// ``` +/// # use nalgebra_glm as glm; +/// let x = glm::vec3(1.0, 2.0, 3.0); +/// let y = glm::vec3(10.0, 20.0, 30.0); +/// let a = glm::vec3(0.1, 0.2, 0.3); +/// assert_eq!(glm::lerp_vec(&x, &y, &a), glm::vec3(1.9, 5.6, 11.1)); +/// ``` +/// +/// # See also: +/// +/// * [`lerp_scalar`](fn.lerp_scalar.html) +/// * [`lerp`](fn.lerp.html) +pub fn lerp_vec( + x: &TVec, + y: &TVec, + a: &TVec, +) -> TVec +where + DefaultAllocator: Alloc, +{ + mix_vec(x, y, a) +} + /// Component-wise modulus. /// /// Returns `x - y * floor(x / y)` for each component in `x` using the corresponding component of `y`. diff --git a/nalgebra-glm/src/constructors.rs b/nalgebra-glm/src/constructors.rs index 210a10d4..1651dd20 100644 --- a/nalgebra-glm/src/constructors.rs +++ b/nalgebra-glm/src/constructors.rs @@ -1,8 +1,9 @@ -use aliases::{ - Qua, TMat, TMat2, TMat2x3, TMat2x4, TMat3, TMat3x2, TMat3x4, TMat4, TMat4x2, TMat4x3, TVec1, - TVec2, TVec3, TVec4, -}; -use na::{Real, Scalar, U2, U3, U4}; +#![cfg_attr(rustfmt, rustfmt_skip)] + +use na::{Scalar, Real, U2, U3, U4}; +use aliases::{TMat, Qua, TVec1, TVec2, TVec3, TVec4, TMat2, TMat2x3, TMat2x4, TMat3, TMat3x2, TMat3x4, + TMat4, TMat4x2, TMat4x3}; + /// Creates a new 1D vector. /// @@ -33,173 +34,136 @@ pub fn vec4(x: N, y: N, z: N, w: N) -> TVec4 { TVec4::new(x, y, z, w) } + /// Create a new 2x2 matrix. -pub fn mat2(m11: N, m12: N, m21: N, m22: N) -> TMat2 { - TMat::::new(m11, m12, m21, m22) +pub fn mat2(m11: N, m12: N, + m21: N, m22: N) -> TMat2 { + TMat::::new( + m11, m12, + m21, m22, + ) } /// Create a new 2x2 matrix. -pub fn mat2x2(m11: N, m12: N, m21: N, m22: N) -> TMat2 { - TMat::::new(m11, m12, m21, m22) +pub fn mat2x2(m11: N, m12: N, + m21: N, m22: N) -> TMat2 { + TMat::::new( + m11, m12, + m21, m22, + ) } /// Create a new 2x3 matrix. -pub fn mat2x3(m11: N, m12: N, m13: N, m21: N, m22: N, m23: N) -> TMat2x3 { - TMat::::new(m11, m12, m13, m21, m22, m23) +pub fn mat2x3(m11: N, m12: N, m13: N, + m21: N, m22: N, m23: N) -> TMat2x3 { + TMat::::new( + m11, m12, m13, + m21, m22, m23, + ) } /// Create a new 2x4 matrix. -pub fn mat2x4( - m11: N, - m12: N, - m13: N, - m14: N, - m21: N, - m22: N, - m23: N, - m24: N, -) -> TMat2x4 -{ - TMat::::new(m11, m12, m13, m14, m21, m22, m23, m24) +pub fn mat2x4(m11: N, m12: N, m13: N, m14: N, + m21: N, m22: N, m23: N, m24: N) -> TMat2x4 { + TMat::::new( + m11, m12, m13, m14, + m21, m22, m23, m24, + ) } /// Create a new 3x3 matrix. -pub fn mat3( - m11: N, - m12: N, - m13: N, - m21: N, - m22: N, - m23: N, - m31: N, - m32: N, - m33: N, -) -> TMat3 -{ - TMat::::new(m11, m12, m13, m21, m22, m23, m31, m32, m33) +pub fn mat3(m11: N, m12: N, m13: N, + m21: N, m22: N, m23: N, + m31: N, m32: N, m33: N) -> TMat3 { + TMat::::new( + m11, m12, m13, + m21, m22, m23, + m31, m32, m33, + ) } /// Create a new 3x2 matrix. -pub fn mat3x2(m11: N, m12: N, m21: N, m22: N, m31: N, m32: N) -> TMat3x2 { - TMat::::new(m11, m12, m21, m22, m31, m32) +pub fn mat3x2(m11: N, m12: N, + m21: N, m22: N, + m31: N, m32: N) -> TMat3x2 { + TMat::::new( + m11, m12, + m21, m22, + m31, m32, + ) } /// Create a new 3x3 matrix. -pub fn mat3x3( - m11: N, - m12: N, - m13: N, - m21: N, - m22: N, - m23: N, - m31: N, - m32: N, - m33: N, -) -> TMat3 -{ - TMat::::new(m11, m12, m13, m31, m32, m33, m21, m22, m23) +pub fn mat3x3(m11: N, m12: N, m13: N, + m21: N, m22: N, m23: N, + m31: N, m32: N, m33: N) -> TMat3 { + TMat::::new( + m11, m12, m13, + m31, m32, m33, + m21, m22, m23, + ) } /// Create a new 3x4 matrix. -pub fn mat3x4( - m11: N, - m12: N, - m13: N, - m14: N, - m21: N, - m22: N, - m23: N, - m24: N, - m31: N, - m32: N, - m33: N, - m34: N, -) -> TMat3x4 -{ - TMat::::new(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34) +pub fn mat3x4(m11: N, m12: N, m13: N, m14: N, + m21: N, m22: N, m23: N, m24: N, + m31: N, m32: N, m33: N, m34: N) -> TMat3x4 { + TMat::::new( + m11, m12, m13, m14, + m21, m22, m23, m24, + m31, m32, m33, m34, + ) } /// Create a new 4x2 matrix. -pub fn mat4x2( - m11: N, - m12: N, - m21: N, - m22: N, - m31: N, - m32: N, - m41: N, - m42: N, -) -> TMat4x2 -{ - TMat::::new(m11, m12, m21, m22, m31, m32, m41, m42) +pub fn mat4x2(m11: N, m12: N, + m21: N, m22: N, + m31: N, m32: N, + m41: N, m42: N) -> TMat4x2 { + TMat::::new( + m11, m12, + m21, m22, + m31, m32, + m41, m42, + ) } /// Create a new 4x3 matrix. -pub fn mat4x3( - m11: N, - m12: N, - m13: N, - m21: N, - m22: N, - m23: N, - m31: N, - m32: N, - m33: N, - m41: N, - m42: N, - m43: N, -) -> TMat4x3 -{ - TMat::::new(m11, m12, m13, m21, m22, m23, m31, m32, m33, m41, m42, m43) -} - -/// Create a new 4x4 matrix. -pub fn mat4x4( - m11: N, - m12: N, - m13: N, - m14: N, - m21: N, - m22: N, - m23: N, - m24: N, - m31: N, - m32: N, - m33: N, - m34: N, - m41: N, - m42: N, - m43: N, - m44: N, -) -> TMat4 -{ - TMat::::new( - m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44, +pub fn mat4x3(m11: N, m12: N, m13: N, + m21: N, m22: N, m23: N, + m31: N, m32: N, m33: N, + m41: N, m42: N, m43: N) -> TMat4x3 { + TMat::::new( + m11, m12, m13, + m21, m22, m23, + m31, m32, m33, + m41, m42, m43, ) } /// Create a new 4x4 matrix. -pub fn mat4( - m11: N, - m12: N, - m13: N, - m14: N, - m21: N, - m22: N, - m23: N, - m24: N, - m31: N, - m32: N, - m33: N, - m34: N, - m41: N, - m42: N, - m43: N, - m44: N, -) -> TMat4 -{ +pub fn mat4x4(m11: N, m12: N, m13: N, m14: N, + m21: N, m22: N, m23: N, m24: N, + m31: N, m32: N, m33: N, m34: N, + m41: N, m42: N, m43: N, m44: N) -> TMat4 { TMat::::new( - m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44, + m11, m12, m13, m14, + m21, m22, m23, m24, + m31, m32, m33, m34, + m41, m42, m43, m44, + ) +} + +/// Create a new 4x4 matrix. +pub fn mat4(m11: N, m12: N, m13: N, m14: N, + m21: N, m22: N, m23: N, m24: N, + m31: N, m32: N, m33: N, m34: N, + m41: N, m42: N, m43: N, m44: N) -> TMat4 { + TMat::::new( + m11, m12, m13, m14, + m21, m22, m23, m24, + m31, m32, m33, m34, + m41, m42, m43, m44, ) } diff --git a/nalgebra-glm/src/ext/matrix_clip_space.rs b/nalgebra-glm/src/ext/matrix_clip_space.rs index 89c137ea..02179759 100644 --- a/nalgebra-glm/src/ext/matrix_clip_space.rs +++ b/nalgebra-glm/src/ext/matrix_clip_space.rs @@ -1,5 +1,5 @@ use aliases::TMat4; -use na::{Orthographic3, Perspective3, Real}; +use na::{Real}; //pub fn frustum(left: N, right: N, bottom: N, top: N, near: N, far: N) -> TMat4 { // unimplemented!() @@ -53,119 +53,644 @@ use na::{Orthographic3, Perspective3, Real}; // unimplemented!() //} -/// Creates a matrix for an orthographic parallel viewing volume, using the right handedness and OpenGL near and far clip planes definition. +/// Creates a matrix for a right hand orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// pub fn ortho(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { - Orthographic3::new(left, right, bottom, top, znear, zfar).unwrap() + ortho_rh_no(left, right, bottom, top, znear, zfar) } -//pub fn ortho_lh(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_lh_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_lh_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_rh(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_rh_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_rh_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} +/// Creates a left hand matrix for a orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_lh(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + ortho_lh_no(left, right, bottom, top, znear, zfar) +} -/// Creates a matrix for a perspective-view frustum based on the right handedness and OpenGL near and far clip planes definition. +/// Creates a left hand matrix for a orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_lh_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + let two : N = ::convert(2.0); + let mut mat : TMat4 = TMat4::::identity(); + + mat[(0, 0)] = two / (right - left); + mat[(0, 3)] = -(right + left) / (right - left); + mat[(1, 1)] = two / (top-bottom); + mat[(1, 3)] = -(top + bottom) / (top - bottom); + mat[(2, 2)] = two / (zfar - znear); + mat[(2, 3)] = -(zfar + znear) / (zfar - znear); + + mat +} + +/// Creates a matrix for a left hand orthographic-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_lh_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + let one : N = N::one(); + let two : N = ::convert(2.0); + let mut mat : TMat4 = TMat4::::identity(); + + mat[(0, 0)] = two / (right - left); + mat[(0, 3)] = - (right + left) / (right - left); + mat[(1, 1)] = two / (top - bottom); + mat[(1, 3)] = - (top + bottom) / (top - bottom); + mat[(2, 2)] = one / (zfar - znear); + mat[(2, 3)] = - znear / (zfar - znear); + + mat +} + +/// Creates a matrix for a right hand orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + ortho_rh_no(left, right, bottom, top, znear, zfar) +} + +/// Creates a matrix for a right hand orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_rh(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + ortho_rh_no(left, right, bottom, top, znear, zfar) +} + +/// Creates a matrix for a right hand orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_rh_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + let two : N = ::convert(2.0); + let mut mat : TMat4 = TMat4::::identity(); + + mat[(0, 0)] = two / (right - left); + mat[(0, 3)] = - (right + left) / (right - left); + mat[(1, 1)] = two/(top-bottom); + mat[(1, 3)] = - (top + bottom) / (top - bottom); + mat[(2, 2)] = - two / (zfar - znear); + mat[(2, 3)] = - (zfar + znear) / (zfar - znear); + + mat +} + +/// Creates a right hand matrix for a orthographic-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_rh_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + let one : N = N::one(); + let two : N = ::convert(2.0); + let mut mat : TMat4 = TMat4::::identity(); + + mat[(0, 0)] = two / (right - left); + mat[(0, 3)] = - (right + left) / (right - left); + mat[(1, 1)] = two/(top-bottom); + mat[(1, 3)] = - (top + bottom) / (top - bottom); + mat[(2, 2)] = - one / (zfar - znear); + mat[(2, 3)] = - znear / (zfar - znear); + + mat +} + +/// Creates a right hand matrix for a orthographic-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + ortho_rh_zo(left, right, bottom, top, znear, zfar) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_rh_no(fov, width, height, near, far) +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_lh(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_lh_no(fov, width, height, near, far) +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_lh_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + assert!( + width > N::zero(), + "The width must be greater than zero" + ); + assert!( + height > N::zero(), + "The height must be greater than zero." + ); + assert!( + fov > N::zero(), + "The fov must be greater than zero" + ); + + let mut mat = TMat4::zeros(); + + let rad = fov; + let h = (rad * ::convert(0.5)).cos() / (rad * ::convert(0.5)).sin(); + let w = h * height / width; + + mat[(0, 0)] = w; + mat[(1, 1)] = h; + mat[(2, 2)] = (far + near) / (far - near); + mat[(2, 3)] = - (far * near * ::convert(2.0)) / (far - near); + mat[(3, 2)] = N::one(); + + mat +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_lh_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + assert!( + width > N::zero(), + "The width must be greater than zero" + ); + assert!( + height > N::zero(), + "The height must be greater than zero." + ); + assert!( + fov > N::zero(), + "The fov must be greater than zero" + ); + + let mut mat = TMat4::zeros(); + + let rad = fov; + let h = (rad * ::convert(0.5)).cos() / (rad * ::convert(0.5)).sin(); + let w = h * height / width; + + mat[(0, 0)] = w; + mat[(1, 1)] = h; + mat[(2, 2)] = far / (far - near); + mat[(2, 3)] = -(far * near) / (far - near); + mat[(3, 2)] = N::one(); + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_rh_no(fov, width, height, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_rh(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_rh_no(fov, width, height, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_rh_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + assert!( + width > N::zero(), + "The width must be greater than zero" + ); + assert!( + height > N::zero(), + "The height must be greater than zero." + ); + assert!( + fov > N::zero(), + "The fov must be greater than zero" + ); + + let mut mat = TMat4::zeros(); + + let rad = fov; + let h = (rad * ::convert(0.5)).cos() / (rad * ::convert(0.5)).sin(); + let w = h * height / width; + + mat[(0, 0)] = w; + mat[(1, 1)] = h; + mat[(2, 2)] = - (far + near) / (far - near); + mat[(2, 3)] = - (far * near * ::convert(2.0)) / (far - near); + mat[(3, 2)] = -N::one(); + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_rh_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + assert!( + width > N::zero(), + "The width must be greater than zero" + ); + assert!( + height > N::zero(), + "The height must be greater than zero." + ); + assert!( + fov > N::zero(), + "The fov must be greater than zero" + ); + + let mut mat = TMat4::zeros(); + + let rad = fov; + let h = (rad * ::convert(0.5)).cos() / (rad * ::convert(0.5)).sin(); + let w = h * height / width; + + mat[(0, 0)] = w; + mat[(1, 1)] = h; + mat[(2, 2)] = far / (near - far); + mat[(2, 3)] = -(far * near) / (far - near); + mat[(3, 2)] = -N::one(); + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_rh_zo(fov, width, height, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane /// /// # Important note /// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. pub fn perspective(aspect: N, fovy: N, near: N, far: N) -> TMat4 { - Perspective3::new(aspect, fovy, near, far).unwrap() + // TODO: Breaking change - revert back to proper glm conventions? + // + // Prior to changes to support configuring the behaviour of this function it was simply + // a wrapper around Perspective3::new(). The argument order for that function is different + // than the glm convention, but reordering the arguments would've caused pointlessly + // un-optimal code to be generated so they were rearranged so the function would just call + // straight through. + // + // Now this call to Perspective3::new() is no longer made so the functions can have their + // arguments reordered to the glm convention. Unfortunately this is a breaking change so + // can't be cleanly integrated into the existing library version without breaking other + // people's code. Reordering to glm isn't a huge deal but if it is done it will have to be + // in a major API breaking update. + // + perspective_rh_no(aspect, fovy, near, far) +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_lh(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + perspective_lh_no(aspect, fovy, near, far) +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_lh_no(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + assert!( + !relative_eq!(far - near, N::zero()), + "The near-plane and far-plane must not be superimposed." + ); + assert!( + !relative_eq!(aspect, N::zero()), + "The apsect ratio must not be zero." + ); + + let one = N::one(); + let two: N = ::convert( 2.0); + let mut mat : TMat4 = TMat4::zeros(); + + let tan_half_fovy = (fovy / two).tan(); + + mat[(0, 0)] = one / (aspect * tan_half_fovy); + mat[(1, 1)] = one / tan_half_fovy; + mat[(2, 2)] = (far + near) / (far - near); + mat[(2, 3)] = -(two * far * near) / (far - near); + mat[(3, 2)] = one; + + mat +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_lh_zo(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + assert!( + !relative_eq!(far - near, N::zero()), + "The near-plane and far-plane must not be superimposed." + ); + assert!( + !relative_eq!(aspect, N::zero()), + "The apsect ratio must not be zero." + ); + + let one = N::one(); + let two: N = ::convert( 2.0); + let mut mat: TMat4 = TMat4::zeros(); + + let tan_half_fovy = (fovy / two).tan(); + + mat[(0, 0)] = one / (aspect * tan_half_fovy); + mat[(1, 1)] = one / tan_half_fovy; + mat[(2, 2)] = far / (far - near); + mat[(2, 3)] = -(far * near) / (far - near); + mat[(3, 2)] = one; + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_no(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + perspective_rh_no(aspect, fovy, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_rh(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + perspective_rh_no(aspect, fovy, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_rh_no(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + assert!( + !relative_eq!(far - near, N::zero()), + "The near-plane and far-plane must not be superimposed." + ); + assert!( + !relative_eq!(aspect, N::zero()), + "The apsect ratio must not be zero." + ); + + let negone = -N::one(); + let one = N::one(); + let two: N = ::convert( 2.0); + let mut mat = TMat4::zeros(); + + let tan_half_fovy = (fovy / two).tan(); + + mat[(0, 0)] = one / (aspect * tan_half_fovy); + mat[(1, 1)] = one / tan_half_fovy; + mat[(2, 2)] = - (far + near) / (far - near); + mat[(2, 3)] = -(two * far * near) / (far - near); + mat[(3, 2)] = negone; + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_rh_zo(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + assert!( + !relative_eq!(far - near, N::zero()), + "The near-plane and far-plane must not be superimposed." + ); + assert!( + !relative_eq!(aspect, N::zero()), + "The apsect ratio must not be zero." + ); + + let negone = -N::one(); + let one = N::one(); + let two = ::convert( 2.0); + let mut mat = TMat4::zeros(); + + let tan_half_fovy = (fovy / two).tan(); + + mat[(0, 0)] = one / (aspect * tan_half_fovy); + mat[(1, 1)] = one / tan_half_fovy; + mat[(2, 2)] = far / (near - far); + mat[(2, 3)] = -(far * near) / (far - near); + mat[(3, 2)] = negone; + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_zo(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + perspective_rh_zo(aspect, fovy, near, far) } -//pub fn perspective_fov(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_lh(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_lh_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_lh_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_rh(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_rh_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_rh_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_lh(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_lh_no(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_lh_zo(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_no(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_rh(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_rh_no(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_rh_zo(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_zo(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// //pub fn tweaked_infinite_perspective(fovy: N, aspect: N, near: N) -> TMat4 { // unimplemented!() //} diff --git a/nalgebra-glm/src/ext/matrix_projection.rs b/nalgebra-glm/src/ext/matrix_projection.rs index d56103a6..4ed23fb3 100644 --- a/nalgebra-glm/src/ext/matrix_projection.rs +++ b/nalgebra-glm/src/ext/matrix_projection.rs @@ -24,7 +24,8 @@ pub fn pick_matrix(center: &TVec2, delta: &TVec2, viewport: &TVec )) } -/// Map the specified object coordinates `(obj.x, obj.y, obj.z)` into window coordinates using OpenGL near and far clip planes definition. +/// Map the specified object coordinates `(obj.x, obj.y, obj.z)` into window coordinates with a +/// depth range of -1 to 1 /// /// # Parameters: /// @@ -114,7 +115,8 @@ pub fn project_zo( ) } -/// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates using OpenGL near and far clip planes definition. +/// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates using a +/// depth range of -1 to 1 /// /// # Parameters: /// diff --git a/nalgebra-glm/src/ext/matrix_transform.rs b/nalgebra-glm/src/ext/matrix_transform.rs index fb23efc9..82926249 100644 --- a/nalgebra-glm/src/ext/matrix_transform.rs +++ b/nalgebra-glm/src/ext/matrix_transform.rs @@ -38,11 +38,7 @@ pub fn look_at(eye: &TVec3, center: &TVec3, up: &TVec3) -> TMa /// * [`look_at`](fn.look_at.html) /// * [`look_at_rh`](fn.look_at_rh.html) pub fn look_at_lh(eye: &TVec3, center: &TVec3, up: &TVec3) -> TMat4 { - TMat::look_at_lh( - &Point3::from_coordinates(*eye), - &Point3::from_coordinates(*center), - up, - ) + TMat::look_at_lh(&Point3::from(*eye), &Point3::from(*center), up) } /// Build a right handed look at view matrix. @@ -58,11 +54,7 @@ pub fn look_at_lh(eye: &TVec3, center: &TVec3, up: &TVec3) -> /// * [`look_at`](fn.look_at.html) /// * [`look_at_lh`](fn.look_at_lh.html) pub fn look_at_rh(eye: &TVec3, center: &TVec3, up: &TVec3) -> TMat4 { - TMat::look_at_rh( - &Point3::from_coordinates(*eye), - &Point3::from_coordinates(*center), - up, - ) + TMat::look_at_rh(&Point3::from(*eye), &Point3::from(*center), up) } /// Builds a rotation 4 * 4 matrix created from an axis vector and an angle and right-multiply it to `m`. diff --git a/nalgebra-glm/src/ext/mod.rs b/nalgebra-glm/src/ext/mod.rs index 6ad74ff6..0f9879ef 100644 --- a/nalgebra-glm/src/ext/mod.rs +++ b/nalgebra-glm/src/ext/mod.rs @@ -1,6 +1,16 @@ //! (Reexported) Additional features not specified by GLSL specification -pub use self::matrix_clip_space::{ortho, perspective}; +pub use self::matrix_clip_space::{ + ortho, ortho_lh, ortho_lh_no, ortho_lh_zo, ortho_no, ortho_rh, ortho_rh_no, ortho_rh_zo, + ortho_zo, + + perspective, perspective_lh, perspective_lh_no, perspective_lh_zo, perspective_no, + perspective_rh, perspective_rh_no, perspective_rh_zo, perspective_zo, + + perspective_fov, perspective_fov_lh,perspective_fov_lh_no, perspective_fov_lh_zo, + perspective_fov_no, perspective_fov_rh, perspective_fov_rh_no, perspective_fov_rh_zo, + perspective_fov_zo, +}; pub use self::matrix_projection::{ pick_matrix, project, project_no, project_zo, unproject, unproject_no, unproject_zo, }; diff --git a/nalgebra-glm/src/ext/quaternion_common.rs b/nalgebra-glm/src/ext/quaternion_common.rs index 208601ed..3a91d0c7 100644 --- a/nalgebra-glm/src/ext/quaternion_common.rs +++ b/nalgebra-glm/src/ext/quaternion_common.rs @@ -33,5 +33,5 @@ pub fn quat_lerp(x: &Qua, y: &Qua, a: N) -> Qua { pub fn quat_slerp(x: &Qua, y: &Qua, a: N) -> Qua { Unit::new_normalize(*x) .slerp(&Unit::new_normalize(*y), a) - .unwrap() + .into_inner() } diff --git a/nalgebra-glm/src/ext/quaternion_transform.rs b/nalgebra-glm/src/ext/quaternion_transform.rs index a1459269..a4a60210 100644 --- a/nalgebra-glm/src/ext/quaternion_transform.rs +++ b/nalgebra-glm/src/ext/quaternion_transform.rs @@ -19,7 +19,7 @@ pub fn quat_pow(q: &Qua, y: N) -> Qua { /// Builds a quaternion from an axis and an angle, and right-multiply it to the quaternion `q`. pub fn quat_rotate(q: &Qua, angle: N, axis: &TVec3) -> Qua { - q * UnitQuaternion::from_axis_angle(&Unit::new_normalize(*axis), angle).unwrap() + q * UnitQuaternion::from_axis_angle(&Unit::new_normalize(*axis), angle).into_inner() } //pub fn quat_sqrt(q: &Qua) -> Qua { diff --git a/nalgebra-glm/src/ext/quaternion_trigonometric.rs b/nalgebra-glm/src/ext/quaternion_trigonometric.rs index 6e9be030..762bd9e9 100644 --- a/nalgebra-glm/src/ext/quaternion_trigonometric.rs +++ b/nalgebra-glm/src/ext/quaternion_trigonometric.rs @@ -9,13 +9,13 @@ pub fn quat_angle(x: &Qua) -> N { /// Creates a quaternion from an axis and an angle. pub fn quat_angle_axis(angle: N, axis: &TVec3) -> Qua { - UnitQuaternion::from_axis_angle(&Unit::new_normalize(*axis), angle).unwrap() + UnitQuaternion::from_axis_angle(&Unit::new_normalize(*axis), angle).into_inner() } /// The rotation axis of a quaternion assumed to be normalized. pub fn quat_axis(x: &Qua) -> TVec3 { if let Some(a) = UnitQuaternion::from_quaternion(*x).axis() { - a.unwrap() + a.into_inner() } else { TVec3::zeros() } diff --git a/nalgebra-glm/src/gtc/integer.rs b/nalgebra-glm/src/gtc/integer.rs index a5f1cc8f..df65843f 100644 --- a/nalgebra-glm/src/gtc/integer.rs +++ b/nalgebra-glm/src/gtc/integer.rs @@ -15,4 +15,4 @@ //pub fn uround(x: &TVec) -> TVec // where DefaultAllocator: Alloc { // unimplemented!() -//} \ No newline at end of file +//} diff --git a/nalgebra-glm/src/gtc/packing.rs b/nalgebra-glm/src/gtc/packing.rs index 414afe72..b0261239 100644 --- a/nalgebra-glm/src/gtc/packing.rs +++ b/nalgebra-glm/src/gtc/packing.rs @@ -288,4 +288,4 @@ pub fn unpackUnorm4x16(p: u64) -> Vec4 { pub fn unpackUnorm4x4(p: u16) -> Vec4 { unimplemented!() -} \ No newline at end of file +} diff --git a/nalgebra-glm/src/gtc/quaternion.rs b/nalgebra-glm/src/gtc/quaternion.rs index fd3e22bf..d31a0bcb 100644 --- a/nalgebra-glm/src/gtc/quaternion.rs +++ b/nalgebra-glm/src/gtc/quaternion.rs @@ -5,7 +5,7 @@ use aliases::{Qua, TMat4, TVec, TVec3}; /// Euler angles of the quaternion `q` as (pitch, yaw, roll). pub fn quat_euler_angles(x: &Qua) -> TVec3 { let q = UnitQuaternion::new_unchecked(*x); - let a = q.to_euler_angles(); + let a = q.euler_angles(); TVec3::new(a.2, a.1, a.0) } @@ -34,19 +34,25 @@ pub fn quat_cast(x: &Qua) -> TMat4 { ::quat_to_mat4(x) } -/// Computes a right-handed look-at quaternion (equivalent to a right-handed look-at matrix). +/// Computes a right hand look-at quaternion +/// +/// # Parameters +/// +/// * `direction` - Direction vector point at where to look +/// * `up` - Object up vector +/// pub fn quat_look_at(direction: &TVec3, up: &TVec3) -> Qua { quat_look_at_rh(direction, up) } /// Computes a left-handed look-at quaternion (equivalent to a left-handed look-at matrix). pub fn quat_look_at_lh(direction: &TVec3, up: &TVec3) -> Qua { - UnitQuaternion::look_at_lh(direction, up).unwrap() + UnitQuaternion::look_at_lh(direction, up).into_inner() } /// Computes a right-handed look-at quaternion (equivalent to a right-handed look-at matrix). pub fn quat_look_at_rh(direction: &TVec3, up: &TVec3) -> Qua { - UnitQuaternion::look_at_rh(direction, up).unwrap() + UnitQuaternion::look_at_rh(direction, up).into_inner() } /// The "roll" Euler angle of the quaternion `x` assumed to be normalized. diff --git a/nalgebra-glm/src/gtc/type_ptr.rs b/nalgebra-glm/src/gtc/type_ptr.rs index 93a88e78..ed901f20 100644 --- a/nalgebra-glm/src/gtc/type_ptr.rs +++ b/nalgebra-glm/src/gtc/type_ptr.rs @@ -113,7 +113,7 @@ pub fn mat4_to_mat2(m: &TMat4) -> TMat2 { /// Creates a quaternion from a slice arranged as `[x, y, z, w]`. pub fn make_quat(ptr: &[N]) -> Qua { - Quaternion::from_vector(TVec4::from_column_slice(ptr)) + Quaternion::from(TVec4::from_column_slice(ptr)) } /// Creates a 1D vector from a slice. diff --git a/nalgebra-glm/src/gtx/quaternion.rs b/nalgebra-glm/src/gtx/quaternion.rs index 5762b58d..70005ca8 100644 --- a/nalgebra-glm/src/gtx/quaternion.rs +++ b/nalgebra-glm/src/gtx/quaternion.rs @@ -21,7 +21,7 @@ pub fn quat_extract_real_component(q: &Qua) -> N { pub fn quat_fast_mix(x: &Qua, y: &Qua, a: N) -> Qua { Unit::new_unchecked(*x) .nlerp(&Unit::new_unchecked(*y), a) - .unwrap() + .into_inner() } //pub fn quat_intermediate(prev: &Qua, curr: &Qua, next: &Qua) -> Qua { @@ -40,7 +40,7 @@ pub fn quat_magnitude2(q: &Qua) -> N { /// The quaternion representing the identity rotation. pub fn quat_identity() -> Qua { - UnitQuaternion::identity().unwrap() + UnitQuaternion::identity().into_inner() } /// Rotates a vector by a quaternion assumed to be normalized. @@ -58,14 +58,14 @@ pub fn quat_rotate_vec(q: &Qua, v: &TVec4) -> TVec4 { pub fn quat_rotation(orig: &TVec3, dest: &TVec3) -> Qua { UnitQuaternion::rotation_between(orig, dest) .unwrap_or_else(UnitQuaternion::identity) - .unwrap() + .into_inner() } /// The spherical linear interpolation between two quaternions. pub fn quat_short_mix(x: &Qua, y: &Qua, a: N) -> Qua { Unit::new_normalize(*x) .slerp(&Unit::new_normalize(*y), a) - .unwrap() + .into_inner() } //pub fn quat_squad(q1: &Qua, q2: &Qua, s1: &Qua, s2: &Qua, h: N) -> Qua { @@ -76,7 +76,7 @@ pub fn quat_short_mix(x: &Qua, y: &Qua, a: N) -> Qua { pub fn quat_to_mat3(x: &Qua) -> TMat3 { UnitQuaternion::new_unchecked(*x) .to_rotation_matrix() - .unwrap() + .into_inner() } /// Converts a quaternion to a rotation matrix in homogenous coordinates. @@ -87,7 +87,7 @@ pub fn quat_to_mat4(x: &Qua) -> TMat4 { /// Converts a rotation matrix to a quaternion. pub fn mat3_to_quat(x: &TMat3) -> Qua { let r = Rotation3::from_matrix_unchecked(*x); - UnitQuaternion::from_rotation_matrix(&r).unwrap() + UnitQuaternion::from_rotation_matrix(&r).into_inner() } /// Converts a rotation matrix in homogeneous coordinates to a quaternion. diff --git a/nalgebra-glm/src/gtx/rotate_normalized_axis.rs b/nalgebra-glm/src/gtx/rotate_normalized_axis.rs index 1ac9a7dc..224d7bfe 100644 --- a/nalgebra-glm/src/gtx/rotate_normalized_axis.rs +++ b/nalgebra-glm/src/gtx/rotate_normalized_axis.rs @@ -21,5 +21,5 @@ pub fn rotate_normalized_axis(m: &TMat4, angle: N, axis: &TVec3) /// * `angle` - Angle expressed in radians. /// * `axis` - Normalized axis of the rotation, must be normalized. pub fn quat_rotate_normalized_axis(q: &Qua, angle: N, axis: &TVec3) -> Qua { - q * UnitQuaternion::from_axis_angle(&Unit::new_unchecked(*axis), angle).unwrap() + q * UnitQuaternion::from_axis_angle(&Unit::new_unchecked(*axis), angle).into_inner() } diff --git a/nalgebra-glm/src/gtx/rotate_vector.rs b/nalgebra-glm/src/gtx/rotate_vector.rs index 0855244d..64ce7244 100644 --- a/nalgebra-glm/src/gtx/rotate_vector.rs +++ b/nalgebra-glm/src/gtx/rotate_vector.rs @@ -60,5 +60,5 @@ pub fn rotate_z_vec4(v: &TVec4, angle: N) -> TVec4 { pub fn slerp(x: &TVec3, y: &TVec3, a: N) -> TVec3 { Unit::new_unchecked(*x) .slerp(&Unit::new_unchecked(*y), a) - .unwrap() + .into_inner() } diff --git a/nalgebra-glm/src/lib.rs b/nalgebra-glm/src/lib.rs index 495b61d9..bd74598c 100644 --- a/nalgebra-glm/src/lib.rs +++ b/nalgebra-glm/src/lib.rs @@ -111,6 +111,7 @@ */ #![doc(html_favicon_url = "http://nalgebra.org/img/favicon.ico")] +#![cfg_attr(not(feature = "std"), no_std)] extern crate num_traits as num; #[macro_use] @@ -122,8 +123,9 @@ pub use aliases::*; pub use common::{ abs, ceil, clamp, clamp_scalar, clamp_vec, float_bits_to_int, float_bits_to_int_vec, float_bits_to_uint, float_bits_to_uint_vec, floor, fract, int_bits_to_float, - int_bits_to_float_vec, mix, modf, modf_vec, round, sign, smoothstep, step, step_scalar, - step_vec, trunc, uint_bits_to_float, uint_bits_to_float_scalar, + int_bits_to_float_vec, lerp, lerp_scalar, lerp_vec, mix, mix_scalar, mix_vec, modf, modf_vec, + round, sign, smoothstep, step, step_scalar, step_vec, trunc, uint_bits_to_float, + uint_bits_to_float_scalar, }; pub use constructors::*; pub use exponential::{exp, exp2, inversesqrt, log, log2, pow, sqrt}; @@ -143,12 +145,17 @@ pub use ext::{ epsilon, equal_columns, equal_columns_eps, equal_columns_eps_vec, equal_eps, equal_eps_vec, identity, look_at, look_at_lh, look_at_rh, max, max2, max3, max3_scalar, max4, max4_scalar, min, min2, min3, min3_scalar, min4, min4_scalar, not_equal_columns, not_equal_columns_eps, - not_equal_columns_eps_vec, not_equal_eps, not_equal_eps_vec, ortho, perspective, pi, - pick_matrix, project, project_no, project_zo, quat_angle, quat_angle_axis, quat_axis, - quat_conjugate, quat_cross, quat_dot, quat_equal, quat_equal_eps, quat_exp, quat_inverse, - quat_length, quat_lerp, quat_log, quat_magnitude, quat_normalize, quat_not_equal, - quat_not_equal_eps, quat_pow, quat_rotate, quat_slerp, rotate, rotate_x, rotate_y, rotate_z, - scale, translate, unproject, unproject_no, unproject_zo, + not_equal_columns_eps_vec, not_equal_eps, not_equal_eps_vec, ortho, perspective, perspective_fov, + perspective_fov_lh,perspective_fov_lh_no, perspective_fov_lh_zo, perspective_fov_no, + perspective_fov_rh, perspective_fov_rh_no, perspective_fov_rh_zo, perspective_fov_zo, + perspective_lh, perspective_lh_no, perspective_lh_zo, perspective_no, perspective_rh, + perspective_rh_no, perspective_rh_zo, perspective_zo, ortho_lh, ortho_lh_no, ortho_lh_zo, + ortho_no, ortho_rh, ortho_rh_no, ortho_rh_zo, ortho_zo, pi, pick_matrix, project, project_no, + project_zo, quat_angle, quat_angle_axis, quat_axis, quat_conjugate, quat_cross, quat_dot, + quat_equal, quat_equal_eps, quat_exp, quat_inverse, quat_length, quat_lerp, quat_log, + quat_magnitude, quat_normalize, quat_not_equal, quat_not_equal_eps, quat_pow, quat_rotate, + quat_slerp, rotate, rotate_x, rotate_y, rotate_z, scale, translate, unproject, unproject_no, + unproject_zo, }; pub use gtc::{ affine_inverse, column, e, euler, four_over_pi, golden_ratio, half_pi, inverse_transpose, diff --git a/nalgebra-glm/src/traits.rs b/nalgebra-glm/src/traits.rs index 3c2033c1..d338539d 100644 --- a/nalgebra-glm/src/traits.rs +++ b/nalgebra-glm/src/traits.rs @@ -17,8 +17,7 @@ pub trait Number: impl + Signed + FromPrimitive + Bounded> Number for T -{ -} +{} #[doc(hidden)] pub trait Alloc: @@ -77,5 +76,4 @@ impl Alloc for T where T: All + Allocator + Allocator<(usize, usize), R> + Allocator<(usize, usize), C> -{ -} +{} diff --git a/nalgebra-glm/tests/lib.rs b/nalgebra-glm/tests/lib.rs new file mode 100644 index 00000000..ce95c64f --- /dev/null +++ b/nalgebra-glm/tests/lib.rs @@ -0,0 +1,55 @@ +extern crate nalgebra as na; +extern crate nalgebra_glm as glm; + +use na::Perspective3; +use na::Orthographic3; +use glm::Mat4; +use glm::Vec4; + +#[test] +pub fn orthographic_glm_nalgebra_same() +{ + let na_mat : Mat4 = Orthographic3::new(-100.0f32,100.0f32, -50.0f32, 50.0f32, 0.1f32, 100.0f32).into_inner(); + let gl_mat : Mat4 = glm::ortho(-100.0f32,100.0f32, -50.0f32, 50.0f32, 0.1f32, 100.0f32); + + assert_eq!(na_mat, gl_mat); +} + +#[test] +pub fn perspective_glm_nalgebra_same() +{ + let na_mat : Mat4 = Perspective3::new(16.0f32/9.0f32, 3.14f32/2.0f32, 0.1f32, 100.0f32).into_inner(); + let gl_mat : Mat4 = glm::perspective(16.0f32/9.0f32, 3.14f32/2.0f32, 0.1f32, 100.0f32); + + assert_eq!(na_mat, gl_mat); +} + +#[test] +pub fn orthographic_glm_nalgebra_project_same() +{ + let point = Vec4::new(1.0,0.0,-20.0,1.0); + + let na_mat : Mat4 = Orthographic3::new(-100.0f32,100.0f32, -50.0f32, 50.0f32, 0.1f32, 100.0f32).into_inner(); + let gl_mat : Mat4 = glm::ortho(-100.0f32,100.0f32, -50.0f32, 50.0f32, 0.1f32, 100.0f32); + + let na_pt = na_mat * point; + let gl_pt = gl_mat * point; + + assert_eq!(na_mat, gl_mat); + assert_eq!(na_pt, gl_pt); +} + +#[test] +pub fn perspective_glm_nalgebra_project_same() +{ + let point = Vec4::new(1.0,0.0,-20.0,1.0); + + let na_mat : Mat4 = Perspective3::new(16.0f32/9.0f32, 3.14f32/2.0f32, 0.1f32, 100.0f32).into_inner(); + let gl_mat : Mat4 = glm::perspective(16.0f32/9.0f32, 3.14f32/2.0f32, 0.1f32, 100.0f32); + + let na_pt = na_mat * point; + let gl_pt = gl_mat * point; + + assert_eq!(na_mat, gl_mat); + assert_eq!(na_pt, gl_pt); +} diff --git a/nalgebra-lapack/Cargo.toml b/nalgebra-lapack/Cargo.toml index e21891c1..c59211ab 100644 --- a/nalgebra-lapack/Cargo.toml +++ b/nalgebra-lapack/Cargo.toml @@ -34,6 +34,6 @@ lapack-src = { version = "0.2", default-features = false } [dev-dependencies] nalgebra = { version = "0.16", path = "..", features = [ "arbitrary" ] } -quickcheck = "0.6" +quickcheck = "0.7" approx = "0.3" -rand = "0.5" +rand = "0.6" diff --git a/nalgebra-lapack/src/cholesky.rs b/nalgebra-lapack/src/cholesky.rs index a213e24f..3976373d 100644 --- a/nalgebra-lapack/src/cholesky.rs +++ b/nalgebra-lapack/src/cholesky.rs @@ -15,13 +15,17 @@ use lapack; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator, - MatrixN: Serialize")) + serde(bound( + serialize = "DefaultAllocator: Allocator, + MatrixN: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator, - MatrixN: Deserialize<'de>")) + serde(bound( + deserialize = "DefaultAllocator: Allocator, + MatrixN: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct Cholesky @@ -34,8 +38,7 @@ impl Copy for Cholesky where DefaultAllocator: Allocator, MatrixN: Copy, -{ -} +{} impl Cholesky where DefaultAllocator: Allocator @@ -157,7 +160,7 @@ where DefaultAllocator: Allocator // Copy lower triangle to upper triangle. for i in 0..dim { for j in i + 1..dim { - unsafe { *self.l.get_unchecked_mut(i, j) = *self.l.get_unchecked(j, i) }; + unsafe { *self.l.get_unchecked_mut((i, j)) = *self.l.get_unchecked((j, i)) }; } } diff --git a/nalgebra-lapack/src/eigen.rs b/nalgebra-lapack/src/eigen.rs index 9fa40e1b..d95219cb 100644 --- a/nalgebra-lapack/src/eigen.rs +++ b/nalgebra-lapack/src/eigen.rs @@ -18,19 +18,19 @@ use lapack; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde( - bound(serialize = "DefaultAllocator: Allocator + Allocator, + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator, VectorN: Serialize, - MatrixN: Serialize") - ) + MatrixN: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde( - bound(deserialize = "DefaultAllocator: Allocator + Allocator, + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator, VectorN: Serialize, - MatrixN: Deserialize<'de>") - ) + MatrixN: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct Eigen @@ -49,8 +49,7 @@ where DefaultAllocator: Allocator + Allocator, VectorN: Copy, MatrixN: Copy, -{ -} +{} impl Eigen where DefaultAllocator: Allocator + Allocator diff --git a/nalgebra-lapack/src/hessenberg.rs b/nalgebra-lapack/src/hessenberg.rs index 57a4b2e1..6bfa673b 100644 --- a/nalgebra-lapack/src/hessenberg.rs +++ b/nalgebra-lapack/src/hessenberg.rs @@ -13,17 +13,21 @@ use lapack; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator>, MatrixN: Serialize, - VectorN>: Serialize")) + VectorN>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator>, MatrixN: Deserialize<'de>, - VectorN>: Deserialize<'de>")) + VectorN>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct Hessenberg> @@ -38,8 +42,7 @@ where DefaultAllocator: Allocator + Allocator>, MatrixN: Copy, VectorN>: Copy, -{ -} +{} impl> Hessenberg where DefaultAllocator: Allocator + Allocator> diff --git a/nalgebra-lapack/src/lu.rs b/nalgebra-lapack/src/lu.rs index 47ee912a..c9b1167c 100644 --- a/nalgebra-lapack/src/lu.rs +++ b/nalgebra-lapack/src/lu.rs @@ -20,17 +20,21 @@ use lapack; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator>, MatrixMN: Serialize, - PermutationSequence>: Serialize")) + PermutationSequence>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator>, MatrixMN: Deserialize<'de>, - PermutationSequence>: Deserialize<'de>")) + PermutationSequence>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct LU, C: Dim> @@ -45,8 +49,7 @@ where DefaultAllocator: Allocator + Allocator>, MatrixMN: Copy, VectorN>: Copy, -{ -} +{} impl LU where diff --git a/nalgebra-lapack/src/qr.rs b/nalgebra-lapack/src/qr.rs index 1bf55644..66220e49 100644 --- a/nalgebra-lapack/src/qr.rs +++ b/nalgebra-lapack/src/qr.rs @@ -16,17 +16,21 @@ use lapack; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator>, MatrixMN: Serialize, - VectorN>: Serialize")) + VectorN>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator>, MatrixMN: Deserialize<'de>, - VectorN>: Deserialize<'de>")) + VectorN>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct QR, C: Dim> @@ -41,8 +45,7 @@ where DefaultAllocator: Allocator + Allocator>, MatrixMN: Copy, VectorN>: Copy, -{ -} +{} impl, C: Dim> QR where DefaultAllocator: Allocator diff --git a/nalgebra-lapack/src/schur.rs b/nalgebra-lapack/src/schur.rs index 27ce5927..a09b31ff 100644 --- a/nalgebra-lapack/src/schur.rs +++ b/nalgebra-lapack/src/schur.rs @@ -18,19 +18,19 @@ use lapack; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde( - bound(serialize = "DefaultAllocator: Allocator + Allocator, + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator, VectorN: Serialize, - MatrixN: Serialize") - ) + MatrixN: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde( - bound(deserialize = "DefaultAllocator: Allocator + Allocator, + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator, VectorN: Serialize, - MatrixN: Deserialize<'de>") - ) + MatrixN: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct RealSchur @@ -47,8 +47,7 @@ where DefaultAllocator: Allocator + Allocator, MatrixN: Copy, VectorN: Copy, -{ -} +{} impl RealSchur where DefaultAllocator: Allocator + Allocator diff --git a/nalgebra-lapack/src/svd.rs b/nalgebra-lapack/src/svd.rs index 5883578a..2d048bf3 100644 --- a/nalgebra-lapack/src/svd.rs +++ b/nalgebra-lapack/src/svd.rs @@ -15,21 +15,25 @@ use lapack; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator> + + serde(bound( + serialize = "DefaultAllocator: Allocator> + Allocator + Allocator, MatrixN: Serialize, MatrixN: Serialize, - VectorN>: Serialize")) + VectorN>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator> + + serde(bound( + serialize = "DefaultAllocator: Allocator> + Allocator + Allocator, MatrixN: Deserialize<'de>, MatrixN: Deserialize<'de>, - VectorN>: Deserialize<'de>")) + VectorN>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct SVD, C: Dim> @@ -49,8 +53,7 @@ where MatrixMN: Copy, MatrixMN: Copy, VectorN>: Copy, -{ -} +{} /// Trait implemented by floats (`f32`, `f64`) and complex floats (`Complex`, `Complex`) /// supported by the Singular Value Decompotition. diff --git a/nalgebra-lapack/src/symmetric_eigen.rs b/nalgebra-lapack/src/symmetric_eigen.rs index a06445ee..e7d5bd0f 100644 --- a/nalgebra-lapack/src/symmetric_eigen.rs +++ b/nalgebra-lapack/src/symmetric_eigen.rs @@ -18,17 +18,21 @@ use lapack; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator, VectorN: Serialize, - MatrixN: Serialize")) + MatrixN: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator, VectorN: Deserialize<'de>, - MatrixN: Deserialize<'de>")) + MatrixN: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct SymmetricEigen @@ -46,8 +50,7 @@ where DefaultAllocator: Allocator + Allocator, MatrixN: Copy, VectorN: Copy, -{ -} +{} impl SymmetricEigen where DefaultAllocator: Allocator + Allocator diff --git a/src/base/alias.rs b/src/base/alias.rs index 3942b94e..f7ca2b34 100644 --- a/src/base/alias.rs +++ b/src/base/alias.rs @@ -2,7 +2,7 @@ use base::dimension::Dynamic; use base::dimension::{U1, U2, U3, U4, U5, U6}; #[cfg(any(feature = "std", feature = "alloc"))] -use base::matrix_vec::MatrixVec; +use base::vec_storage::VecStorage; use base::storage::Owned; use base::Matrix; @@ -119,7 +119,7 @@ pub type Matrix6x5 = MatrixMN; */ /// A dynamically sized column vector. #[cfg(any(feature = "std", feature = "alloc"))] -pub type DVector = Matrix>; +pub type DVector = Matrix>; /// A statically sized D-dimensional column vector. pub type VectorN = MatrixMN; @@ -146,7 +146,7 @@ pub type Vector6 = VectorN; */ /// A dynamically sized row vector. #[cfg(any(feature = "std", feature = "alloc"))] -pub type RowDVector = Matrix>; +pub type RowDVector = Matrix>; /// A statically sized D-dimensional row vector. pub type RowVectorN = MatrixMN; diff --git a/src/base/alias_slice.rs b/src/base/alias_slice.rs index 1b368c8e..790e4e59 100644 --- a/src/base/alias_slice.rs +++ b/src/base/alias_slice.rs @@ -175,24 +175,24 @@ pub type MatrixSliceXx6<'a, N, RStride = U1, CStride = Dynamic> = MatrixSliceMN<'a, N, Dynamic, U6, RStride, CStride>; /// A column vector slice with `D` rows. -pub type VectorSliceN<'a, N, D, Stride = U1> = - Matrix>; +pub type VectorSliceN<'a, N, D, RStride = U1, CStride = D> = + Matrix>; /// A column vector slice dynamic numbers of rows and columns. -pub type DVectorSlice<'a, N, Stride = U1> = VectorSliceN<'a, N, Dynamic, Stride>; +pub type DVectorSlice<'a, N, RStride = U1, CStride = Dynamic> = VectorSliceN<'a, N, Dynamic, RStride, CStride>; /// A 1D column vector slice. -pub type VectorSlice1<'a, N, Stride = U1> = VectorSliceN<'a, N, U1, Stride>; +pub type VectorSlice1<'a, N, RStride = U1, CStride = U1> = VectorSliceN<'a, N, U1, RStride, CStride>; /// A 2D column vector slice. -pub type VectorSlice2<'a, N, Stride = U1> = VectorSliceN<'a, N, U2, Stride>; +pub type VectorSlice2<'a, N, RStride = U1, CStride = U2> = VectorSliceN<'a, N, U2, RStride, CStride>; /// A 3D column vector slice. -pub type VectorSlice3<'a, N, Stride = U1> = VectorSliceN<'a, N, U3, Stride>; +pub type VectorSlice3<'a, N, RStride = U1, CStride = U3> = VectorSliceN<'a, N, U3, RStride, CStride>; /// A 4D column vector slice. -pub type VectorSlice4<'a, N, Stride = U1> = VectorSliceN<'a, N, U4, Stride>; +pub type VectorSlice4<'a, N, RStride = U1, CStride = U4> = VectorSliceN<'a, N, U4, RStride, CStride>; /// A 5D column vector slice. -pub type VectorSlice5<'a, N, Stride = U1> = VectorSliceN<'a, N, U5, Stride>; +pub type VectorSlice5<'a, N, RStride = U1, CStride = U5> = VectorSliceN<'a, N, U5, RStride, CStride>; /// A 6D column vector slice. -pub type VectorSlice6<'a, N, Stride = U1> = VectorSliceN<'a, N, U6, Stride>; +pub type VectorSlice6<'a, N, RStride = U1, CStride = U6> = VectorSliceN<'a, N, U6, RStride, CStride>; /* * @@ -367,21 +367,21 @@ pub type MatrixSliceMutXx6<'a, N, RStride = U1, CStride = Dynamic> = MatrixSliceMutMN<'a, N, Dynamic, U6, RStride, CStride>; /// A mutable column vector slice with `D` rows. -pub type VectorSliceMutN<'a, N, D, Stride = U1> = - Matrix>; +pub type VectorSliceMutN<'a, N, D, RStride = U1, CStride = D> = + Matrix>; /// A mutable column vector slice dynamic numbers of rows and columns. -pub type DVectorSliceMut<'a, N, Stride = U1> = VectorSliceMutN<'a, N, Dynamic, Stride>; +pub type DVectorSliceMut<'a, N, RStride = U1, CStride = Dynamic> = VectorSliceMutN<'a, N, Dynamic, RStride, CStride>; /// A 1D mutable column vector slice. -pub type VectorSliceMut1<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U1, Stride>; +pub type VectorSliceMut1<'a, N, RStride = U1, CStride = U1> = VectorSliceMutN<'a, N, U1, RStride, CStride>; /// A 2D mutable column vector slice. -pub type VectorSliceMut2<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U2, Stride>; +pub type VectorSliceMut2<'a, N, RStride = U1, CStride = U2> = VectorSliceMutN<'a, N, U2, RStride, CStride>; /// A 3D mutable column vector slice. -pub type VectorSliceMut3<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U3, Stride>; +pub type VectorSliceMut3<'a, N, RStride = U1, CStride = U3> = VectorSliceMutN<'a, N, U3, RStride, CStride>; /// A 4D mutable column vector slice. -pub type VectorSliceMut4<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U4, Stride>; +pub type VectorSliceMut4<'a, N, RStride = U1, CStride = U4> = VectorSliceMutN<'a, N, U4, RStride, CStride>; /// A 5D mutable column vector slice. -pub type VectorSliceMut5<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U5, Stride>; +pub type VectorSliceMut5<'a, N, RStride = U1, CStride = U5> = VectorSliceMutN<'a, N, U5, RStride, CStride>; /// A 6D mutable column vector slice. -pub type VectorSliceMut6<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U6, Stride>; +pub type VectorSliceMut6<'a, N, RStride = U1, CStride = U6> = VectorSliceMutN<'a, N, U6, RStride, CStride>; diff --git a/src/base/allocator.rs b/src/base/allocator.rs index 30f08af0..5b17c183 100644 --- a/src/base/allocator.rs +++ b/src/base/allocator.rs @@ -79,8 +79,7 @@ where N: Scalar, DefaultAllocator: Allocator + Allocator, SameShapeC>, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, -{ -} +{} // XXX: Bad name. /// Restricts the given number of rows to be equal. @@ -101,5 +100,4 @@ where N: Scalar, DefaultAllocator: Allocator + Allocator>, ShapeConstraint: SameNumberOfRows, -{ -} +{} diff --git a/src/base/matrix_array.rs b/src/base/array_storage.rs similarity index 83% rename from src/base/matrix_array.rs rename to src/base/array_storage.rs index deaa9fc3..3beab9e1 100644 --- a/src/base/matrix_array.rs +++ b/src/base/array_storage.rs @@ -34,7 +34,7 @@ use base::Scalar; */ /// A array-based statically sized matrix data storage. #[repr(C)] -pub struct MatrixArray +pub struct ArrayStorage where R: DimName, C: DimName, @@ -44,7 +44,11 @@ where data: GenericArray>, } -impl Hash for MatrixArray +#[deprecated(note="renamed to `ArrayStorage`")] +/// Renamed to [ArrayStorage]. +pub type MatrixArray = ArrayStorage; + +impl Hash for ArrayStorage where N: Hash, R: DimName, @@ -57,7 +61,7 @@ where } } -impl Deref for MatrixArray +impl Deref for ArrayStorage where R: DimName, C: DimName, @@ -72,7 +76,7 @@ where } } -impl DerefMut for MatrixArray +impl DerefMut for ArrayStorage where R: DimName, C: DimName, @@ -85,7 +89,7 @@ where } } -impl Debug for MatrixArray +impl Debug for ArrayStorage where N: Debug, R: DimName, @@ -99,7 +103,7 @@ where } } -impl Copy for MatrixArray +impl Copy for ArrayStorage where N: Copy, R: DimName, @@ -107,10 +111,9 @@ where R::Value: Mul, Prod: ArrayLength, GenericArray>: Copy, -{ -} +{} -impl Clone for MatrixArray +impl Clone for ArrayStorage where N: Clone, R: DimName, @@ -120,23 +123,22 @@ where { #[inline] fn clone(&self) -> Self { - MatrixArray { + ArrayStorage { data: self.data.clone(), } } } -impl Eq for MatrixArray +impl Eq for ArrayStorage where N: Eq, R: DimName, C: DimName, R::Value: Mul, Prod: ArrayLength, -{ -} +{} -impl PartialEq for MatrixArray +impl PartialEq for ArrayStorage where N: PartialEq, R: DimName, @@ -150,7 +152,7 @@ where } } -unsafe impl Storage for MatrixArray +unsafe impl Storage for ArrayStorage where N: Scalar, R: DimName, @@ -202,7 +204,7 @@ where } } -unsafe impl StorageMut for MatrixArray +unsafe impl StorageMut for ArrayStorage where N: Scalar, R: DimName, @@ -222,7 +224,7 @@ where } } -unsafe impl ContiguousStorage for MatrixArray +unsafe impl ContiguousStorage for ArrayStorage where N: Scalar, R: DimName, @@ -230,10 +232,9 @@ where R::Value: Mul, Prod: ArrayLength, DefaultAllocator: Allocator, -{ -} +{} -unsafe impl ContiguousStorageMut for MatrixArray +unsafe impl ContiguousStorageMut for ArrayStorage where N: Scalar, R: DimName, @@ -241,8 +242,7 @@ where R::Value: Mul, Prod: ArrayLength, DefaultAllocator: Allocator, -{ -} +{} /* * @@ -251,7 +251,7 @@ where */ // XXX: open an issue for GenericArray so that it implements serde traits? #[cfg(feature = "serde-serialize")] -impl Serialize for MatrixArray +impl Serialize for ArrayStorage where N: Scalar + Serialize, R: DimName, @@ -272,7 +272,7 @@ where } #[cfg(feature = "serde-serialize")] -impl<'a, N, R, C> Deserialize<'a> for MatrixArray +impl<'a, N, R, C> Deserialize<'a> for ArrayStorage where N: Scalar + Deserialize<'a>, R: DimName, @@ -282,18 +282,18 @@ where { fn deserialize(deserializer: D) -> Result where D: Deserializer<'a> { - deserializer.deserialize_seq(MatrixArrayVisitor::new()) + deserializer.deserialize_seq(ArrayStorageVisitor::new()) } } #[cfg(feature = "serde-serialize")] /// A visitor that produces a matrix array. -struct MatrixArrayVisitor { +struct ArrayStorageVisitor { marker: PhantomData<(N, R, C)>, } #[cfg(feature = "serde-serialize")] -impl MatrixArrayVisitor +impl ArrayStorageVisitor where N: Scalar, R: DimName, @@ -303,14 +303,14 @@ where { /// Construct a new sequence visitor. pub fn new() -> Self { - MatrixArrayVisitor { + ArrayStorageVisitor { marker: PhantomData, } } } #[cfg(feature = "serde-serialize")] -impl<'a, N, R, C> Visitor<'a> for MatrixArrayVisitor +impl<'a, N, R, C> Visitor<'a> for ArrayStorageVisitor where N: Scalar + Deserialize<'a>, R: DimName, @@ -318,20 +318,20 @@ where R::Value: Mul, Prod: ArrayLength, { - type Value = MatrixArray; + type Value = ArrayStorage; fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { formatter.write_str("a matrix array") } #[inline] - fn visit_seq(self, mut visitor: V) -> Result, V::Error> + fn visit_seq(self, mut visitor: V) -> Result, V::Error> where V: SeqAccess<'a> { let mut out: Self::Value = unsafe { mem::uninitialized() }; let mut curr = 0; while let Some(value) = try!(visitor.next_element()) { - out[curr] = value; + *out.get_mut(curr).ok_or_else(|| V::Error::invalid_length(curr, &self))? = value; curr += 1; } @@ -344,7 +344,7 @@ where } #[cfg(feature = "abomonation-serialize")] -impl Abomonation for MatrixArray +impl Abomonation for ArrayStorage where R: DimName, C: DimName, diff --git a/src/base/blas.rs b/src/base/blas.rs index 073f519b..c16496d4 100644 --- a/src/base/blas.rs +++ b/src/base/blas.rs @@ -13,18 +13,18 @@ use base::dimension::{Dim, Dynamic, U1, U2, U3, U4}; use base::storage::{Storage, StorageMut}; use base::{DefaultAllocator, Matrix, Scalar, SquareMatrix, Vector}; -impl> Vector { - /// Computes the index of the vector component with the largest value. +impl> Vector { + /// Computes the index and value of the vector component with the largest value. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let vec = Vector3::new(11, -15, 13); - /// assert_eq!(vec.imax(), 2); + /// assert_eq!(vec.argmax(), (2, 13)); /// ``` #[inline] - pub fn imax(&self) -> usize { + pub fn argmax(&self) -> (usize, N) { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_max = unsafe { self.vget_unchecked(0) }; @@ -39,7 +39,21 @@ impl> Vector } } - the_i + (the_i, *the_max) + } + + /// Computes the index of the vector component with the largest value. + /// + /// # Examples: + /// + /// ``` + /// # use nalgebra::Vector3; + /// let vec = Vector3::new(11, -15, 13); + /// assert_eq!(vec.imax(), 2); + /// ``` + #[inline] + pub fn imax(&self) -> usize { + self.argmax().0 } /// Computes the index of the vector component with the largest absolute value. @@ -52,7 +66,8 @@ impl> Vector /// assert_eq!(vec.iamax(), 1); /// ``` #[inline] - pub fn iamax(&self) -> usize { + pub fn iamax(&self) -> usize + where N: Signed { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_max = unsafe { self.vget_unchecked(0).abs() }; @@ -70,6 +85,34 @@ impl> Vector the_i } + /// Computes the index and value of the vector component with the smallest value. + /// + /// # Examples: + /// + /// ``` + /// # use nalgebra::Vector3; + /// let vec = Vector3::new(11, -15, 13); + /// assert_eq!(vec.argmin(), (1, -15)); + /// ``` + #[inline] + pub fn argmin(&self) -> (usize, N) { + assert!(!self.is_empty(), "The input vector must not be empty."); + + let mut the_min = unsafe { self.vget_unchecked(0) }; + let mut the_i = 0; + + for i in 1..self.nrows() { + let val = unsafe { self.vget_unchecked(i) }; + + if val < the_min { + the_min = val; + the_i = i; + } + } + + (the_i, *the_min) + } + /// Computes the index of the vector component with the smallest value. /// /// # Examples: @@ -81,21 +124,7 @@ impl> Vector /// ``` #[inline] pub fn imin(&self) -> usize { - assert!(!self.is_empty(), "The input vector must not be empty."); - - let mut the_max = unsafe { self.vget_unchecked(0) }; - let mut the_i = 0; - - for i in 1..self.nrows() { - let val = unsafe { self.vget_unchecked(i) }; - - if val < the_max { - the_max = val; - the_i = i; - } - } - - the_i + self.argmin().0 } /// Computes the index of the vector component with the smallest absolute value. @@ -108,17 +137,18 @@ impl> Vector /// assert_eq!(vec.iamin(), 0); /// ``` #[inline] - pub fn iamin(&self) -> usize { + pub fn iamin(&self) -> usize + where N: Signed { assert!(!self.is_empty(), "The input vector must not be empty."); - let mut the_max = unsafe { self.vget_unchecked(0).abs() }; + let mut the_min = unsafe { self.vget_unchecked(0).abs() }; let mut the_i = 0; for i in 1..self.nrows() { let val = unsafe { self.vget_unchecked(i).abs() }; - if val < the_max { - the_max = val; + if val < the_min { + the_min = val; the_i = i; } } @@ -142,12 +172,12 @@ impl> Matri pub fn iamax_full(&self) -> (usize, usize) { assert!(!self.is_empty(), "The input matrix must not be empty."); - let mut the_max = unsafe { self.get_unchecked(0, 0).abs() }; + let mut the_max = unsafe { self.get_unchecked((0, 0)).abs() }; let mut the_ij = (0, 0); for j in 0..self.ncols() { for i in 0..self.nrows() { - let val = unsafe { self.get_unchecked(i, j).abs() }; + let val = unsafe { self.get_unchecked((i, j)).abs() }; if val > the_max { the_max = val; @@ -197,27 +227,27 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul // because the `for` loop below won't be very efficient on those. if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) { unsafe { - let a = *self.get_unchecked(0, 0) * *rhs.get_unchecked(0, 0); - let b = *self.get_unchecked(1, 0) * *rhs.get_unchecked(1, 0); + let a = *self.get_unchecked((0, 0)) * *rhs.get_unchecked((0, 0)); + let b = *self.get_unchecked((1, 0)) * *rhs.get_unchecked((1, 0)); return a + b; } } if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) { unsafe { - let a = *self.get_unchecked(0, 0) * *rhs.get_unchecked(0, 0); - let b = *self.get_unchecked(1, 0) * *rhs.get_unchecked(1, 0); - let c = *self.get_unchecked(2, 0) * *rhs.get_unchecked(2, 0); + let a = *self.get_unchecked((0, 0)) * *rhs.get_unchecked((0, 0)); + let b = *self.get_unchecked((1, 0)) * *rhs.get_unchecked((1, 0)); + let c = *self.get_unchecked((2, 0)) * *rhs.get_unchecked((2, 0)); return a + b + c; } } if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) { unsafe { - let mut a = *self.get_unchecked(0, 0) * *rhs.get_unchecked(0, 0); - let mut b = *self.get_unchecked(1, 0) * *rhs.get_unchecked(1, 0); - let c = *self.get_unchecked(2, 0) * *rhs.get_unchecked(2, 0); - let d = *self.get_unchecked(3, 0) * *rhs.get_unchecked(3, 0); + let mut a = *self.get_unchecked((0, 0)) * *rhs.get_unchecked((0, 0)); + let mut b = *self.get_unchecked((1, 0)) * *rhs.get_unchecked((1, 0)); + let c = *self.get_unchecked((2, 0)) * *rhs.get_unchecked((2, 0)); + let d = *self.get_unchecked((3, 0)) * *rhs.get_unchecked((3, 0)); a += c; b += d; @@ -257,14 +287,14 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul acc7 = N::zero(); while self.nrows() - i >= 8 { - acc0 += unsafe { *self.get_unchecked(i + 0, j) * *rhs.get_unchecked(i + 0, j) }; - acc1 += unsafe { *self.get_unchecked(i + 1, j) * *rhs.get_unchecked(i + 1, j) }; - acc2 += unsafe { *self.get_unchecked(i + 2, j) * *rhs.get_unchecked(i + 2, j) }; - acc3 += unsafe { *self.get_unchecked(i + 3, j) * *rhs.get_unchecked(i + 3, j) }; - acc4 += unsafe { *self.get_unchecked(i + 4, j) * *rhs.get_unchecked(i + 4, j) }; - acc5 += unsafe { *self.get_unchecked(i + 5, j) * *rhs.get_unchecked(i + 5, j) }; - acc6 += unsafe { *self.get_unchecked(i + 6, j) * *rhs.get_unchecked(i + 6, j) }; - acc7 += unsafe { *self.get_unchecked(i + 7, j) * *rhs.get_unchecked(i + 7, j) }; + acc0 += unsafe { *self.get_unchecked((i + 0, j)) * *rhs.get_unchecked((i + 0, j)) }; + acc1 += unsafe { *self.get_unchecked((i + 1, j)) * *rhs.get_unchecked((i + 1, j)) }; + acc2 += unsafe { *self.get_unchecked((i + 2, j)) * *rhs.get_unchecked((i + 2, j)) }; + acc3 += unsafe { *self.get_unchecked((i + 3, j)) * *rhs.get_unchecked((i + 3, j)) }; + acc4 += unsafe { *self.get_unchecked((i + 4, j)) * *rhs.get_unchecked((i + 4, j)) }; + acc5 += unsafe { *self.get_unchecked((i + 5, j)) * *rhs.get_unchecked((i + 5, j)) }; + acc6 += unsafe { *self.get_unchecked((i + 6, j)) * *rhs.get_unchecked((i + 6, j)) }; + acc7 += unsafe { *self.get_unchecked((i + 7, j)) * *rhs.get_unchecked((i + 7, j)) }; i += 8; } @@ -274,7 +304,7 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul res += acc3 + acc7; for k in i..self.nrows() { - res += unsafe { *self.get_unchecked(k, j) * *rhs.get_unchecked(k, j) } + res += unsafe { *self.get_unchecked((k, j)) * *rhs.get_unchecked((k, j)) } } } @@ -314,7 +344,7 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul for j in 0..self.nrows() { for i in 0..self.ncols() { - res += unsafe { *self.get_unchecked(j, i) * *rhs.get_unchecked(i, j) } + res += unsafe { *self.get_unchecked((j, i)) * *rhs.get_unchecked((i, j)) } } } @@ -627,7 +657,6 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix2x3, Matrix3x4, Matrix2x4}; /// let mut mat1 = Matrix2x4::identity(); /// let mat2 = Matrix2x3::new(1.0, 2.0, 3.0, @@ -760,7 +789,6 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3x2, Matrix3x4, Matrix2x4}; /// let mut mat1 = Matrix2x4::identity(); /// let mat2 = Matrix3x2::new(1.0, 4.0, @@ -879,7 +907,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{DMatrix, DVector}; /// // Note that all those would also work with statically-sized matrices. /// // We use DMatrix/DVector since that's the only case where pre-allocating the @@ -934,7 +961,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix2, Matrix3, Matrix2x3, Vector2}; /// let mut mat = Matrix2::identity(); /// let lhs = Matrix2x3::new(1.0, 2.0, 3.0, @@ -971,7 +997,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{DMatrix, DVector}; /// // Note that all those would also work with statically-sized matrices. /// // We use DMatrix/DVector since that's the only case where pre-allocating the @@ -1026,7 +1051,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix2, Matrix3x2, Matrix3}; /// let mut mat = Matrix2::identity(); /// let rhs = Matrix3x2::new(1.0, 2.0, diff --git a/src/base/cg.rs b/src/base/cg.rs index 01be4e99..5883e710 100644 --- a/src/base/cg.rs +++ b/src/base/cg.rs @@ -115,13 +115,13 @@ impl Matrix4 { /// Creates a new homogeneous matrix for an orthographic projection. #[inline] pub fn new_orthographic(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> Self { - Orthographic3::new(left, right, bottom, top, znear, zfar).unwrap() + Orthographic3::new(left, right, bottom, top, znear, zfar).into_inner() } /// Creates a new homogeneous matrix for a perspective projection. #[inline] pub fn new_perspective(aspect: N, fovy: N, znear: N, zfar: N) -> Self { - Perspective3::new(aspect, fovy, znear, zfar).unwrap() + Perspective3::new(aspect, fovy, znear, zfar).into_inner() } /// Creates an isometry that corresponds to the local frame of an observer standing at the @@ -130,8 +130,14 @@ impl Matrix4 { /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the /// `eye`. #[inline] + pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3) -> Self { + IsometryMatrix3::face_towards(eye, target, up).to_homogeneous() + } + + /// Deprecated: Use [Matrix4::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] pub fn new_observer_frame(eye: &Point3, target: &Point3, up: &Vector3) -> Self { - IsometryMatrix3::new_observer_frame(eye, target, up).to_homogeneous() + Matrix4::face_towards(eye, target, up) } /// Builds a right-handed look-at view matrix. @@ -314,13 +320,14 @@ impl> SquareMatrix } } -impl> Transformation>> for MatrixN +impl, S: Storage> SquareMatrix where DefaultAllocator: Allocator + Allocator> + Allocator, DimNameDiff> { + /// Transforms the given vector, assuming the matrix `self` uses homogeneous coordinates. #[inline] - fn transform_vector( + pub fn transform_vector( &self, v: &VectorN>, ) -> VectorN> @@ -336,13 +343,18 @@ where DefaultAllocator: Allocator transform * v } + /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates. #[inline] - fn transform_point(&self, pt: &Point>) -> Point> { + pub fn transform_point( + &self, + pt: &Point>, + ) -> Point> + { let transform = self.fixed_slice::, DimNameDiff>(0, 0); let translation = self.fixed_slice::, U1>(0, D::dim() - 1); let normalizer = self.fixed_slice::>(D::dim() - 1, 0); let n = normalizer.tr_dot(&pt.coords) - + unsafe { *self.get_unchecked(D::dim() - 1, D::dim() - 1) }; + + unsafe { *self.get_unchecked((D::dim() - 1, D::dim() - 1)) }; if !n.is_zero() { return transform * (pt / n) + translation; @@ -351,3 +363,23 @@ where DefaultAllocator: Allocator transform * pt + translation } } + +impl> Transformation>> for MatrixN +where DefaultAllocator: Allocator + + Allocator> + + Allocator, DimNameDiff> +{ + #[inline] + fn transform_vector( + &self, + v: &VectorN>, + ) -> VectorN> + { + self.transform_vector(v) + } + + #[inline] + fn transform_point(&self, pt: &Point>) -> Point> { + self.transform_point(pt) + } +} diff --git a/src/base/componentwise.rs b/src/base/componentwise.rs index 5db2ebb8..9081cf36 100644 --- a/src/base/componentwise.rs +++ b/src/base/componentwise.rs @@ -11,11 +11,20 @@ use base::dimension::Dim; use base::storage::{Storage, StorageMut}; use base::{DefaultAllocator, Matrix, MatrixMN, MatrixSum, Scalar}; -/// The type of the result of a matrix componentwise operation. +/// The type of the result of a matrix component-wise operation. pub type MatrixComponentOp = MatrixSum; impl> Matrix { - /// Computes the componentwise absolute value. + /// Computes the component-wise absolute value. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2; + /// let a = Matrix2::new(0.0, 1.0, + /// -2.0, -3.0); + /// assert_eq!(a.abs(), Matrix2::new(0.0, 1.0, 2.0, 3.0)) + /// ``` #[inline] pub fn abs(&self) -> MatrixMN where @@ -52,7 +61,7 @@ macro_rules! component_binop_impl( for j in 0 .. res.ncols() { for i in 0 .. res.nrows() { unsafe { - res.get_unchecked_mut(i, j).$op_assign(*rhs.get_unchecked(i, j)); + res.get_unchecked_mut((i, j)).$op_assign(*rhs.get_unchecked((i, j))); } } } @@ -80,8 +89,8 @@ macro_rules! component_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - let res = alpha * a.get_unchecked(i, j).$op(*b.get_unchecked(i, j)); - *self.get_unchecked_mut(i, j) = res; + let res = alpha * a.get_unchecked((i, j)).$op(*b.get_unchecked((i, j))); + *self.get_unchecked_mut((i, j)) = res; } } } @@ -90,8 +99,8 @@ macro_rules! component_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - let res = alpha * a.get_unchecked(i, j).$op(*b.get_unchecked(i, j)); - *self.get_unchecked_mut(i, j) = beta * *self.get_unchecked(i, j) + res; + let res = alpha * a.get_unchecked((i, j)).$op(*b.get_unchecked((i, j))); + *self.get_unchecked_mut((i, j)) = beta * *self.get_unchecked((i, j)) + res; } } } @@ -112,7 +121,7 @@ macro_rules! component_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - self.get_unchecked_mut(i, j).$op_assign(*rhs.get_unchecked(i, j)); + self.get_unchecked_mut((i, j)).$op_assign(*rhs.get_unchecked((i, j))); } } } @@ -135,12 +144,94 @@ macro_rules! component_binop_impl( component_binop_impl!( component_mul, component_mul_mut, component_mul_assign, cmpy, ClosedMul.mul.mul_assign, - "Componentwise matrix multiplication.", - "Computes componentwise `self[i] = alpha * a[i] * b[i] + beta * self[i]`.", - "Inplace componentwise matrix multiplication."; + r" + Componentwise matrix or vector multiplication. + + # Example + + ``` + # use nalgebra::Matrix2; + let a = Matrix2::new(0.0, 1.0, 2.0, 3.0); + let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); + let expected = Matrix2::new(0.0, 5.0, 12.0, 21.0); + + assert_eq!(a.component_mul(&b), expected); + ``` + ", + r" + Computes componentwise `self[i] = alpha * a[i] * b[i] + beta * self[i]`. + + # Example + ``` + # use nalgebra::Matrix2; + let mut m = Matrix2::new(0.0, 1.0, 2.0, 3.0); + let a = Matrix2::new(0.0, 1.0, 2.0, 3.0); + let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); + let expected = (a.component_mul(&b) * 5.0) + m * 10.0; + + m.cmpy(5.0, &a, &b, 10.0); + assert_eq!(m, expected); + ``` + ", + r" + Inplace componentwise matrix or vector multiplication. + + # Example + ``` + # use nalgebra::Matrix2; + let mut a = Matrix2::new(0.0, 1.0, 2.0, 3.0); + let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); + let expected = Matrix2::new(0.0, 5.0, 12.0, 21.0); + + a.component_mul_assign(&b); + + assert_eq!(a, expected); + ``` + "; component_div, component_div_mut, component_div_assign, cdpy, ClosedDiv.div.div_assign, - "Componentwise matrix division.", - "Computes componentwise `self[i] = alpha * a[i] / b[i] + beta * self[i]`.", - "Inplace componentwise matrix division."; + r" + Componentwise matrix or vector division. + + # Example + + ``` + # use nalgebra::Matrix2; + let a = Matrix2::new(0.0, 1.0, 2.0, 3.0); + let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); + let expected = Matrix2::new(0.0, 1.0 / 5.0, 2.0 / 6.0, 3.0 / 7.0); + + assert_eq!(a.component_div(&b), expected); + ``` + ", + r" + Computes componentwise `self[i] = alpha * a[i] / b[i] + beta * self[i]`. + + # Example + ``` + # use nalgebra::Matrix2; + let mut m = Matrix2::new(0.0, 1.0, 2.0, 3.0); + let a = Matrix2::new(4.0, 5.0, 6.0, 7.0); + let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); + let expected = (a.component_div(&b) * 5.0) + m * 10.0; + + m.cdpy(5.0, &a, &b, 10.0); + assert_eq!(m, expected); + ``` + ", + r" + Inplace componentwise matrix or vector division. + + # Example + ``` + # use nalgebra::Matrix2; + let mut a = Matrix2::new(0.0, 1.0, 2.0, 3.0); + let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); + let expected = Matrix2::new(0.0, 1.0 / 5.0, 2.0 / 6.0, 3.0 / 7.0); + + a.component_div_assign(&b); + + assert_eq!(a, expected); + ``` + "; // FIXME: add other operators like bitshift, etc. ? ); diff --git a/src/base/construction.rs b/src/base/construction.rs index af371413..aeee6121 100644 --- a/src/base/construction.rs +++ b/src/base/construction.rs @@ -82,7 +82,7 @@ where DefaultAllocator: Allocator for i in 0..nrows.value() { for j in 0..ncols.value() { - unsafe { *res.get_unchecked_mut(i, j) = *iter.next().unwrap() } + unsafe { *res.get_unchecked_mut((i, j)) = *iter.next().unwrap() } } } @@ -105,7 +105,7 @@ where DefaultAllocator: Allocator for j in 0..ncols.value() { for i in 0..nrows.value() { - unsafe { *res.get_unchecked_mut(i, j) = f(i, j) } + unsafe { *res.get_unchecked_mut((i, j)) = f(i, j) } } } @@ -132,7 +132,7 @@ where DefaultAllocator: Allocator let mut res = Self::zeros_generic(nrows, ncols); for i in 0..::min(nrows.value(), ncols.value()) { - unsafe { *res.get_unchecked_mut(i, i) = elt } + unsafe { *res.get_unchecked_mut((i, i)) = elt } } res @@ -152,7 +152,7 @@ where DefaultAllocator: Allocator ); for (i, elt) in elts.iter().enumerate() { - unsafe { *res.get_unchecked_mut(i, i) = *elt } + unsafe { *res.get_unchecked_mut((i, i)) = *elt } } res @@ -162,6 +162,18 @@ where DefaultAllocator: Allocator /// /// Panics if not enough rows are provided (for statically-sized matrices), or if all rows do /// not have the same dimensions. + /// + /// # Example + /// ``` + /// # use nalgebra::{RowVector3, Matrix3}; + /// # use std::iter; + /// + /// let m = Matrix3::from_rows(&[ RowVector3::new(1.0, 2.0, 3.0), RowVector3::new(4.0, 5.0, 6.0), RowVector3::new(7.0, 8.0, 9.0) ]); + /// + /// assert!(m.m11 == 1.0 && m.m12 == 2.0 && m.m13 == 3.0 && + /// m.m21 == 4.0 && m.m22 == 5.0 && m.m23 == 6.0 && + /// m.m31 == 7.0 && m.m32 == 8.0 && m.m33 == 9.0); + /// ``` #[inline] pub fn from_rows(rows: &[Matrix]) -> Self where SB: Storage { @@ -190,6 +202,18 @@ where DefaultAllocator: Allocator /// /// Panics if not enough columns are provided (for statically-sized matrices), or if all /// columns do not have the same dimensions. + /// + /// # Example + /// ``` + /// # use nalgebra::{Vector3, Matrix3}; + /// # use std::iter; + /// + /// let m = Matrix3::from_columns(&[ Vector3::new(1.0, 2.0, 3.0), Vector3::new(4.0, 5.0, 6.0), Vector3::new(7.0, 8.0, 9.0) ]); + /// + /// assert!(m.m11 == 1.0 && m.m12 == 4.0 && m.m13 == 7.0 && + /// m.m21 == 2.0 && m.m22 == 5.0 && m.m23 == 8.0 && + /// m.m31 == 3.0 && m.m32 == 6.0 && m.m33 == 9.0); + /// ``` #[inline] pub fn from_columns(columns: &[Vector]) -> Self where SB: Storage { @@ -227,12 +251,35 @@ where DefaultAllocator: Allocator pub fn from_distribution_generic + ?Sized, G: Rng + ?Sized>( nrows: R, ncols: C, - distribution: &mut Distr, + distribution: &Distr, rng: &mut G, ) -> Self { Self::from_fn_generic(nrows, ncols, |_, _| distribution.sample(rng)) } + + /// Creates a matrix backed by a given `Vec`. + /// + /// The output matrix is filled column-by-column. + /// + /// # Example + /// ``` + /// # use nalgebra::{Dynamic, DMatrix, Matrix, U1}; + /// + /// let vec = vec![0, 1, 2, 3, 4, 5]; + /// let vec_ptr = vec.as_ptr(); + /// + /// let matrix = Matrix::from_vec_generic(Dynamic::new(vec.len()), U1, vec); + /// let matrix_storage_ptr = matrix.data.as_vec().as_ptr(); + /// + /// // `matrix` is backed by exactly the same `Vec` as it was constructed from. + /// assert_eq!(matrix_storage_ptr, vec_ptr); + /// ``` + #[inline] + #[cfg(feature = "std")] + pub fn from_vec_generic(nrows: R, ncols: C, data: Vec) -> Self { + Self::from_iterator_generic(nrows, ncols, data) + } } impl MatrixN @@ -241,6 +288,23 @@ where DefaultAllocator: Allocator, { /// Creates a square matrix with its diagonal set to `diag` and all other entries set to 0. + /// + /// # Example + /// ``` + /// # use nalgebra::{Vector3, DVector, Matrix3, DMatrix}; + /// # use std::iter; + /// + /// let m = Matrix3::from_diagonal(&Vector3::new(1.0, 2.0, 3.0)); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_diagonal(&DVector::from_row_slice(&[1.0, 2.0, 3.0])); + /// + /// assert!(m.m11 == 1.0 && m.m12 == 0.0 && m.m13 == 0.0 && + /// m.m21 == 0.0 && m.m22 == 2.0 && m.m23 == 0.0 && + /// m.m31 == 0.0 && m.m32 == 0.0 && m.m33 == 3.0); + /// assert!(dm[(0, 0)] == 1.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && + /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 2.0 && dm[(1, 2)] == 0.0 && + /// dm[(2, 0)] == 0.0 && dm[(2, 1)] == 0.0 && dm[(2, 2)] == 3.0); + /// ``` #[inline] pub fn from_diagonal>(diag: &Vector) -> Self where N: Zero { @@ -249,7 +313,7 @@ where for i in 0..diag.len() { unsafe { - *res.get_unchecked_mut(i, i) = *diag.vget_unchecked(i); + *res.get_unchecked_mut((i, i)) = *diag.vget_unchecked(i); } } @@ -267,60 +331,141 @@ macro_rules! impl_constructors( impl MatrixMN where DefaultAllocator: Allocator { - /// Creates a new uninitialized matrix. + /// Creates a new uninitialized matrix or vector. #[inline] pub unsafe fn new_uninitialized($($args: usize),*) -> Self { Self::new_uninitialized_generic($($gargs),*) } - /// Creates a matrix with all its elements set to `elem`. + /// Creates a matrix or vector with all its elements set to `elem`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// + /// let v = Vector3::from_element(2.0); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::from_element(3, 2.0); + /// let m = Matrix2x3::from_element(2.0); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_element(2, 3, 2.0); + /// + /// assert!(v.x == 2.0 && v.y == 2.0 && v.z == 2.0); + /// assert!(dv[0] == 2.0 && dv[1] == 2.0 && dv[2] == 2.0); + /// assert!(m.m11 == 2.0 && m.m12 == 2.0 && m.m13 == 2.0 && + /// m.m21 == 2.0 && m.m22 == 2.0 && m.m23 == 2.0); + /// assert!(dm[(0, 0)] == 2.0 && dm[(0, 1)] == 2.0 && dm[(0, 2)] == 2.0 && + /// dm[(1, 0)] == 2.0 && dm[(1, 1)] == 2.0 && dm[(1, 2)] == 2.0); + /// ``` #[inline] pub fn from_element($($args: usize,)* elem: N) -> Self { Self::from_element_generic($($gargs, )* elem) } - /// Creates a matrix with all its elements set to `elem`. + /// Creates a matrix or vector with all its elements set to `elem`. /// /// Same as `.from_element`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// + /// let v = Vector3::repeat(2.0); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::repeat(3, 2.0); + /// let m = Matrix2x3::repeat(2.0); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::repeat(2, 3, 2.0); + /// + /// assert!(v.x == 2.0 && v.y == 2.0 && v.z == 2.0); + /// assert!(dv[0] == 2.0 && dv[1] == 2.0 && dv[2] == 2.0); + /// assert!(m.m11 == 2.0 && m.m12 == 2.0 && m.m13 == 2.0 && + /// m.m21 == 2.0 && m.m22 == 2.0 && m.m23 == 2.0); + /// assert!(dm[(0, 0)] == 2.0 && dm[(0, 1)] == 2.0 && dm[(0, 2)] == 2.0 && + /// dm[(1, 0)] == 2.0 && dm[(1, 1)] == 2.0 && dm[(1, 2)] == 2.0); + /// ``` #[inline] pub fn repeat($($args: usize,)* elem: N) -> Self { Self::repeat_generic($($gargs, )* elem) } - /// Creates a matrix with all its elements set to `0`. + /// Creates a matrix or vector with all its elements set to `0`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// + /// let v = Vector3::::zeros(); + /// // The argument represents the vector dimension. + /// let dv = DVector::::zeros(3); + /// let m = Matrix2x3::::zeros(); + /// // The two arguments represent the matrix dimensions. + /// let dm = DMatrix::::zeros(2, 3); + /// + /// assert!(v.x == 0.0 && v.y == 0.0 && v.z == 0.0); + /// assert!(dv[0] == 0.0 && dv[1] == 0.0 && dv[2] == 0.0); + /// assert!(m.m11 == 0.0 && m.m12 == 0.0 && m.m13 == 0.0 && + /// m.m21 == 0.0 && m.m22 == 0.0 && m.m23 == 0.0); + /// assert!(dm[(0, 0)] == 0.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && + /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 0.0 && dm[(1, 2)] == 0.0); + /// ``` #[inline] pub fn zeros($($args: usize),*) -> Self where N: Zero { Self::zeros_generic($($gargs),*) } - /// Creates a matrix with all its elements filled by an iterator. + /// Creates a matrix or vector with all its elements filled by an iterator. + /// + /// The output matrix is filled column-by-column. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// # use std::iter; + /// + /// let v = Vector3::from_iterator((0..3).into_iter()); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::from_iterator(3, (0..3).into_iter()); + /// let m = Matrix2x3::from_iterator((0..6).into_iter()); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_iterator(2, 3, (0..6).into_iter()); + /// + /// assert!(v.x == 0 && v.y == 1 && v.z == 2); + /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); + /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && + /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && + /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); + /// ``` #[inline] pub fn from_iterator($($args: usize,)* iter: I) -> Self where I: IntoIterator { Self::from_iterator_generic($($gargs, )* iter) } - /// Creates a matrix with its elements filled with the components provided by a slice - /// in row-major order. - /// - /// The order of elements in the slice must follow the usual mathematic writing, i.e., - /// row-by-row. - #[inline] - pub fn from_row_slice($($args: usize,)* slice: &[N]) -> Self { - Self::from_row_slice_generic($($gargs, )* slice) - } - - /// Creates a matrix with its elements filled with the components provided by a slice - /// in column-major order. - #[inline] - pub fn from_column_slice($($args: usize,)* slice: &[N]) -> Self { - Self::from_column_slice_generic($($gargs, )* slice) - } - - /// Creates a matrix filled with the results of a function applied to each of its + /// Creates a matrix or vector filled with the results of a function applied to each of its /// component coordinates. - // FIXME: don't take a dimension of the matrix is statically sized. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// # use std::iter; + /// + /// let v = Vector3::from_fn(|i, _| i); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::from_fn(3, |i, _| i); + /// let m = Matrix2x3::from_fn(|i, j| i * 3 + j); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_fn(2, 3, |i, j| i * 3 + j); + /// + /// assert!(v.x == 0 && v.y == 1 && v.z == 2); + /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); + /// assert!(m.m11 == 0 && m.m12 == 1 && m.m13 == 2 && + /// m.m21 == 3 && m.m22 == 4 && m.m23 == 5); + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 1 && dm[(0, 2)] == 2 && + /// dm[(1, 0)] == 3 && dm[(1, 1)] == 4 && dm[(1, 2)] == 5); + /// ``` #[inline] pub fn from_fn($($args: usize,)* f: F) -> Self where F: FnMut(usize, usize) -> N { @@ -330,6 +475,21 @@ macro_rules! impl_constructors( /// Creates an identity matrix. If the matrix is not square, the largest square /// submatrix (starting at the first row and column) is set to the identity while all /// other entries are set to zero. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, DMatrix}; + /// # use std::iter; + /// + /// let m = Matrix2x3::::identity(); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::::identity(2, 3); + /// + /// assert!(m.m11 == 1.0 && m.m12 == 0.0 && m.m13 == 0.0 && + /// m.m21 == 0.0 && m.m22 == 1.0 && m.m23 == 0.0); + /// assert!(dm[(0, 0)] == 1.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && + /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 1.0 && dm[(1, 2)] == 0.0); + /// ``` #[inline] pub fn identity($($args: usize,)*) -> Self where N: Zero + One { @@ -338,6 +498,21 @@ macro_rules! impl_constructors( /// Creates a matrix filled with its diagonal filled with `elt` and all other /// components set to zero. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, DMatrix}; + /// # use std::iter; + /// + /// let m = Matrix2x3::from_diagonal_element(5.0); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_diagonal_element(2, 3, 5.0); + /// + /// assert!(m.m11 == 5.0 && m.m12 == 0.0 && m.m13 == 0.0 && + /// m.m21 == 0.0 && m.m22 == 5.0 && m.m23 == 0.0); + /// assert!(dm[(0, 0)] == 5.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && + /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 5.0 && dm[(1, 2)] == 0.0); + /// ``` #[inline] pub fn from_diagonal_element($($args: usize,)* elt: N) -> Self where N: Zero + One { @@ -348,17 +523,34 @@ macro_rules! impl_constructors( /// elements are filled with the content of `elts`. Others are set to 0. /// /// Panics if `elts.len()` is larger than the minimum among `nrows` and `ncols`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix3, DMatrix}; + /// # use std::iter; + /// + /// let m = Matrix3::from_partial_diagonal(&[1.0, 2.0]); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_partial_diagonal(3, 3, &[1.0, 2.0]); + /// + /// assert!(m.m11 == 1.0 && m.m12 == 0.0 && m.m13 == 0.0 && + /// m.m21 == 0.0 && m.m22 == 2.0 && m.m23 == 0.0 && + /// m.m31 == 0.0 && m.m32 == 0.0 && m.m33 == 0.0); + /// assert!(dm[(0, 0)] == 1.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && + /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 2.0 && dm[(1, 2)] == 0.0 && + /// dm[(2, 0)] == 0.0 && dm[(2, 1)] == 0.0 && dm[(2, 2)] == 0.0); + /// ``` #[inline] pub fn from_partial_diagonal($($args: usize,)* elts: &[N]) -> Self where N: Zero { Self::from_partial_diagonal_generic($($gargs, )* elts) } - /// Creates a matrix filled with random values from the given distribution. + /// Creates a matrix or vector filled with random values from the given distribution. #[inline] pub fn from_distribution + ?Sized, G: Rng + ?Sized>( $($args: usize,)* - distribution: &mut Distr, + distribution: &Distr, rng: &mut G, ) -> Self { Self::from_distribution_generic($($gargs, )* distribution, rng) @@ -401,6 +593,125 @@ impl_constructors!(Dynamic, Dynamic; Dynamic::new(nrows), Dynamic::new(ncols); nrows, ncols); +/* + * + * Constructors that don't necessarily require all dimensions + * to be specified whon one dimension is already known. + * + */ +macro_rules! impl_constructors_from_data( + ($data: ident; $($Dims: ty),*; $(=> $DimIdent: ident: $DimBound: ident),*; $($gargs: expr),*; $($args: ident),*) => { + impl MatrixMN + where DefaultAllocator: Allocator { + /// Creates a matrix with its elements filled with the components provided by a slice + /// in row-major order. + /// + /// The order of elements in the slice must follow the usual mathematic writing, i.e., + /// row-by-row. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// # use std::iter; + /// + /// let v = Vector3::from_row_slice(&[0, 1, 2]); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::from_row_slice(&[0, 1, 2]); + /// let m = Matrix2x3::from_row_slice(&[0, 1, 2, 3, 4, 5]); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_row_slice(2, 3, &[0, 1, 2, 3, 4, 5]); + /// + /// assert!(v.x == 0 && v.y == 1 && v.z == 2); + /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); + /// assert!(m.m11 == 0 && m.m12 == 1 && m.m13 == 2 && + /// m.m21 == 3 && m.m22 == 4 && m.m23 == 5); + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 1 && dm[(0, 2)] == 2 && + /// dm[(1, 0)] == 3 && dm[(1, 1)] == 4 && dm[(1, 2)] == 5); + /// ``` + #[inline] + pub fn from_row_slice($($args: usize,)* $data: &[N]) -> Self { + Self::from_row_slice_generic($($gargs, )* $data) + } + + /// Creates a matrix with its elements filled with the components provided by a slice + /// in column-major order. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// # use std::iter; + /// + /// let v = Vector3::from_column_slice(&[0, 1, 2]); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::from_column_slice(&[0, 1, 2]); + /// let m = Matrix2x3::from_column_slice(&[0, 1, 2, 3, 4, 5]); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_column_slice(2, 3, &[0, 1, 2, 3, 4, 5]); + /// + /// assert!(v.x == 0 && v.y == 1 && v.z == 2); + /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); + /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && + /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && + /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); + /// ``` + #[inline] + pub fn from_column_slice($($args: usize,)* $data: &[N]) -> Self { + Self::from_column_slice_generic($($gargs, )* $data) + } + + /// Creates a matrix backed by a given `Vec`. + /// + /// The output matrix is filled column-by-column. + /// + /// # Example + /// ``` + /// # use nalgebra::{DMatrix, Matrix2x3}; + /// + /// let m = Matrix2x3::from_vec(vec![0, 1, 2, 3, 4, 5]); + /// + /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && + /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); + /// + /// + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_vec(2, 3, vec![0, 1, 2, 3, 4, 5]); + /// + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && + /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); + /// ``` + #[inline] + #[cfg(feature = "std")] + pub fn from_vec($($args: usize,)* $data: Vec) -> Self { + Self::from_vec_generic($($gargs, )* $data) + } + } + } +); + +// FIXME: this is not very pretty. We could find a better call syntax. +impl_constructors_from_data!(data; R, C; // Arguments for Matrix + => R: DimName, => C: DimName; // Type parameters for impl + R::name(), C::name(); // Arguments for `_generic` constructors. + ); // Arguments for non-generic constructors. + +impl_constructors_from_data!(data; R, Dynamic; + => R: DimName; + R::name(), Dynamic::new(data.len() / R::dim()); + ); + +impl_constructors_from_data!(data; Dynamic, C; + => C: DimName; + Dynamic::new(data.len() / C::dim()), C::name(); + ); + +impl_constructors_from_data!(data; Dynamic, Dynamic; + ; + Dynamic::new(nrows), Dynamic::new(ncols); + nrows, ncols); + + + /* * * Zero, One, Rand traits. @@ -495,7 +806,7 @@ where Unit::new_normalize(VectorN::from_distribution_generic( D::name(), U1, - &mut StandardNormal, + &StandardNormal, rng, )) } @@ -516,7 +827,7 @@ macro_rules! componentwise_constructors_impl( pub fn new($($args: N),*) -> Self { unsafe { let mut res = Self::new_uninitialized(); - $( *res.get_unchecked_mut($irow, $icol) = $args; )* + $( *res.get_unchecked_mut(($irow, $icol)) = $args; )* res } diff --git a/src/base/conversion.rs b/src/base/conversion.rs index 930fc24d..2e5ca9df 100644 --- a/src/base/conversion.rs +++ b/src/base/conversion.rs @@ -17,8 +17,8 @@ use base::dimension::{ use base::iter::{MatrixIter, MatrixIterMut}; use base::storage::{ContiguousStorage, ContiguousStorageMut, Storage, StorageMut}; #[cfg(any(feature = "std", feature = "alloc"))] -use base::MatrixVec; -use base::{DefaultAllocator, Matrix, MatrixArray, MatrixMN, MatrixSlice, MatrixSliceMut, Scalar}; +use base::VecStorage; +use base::{DefaultAllocator, Matrix, ArrayStorage, MatrixMN, MatrixSlice, MatrixSliceMut, Scalar}; // FIXME: too bad this won't work allo slice conversions. impl SubsetOf> for MatrixMN @@ -42,7 +42,7 @@ where let mut res = unsafe { MatrixMN::::new_uninitialized_generic(nrows2, ncols2) }; for i in 0..nrows { for j in 0..ncols { - unsafe { *res.get_unchecked_mut(i, j) = N2::from_subset(self.get_unchecked(i, j)) } + unsafe { *res.get_unchecked_mut((i, j)) = N2::from_subset(self.get_unchecked((i, j))) } } } @@ -63,7 +63,7 @@ where let mut res = Self::new_uninitialized_generic(nrows, ncols); for i in 0..nrows2 { for j in 0..ncols2 { - *res.get_unchecked_mut(i, j) = m.get_unchecked(i, j).to_subset_unchecked() + *res.get_unchecked_mut((i, j)) = m.get_unchecked((i, j)).to_subset_unchecked() } } @@ -336,7 +336,7 @@ impl_from_into_mint_2D!( ); impl<'a, N, R, C, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, R: DimName, @@ -353,7 +353,7 @@ where #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, N, C, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, C: Dim, @@ -367,7 +367,7 @@ where #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, N, R, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, R: DimName, @@ -380,7 +380,7 @@ where } impl<'a, N, R, C, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, R: DimName, @@ -397,7 +397,7 @@ where #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, N, C, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, C: Dim, @@ -411,7 +411,7 @@ where #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, N, R, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, R: DimName, diff --git a/src/base/default_allocator.rs b/src/base/default_allocator.rs index 6bdbd30d..5926f39d 100644 --- a/src/base/default_allocator.rs +++ b/src/base/default_allocator.rs @@ -18,9 +18,9 @@ use base::allocator::{Allocator, Reallocator}; #[cfg(any(feature = "alloc", feature = "std"))] use base::dimension::Dynamic; use base::dimension::{Dim, DimName}; -use base::matrix_array::MatrixArray; +use base::array_storage::ArrayStorage; #[cfg(any(feature = "std", feature = "alloc"))] -use base::matrix_vec::MatrixVec; +use base::vec_storage::VecStorage; use base::storage::{Storage, StorageMut}; use base::Scalar; @@ -29,7 +29,7 @@ use base::Scalar; * Allocator. * */ -/// An allocator based on `GenericArray` and `MatrixVec` for statically-sized and dynamically-sized +/// An allocator based on `GenericArray` and `VecStorage` for statically-sized and dynamically-sized /// matrices respectively. pub struct DefaultAllocator; @@ -42,7 +42,7 @@ where R::Value: Mul, Prod: ArrayLength, { - type Buffer = MatrixArray; + type Buffer = ArrayStorage; #[inline] unsafe fn allocate_uninitialized(_: R, _: C) -> Self::Buffer { @@ -77,7 +77,7 @@ where // Dynamic - Dynamic #[cfg(any(feature = "std", feature = "alloc"))] impl Allocator for DefaultAllocator { - type Buffer = MatrixVec; + type Buffer = VecStorage; #[inline] unsafe fn allocate_uninitialized(nrows: Dynamic, ncols: C) -> Self::Buffer { @@ -86,7 +86,7 @@ impl Allocator for DefaultAllocator { res.reserve_exact(length); res.set_len(length); - MatrixVec::new(nrows, ncols, res) + VecStorage::new(nrows, ncols, res) } #[inline] @@ -101,14 +101,14 @@ impl Allocator for DefaultAllocator { assert!(res.len() == nrows.value() * ncols.value(), "Allocation from iterator error: the iterator did not yield the correct number of elements."); - MatrixVec::new(nrows, ncols, res) + VecStorage::new(nrows, ncols, res) } } // Static - Dynamic #[cfg(any(feature = "std", feature = "alloc"))] impl Allocator for DefaultAllocator { - type Buffer = MatrixVec; + type Buffer = VecStorage; #[inline] unsafe fn allocate_uninitialized(nrows: R, ncols: Dynamic) -> Self::Buffer { @@ -117,7 +117,7 @@ impl Allocator for DefaultAllocator { res.reserve_exact(length); res.set_len(length); - MatrixVec::new(nrows, ncols, res) + VecStorage::new(nrows, ncols, res) } #[inline] @@ -132,7 +132,7 @@ impl Allocator for DefaultAllocator { assert!(res.len() == nrows.value() * ncols.value(), "Allocation from iterator error: the iterator did not yield the correct number of elements."); - MatrixVec::new(nrows, ncols, res) + VecStorage::new(nrows, ncols, res) } } @@ -157,7 +157,7 @@ where rto: RTo, cto: CTo, buf: >::Buffer, - ) -> MatrixArray + ) -> ArrayStorage { let mut res = >::allocate_uninitialized(rto, cto); @@ -185,8 +185,8 @@ where unsafe fn reallocate_copy( rto: Dynamic, cto: CTo, - buf: MatrixArray, - ) -> MatrixVec + buf: ArrayStorage, + ) -> VecStorage { let mut res = >::allocate_uninitialized(rto, cto); @@ -214,8 +214,8 @@ where unsafe fn reallocate_copy( rto: RTo, cto: Dynamic, - buf: MatrixArray, - ) -> MatrixVec + buf: ArrayStorage, + ) -> VecStorage { let mut res = >::allocate_uninitialized(rto, cto); @@ -238,11 +238,11 @@ impl Reallocator, - ) -> MatrixVec + buf: VecStorage, + ) -> VecStorage { let new_buf = buf.resize(rto.value() * cto.value()); - MatrixVec::new(rto, cto, new_buf) + VecStorage::new(rto, cto, new_buf) } } @@ -254,11 +254,11 @@ impl Reallocator, - ) -> MatrixVec + buf: VecStorage, + ) -> VecStorage { let new_buf = buf.resize(rto.value() * cto.value()); - MatrixVec::new(rto, cto, new_buf) + VecStorage::new(rto, cto, new_buf) } } @@ -270,11 +270,11 @@ impl Reallocator, - ) -> MatrixVec + buf: VecStorage, + ) -> VecStorage { let new_buf = buf.resize(rto.value() * cto.value()); - MatrixVec::new(rto, cto, new_buf) + VecStorage::new(rto, cto, new_buf) } } @@ -286,10 +286,10 @@ impl Reallocator, - ) -> MatrixVec + buf: VecStorage, + ) -> VecStorage { let new_buf = buf.resize(rto.value() * cto.value()); - MatrixVec::new(rto, cto, new_buf) + VecStorage::new(rto, cto, new_buf) } } diff --git a/src/base/dimension.rs b/src/base/dimension.rs index 1d79d48e..9ba509b9 100644 --- a/src/base/dimension.rs +++ b/src/base/dimension.rs @@ -181,7 +181,7 @@ dim_ops!( DimMul, DimNameMul, Mul, mul, Mul::mul, DimProd, DimNameProd, Prod; DimSub, DimNameSub, Sub, sub, Sub::sub, DimDiff, DimNameDiff, Diff; DimDiv, DimNameDiv, Div, div, Div::div, DimQuot, DimNameQuot, Quot; - DimMin, DimNameMin, Min, min, cmp::min, DimMinimum, DimNameNimimum, Minimum; + DimMin, DimNameMin, Min, min, cmp::min, DimMinimum, DimNameMinimum, Minimum; DimMax, DimNameMax, Max, max, cmp::max, DimMaximum, DimNameMaximum, Maximum; ); diff --git a/src/base/edition.rs b/src/base/edition.rs index 74acd5d9..82e5d7ea 100644 --- a/src/base/edition.rs +++ b/src/base/edition.rs @@ -1,6 +1,7 @@ use num::{One, Zero}; use std::cmp; use std::ptr; +use std::iter::ExactSizeIterator; use std::mem; use base::allocator::{Allocator, Reallocator}; @@ -24,7 +25,7 @@ impl> Matrix { res } - /// Extracts the upper triangular part of this matrix (including the diagonal). + /// Extracts the lower triangular part of this matrix (including the diagonal). #[inline] pub fn lower_triangle(&self) -> MatrixMN where DefaultAllocator: Allocator { @@ -33,6 +34,52 @@ impl> Matrix { res } + + /// Creates a new matrix by extracting the given set of rows from `self`. + pub fn select_rows<'a, I>(&self, irows: I) -> MatrixMN + where I: IntoIterator, + I::IntoIter: ExactSizeIterator + Clone, + DefaultAllocator: Allocator { + let irows = irows.into_iter(); + let ncols = self.data.shape().1; + let mut res = unsafe { MatrixMN::new_uninitialized_generic(Dynamic::new(irows.len()), ncols) }; + + // First, check that all the indices from irows are valid. + // This will allow us to use unchecked access in the inner loop. + for i in irows.clone() { + assert!(*i < self.nrows(), "Row index out of bounds.") + } + + for j in 0..ncols.value() { + // FIXME: use unchecked column indexing + let mut res = res.column_mut(j); + let mut src = self.column(j); + + for (destination, source) in irows.clone().enumerate() { + unsafe { + *res.vget_unchecked_mut(destination) = *src.vget_unchecked(*source) + } + } + } + + res + } + + /// Creates a new matrix by extracting the given set of columns from `self`. + pub fn select_columns<'a, I>(&self, icols: I) -> MatrixMN + where I: IntoIterator, + I::IntoIter: ExactSizeIterator, + DefaultAllocator: Allocator { + let icols = icols.into_iter(); + let nrows = self.data.shape().0; + let mut res = unsafe { MatrixMN::new_uninitialized_generic(nrows, Dynamic::new(icols.len())) }; + + for (destination, source) in icols.enumerate() { + res.column_mut(destination).copy_from(&self.column(*source)) + } + + res + } } impl> Matrix { @@ -59,7 +106,7 @@ impl> Matrix { let n = cmp::min(nrows, ncols); for i in 0..n { - unsafe { *self.get_unchecked_mut(i, i) = val } + unsafe { *self.get_unchecked_mut((i, i)) = val } } } @@ -68,7 +115,7 @@ impl> Matrix { pub fn fill_row(&mut self, i: usize, val: N) { assert!(i < self.nrows(), "Row index out of bounds."); for j in 0..self.ncols() { - unsafe { *self.get_unchecked_mut(i, j) = val } + unsafe { *self.get_unchecked_mut((i, j)) = val } } } @@ -77,7 +124,7 @@ impl> Matrix { pub fn fill_column(&mut self, j: usize, val: N) { assert!(j < self.ncols(), "Row index out of bounds."); for i in 0..self.nrows() { - unsafe { *self.get_unchecked_mut(i, j) = val } + unsafe { *self.get_unchecked_mut((i, j)) = val } } } @@ -94,7 +141,7 @@ impl> Matrix { assert_eq!(diag.len(), min_nrows_ncols, "Mismatched dimensions."); for i in 0..min_nrows_ncols { - unsafe { *self.get_unchecked_mut(i, i) = *diag.vget_unchecked(i) } + unsafe { *self.get_unchecked_mut((i, i)) = *diag.vget_unchecked(i) } } } @@ -129,7 +176,7 @@ impl> Matrix { pub fn fill_lower_triangle(&mut self, val: N, shift: usize) { for j in 0..self.ncols() { for i in (j + shift)..self.nrows() { - unsafe { *self.get_unchecked_mut(i, j) = val } + unsafe { *self.get_unchecked_mut((i, j)) = val } } } } @@ -147,7 +194,7 @@ impl> Matrix { // FIXME: is there a more efficient way to avoid the min ? // (necessary for rectangular matrices) for i in 0..cmp::min(j + 1 - shift, self.nrows()) { - unsafe { *self.get_unchecked_mut(i, j) = val } + unsafe { *self.get_unchecked_mut((i, j)) = val } } } } @@ -192,7 +239,7 @@ impl> Matrix { for j in 0..dim { for i in j + 1..dim { unsafe { - *self.get_unchecked_mut(i, j) = *self.get_unchecked(j, i); + *self.get_unchecked_mut((i, j)) = *self.get_unchecked((j, i)); } } } @@ -207,7 +254,7 @@ impl> Matrix { for j in 1..self.ncols() { for i in 0..j { unsafe { - *self.get_unchecked_mut(i, j) = *self.get_unchecked(j, i); + *self.get_unchecked_mut((i, j)) = *self.get_unchecked((j, i)); } } } @@ -780,3 +827,136 @@ unsafe fn extend_rows( ); } } + +/// Extend the number of columns of the `Matrix` with elements from +/// a given iterator. +impl Extend for Matrix +where + N: Scalar, + R: Dim, + S: Extend, +{ + /// Extend the number of columns of the `Matrix` with elements + /// from the given iterator. + /// + /// # Example + /// ``` + /// # use nalgebra::{DMatrix, Dynamic, Matrix, MatrixMN, Matrix3}; + /// + /// let data = vec![0, 1, 2, // column 1 + /// 3, 4, 5]; // column 2 + /// + /// let mut matrix = DMatrix::from_vec(3, 2, data); + /// + /// matrix.extend(vec![6, 7, 8]); // column 3 + /// + /// assert!(matrix.eq(&Matrix3::new(0, 3, 6, + /// 1, 4, 7, + /// 2, 5, 8))); + /// ``` + /// + /// # Panics + /// This function panics if the number of elements yielded by the + /// given iterator is not a multiple of the number of rows of the + /// `Matrix`. + /// + /// ```should_panic + /// # use nalgebra::{DMatrix, Dynamic, MatrixMN}; + /// let data = vec![0, 1, 2, // column 1 + /// 3, 4, 5]; // column 2 + /// + /// let mut matrix = DMatrix::from_vec(3, 2, data); + /// + /// // The following panics because the vec length is not a multiple of 3. + /// matrix.extend(vec![6, 7, 8, 9]); + /// ``` + fn extend>(&mut self, iter: I) { + self.data.extend(iter); + } +} + +/// Extend the number of rows of the `Vector` with elements from +/// a given iterator. +impl Extend for Matrix +where + N: Scalar, + S: Extend, +{ + /// Extend the number of rows of a `Vector` with elements + /// from the given iterator. + /// + /// # Example + /// ``` + /// # use nalgebra::DVector; + /// let mut vector = DVector::from_vec(vec![0, 1, 2]); + /// vector.extend(vec![3, 4, 5]); + /// assert!(vector.eq(&DVector::from_vec(vec![0, 1, 2, 3, 4, 5]))); + /// ``` + fn extend>(&mut self, iter: I) { + self.data.extend(iter); + } +} + +impl Extend> for Matrix +where + N: Scalar, + R: Dim, + S: Extend>, + RV: Dim, + SV: Storage, + ShapeConstraint: SameNumberOfRows, +{ + /// Extends the number of columns of a `Matrix` with `Vector`s + /// from a given iterator. + /// + /// # Example + /// ``` + /// # use nalgebra::{DMatrix, Vector3, Matrix3x4}; + /// + /// let data = vec![0, 1, 2, // column 1 + /// 3, 4, 5]; // column 2 + /// + /// let mut matrix = DMatrix::from_vec(3, 2, data); + /// + /// matrix.extend( + /// vec![Vector3::new(6, 7, 8), // column 3 + /// Vector3::new(9, 10, 11)]); // column 4 + /// + /// assert!(matrix.eq(&Matrix3x4::new(0, 3, 6, 9, + /// 1, 4, 7, 10, + /// 2, 5, 8, 11))); + /// ``` + /// + /// # Panics + /// This function panics if the dimension of each `Vector` yielded + /// by the given iterator is not equal to the number of rows of + /// this `Matrix`. + /// + /// ```should_panic + /// # use nalgebra::{DMatrix, Vector2, Matrix3x4}; + /// let mut matrix = + /// DMatrix::from_vec(3, 2, + /// vec![0, 1, 2, // column 1 + /// 3, 4, 5]); // column 2 + /// + /// // The following panics because this matrix can only be extended with 3-dimensional vectors. + /// matrix.extend( + /// vec![Vector2::new(6, 7)]); // too few dimensions! + /// ``` + /// + /// ```should_panic + /// # use nalgebra::{DMatrix, Vector4, Matrix3x4}; + /// let mut matrix = + /// DMatrix::from_vec(3, 2, + /// vec![0, 1, 2, // column 1 + /// 3, 4, 5]); // column 2 + /// + /// // The following panics because this matrix can only be extended with 3-dimensional vectors. + /// matrix.extend( + /// vec![Vector4::new(6, 7, 8, 9)]); // too few dimensions! + /// ``` + fn extend>>(&mut self, iter: I) + { + self.data.extend(iter); + } +} diff --git a/src/base/indexing.rs b/src/base/indexing.rs new file mode 100644 index 00000000..976eef87 --- /dev/null +++ b/src/base/indexing.rs @@ -0,0 +1,714 @@ +//! Indexing + +use base::{Dim, DimName, DimDiff, DimSub, Dynamic, Matrix, MatrixSlice, MatrixSliceMut, Scalar, U1}; +use base::storage::{Storage, StorageMut}; + +use std::ops; + +// N.B.: Not a public trait! +trait DimRange +{ + /// The number of elements indexed by this range. + type Length: Dim; + + /// The lower bound of the range, inclusive. + fn lower(&self, dimension: D) -> usize; + + /// The number of elements included in the range. + fn length(&self, dimension: D) -> Self::Length; + + /// Produces true if `Self` is contained within `dimension`. + fn contained_by(&self, dimension: D) -> bool; +} + +impl DimRange for usize { + type Length = U1; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + *self + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + U1 + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + *self < dimension.value() + } +} + +#[test] +fn dimrange_usize() { + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&0, U0), false); + assert_eq!(DimRange::contained_by(&0, U1), true); +} + +impl DimRange for ops::Range { + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + self.start + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + Dynamic::new(self.end.saturating_sub(self.start)) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + (self.start < dimension.value()) && (self.end <= dimension.value()) + } +} + +#[test] +fn dimrange_range_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(0..0), U0), false); + assert_eq!(DimRange::contained_by(&(0..1), U0), false); + assert_eq!(DimRange::contained_by(&(0..1), U1), true); + assert_eq!(DimRange::contained_by(&((MAX - 1)..MAX), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&((MAX - 1)..MAX), Dynamic::new(MAX)), Dynamic::new(1)); + assert_eq!(DimRange::length(&(MAX..(MAX - 1)), Dynamic::new(MAX)), Dynamic::new(0)); + assert_eq!(DimRange::length(&(MAX..MAX), Dynamic::new(MAX)), Dynamic::new(0)); +} + +impl DimRange for ops::RangeFrom { + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + self.start + } + + #[inline(always)] + fn length(&self, dimension: D) -> Self::Length { + (self.start..dimension.value()).length(dimension) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + self.start < dimension.value() + } +} + +#[test] +fn dimrange_rangefrom_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(0..), U0), false); + assert_eq!(DimRange::contained_by(&(0..), U0), false); + assert_eq!(DimRange::contained_by(&(0..), U1), true); + assert_eq!(DimRange::contained_by(&((MAX - 1)..), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&((MAX - 1)..), Dynamic::new(MAX)), Dynamic::new(1)); + assert_eq!(DimRange::length(&(MAX..), Dynamic::new(MAX)), Dynamic::new(0)); +} + +impl DimRange for ops::RangeFrom +where D: DimSub +{ + type Length = DimDiff; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + self.start.value() + } + + #[inline(always)] + fn length(&self, dimension: D) -> Self::Length { + dimension.sub(self.start) + } + + #[inline(always)] + fn contained_by(&self, _: D) -> bool { + true + } +} + +#[test] +fn dimrange_rangefrom_dimname() { + use base::dimension::{U5, U4}; + assert_eq!(DimRange::length(&(U1..), U5), U4); +} + +impl DimRange for ops::RangeFull { + type Length = D; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + 0 + } + + #[inline(always)] + fn length(&self, dimension: D) -> Self::Length { + dimension + } + + #[inline(always)] + fn contained_by(&self, _: D) -> bool { + true + } +} + +#[test] +fn dimrange_rangefull() { + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(..), U0), true); + assert_eq!(DimRange::length(&(..), U1), U1); +} + +impl DimRange for ops::RangeInclusive { + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + *self.start() + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + Dynamic::new( + if self.end() < self.start() { + 0 + } else { + self.end().wrapping_sub(self.start().wrapping_sub(1)) + }) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + (*self.start() < dimension.value()) && (*self.end() < dimension.value()) + } +} + +#[test] +fn dimrange_rangeinclusive_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(0..=0), U0), false); + assert_eq!(DimRange::contained_by(&(0..=0), U1), true); + assert_eq!(DimRange::contained_by(&(MAX..=MAX), Dynamic::new(MAX)), false); + assert_eq!(DimRange::contained_by(&((MAX-1)..=MAX), Dynamic::new(MAX)), false); + assert_eq!(DimRange::contained_by(&((MAX-1)..=(MAX-1)), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&(0..=0), U1), Dynamic::new(1)); + assert_eq!(DimRange::length(&((MAX - 1)..=MAX), Dynamic::new(MAX)), Dynamic::new(2)); + assert_eq!(DimRange::length(&(MAX..=(MAX - 1)), Dynamic::new(MAX)), Dynamic::new(0)); + assert_eq!(DimRange::length(&(MAX..=MAX), Dynamic::new(MAX)), Dynamic::new(1)); +} + +impl DimRange for ops::RangeTo +{ + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + 0 + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + Dynamic::new(self.end) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + self.end <= dimension.value() + } +} + +#[test] +fn dimrange_rangeto_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(..0), U0), true); + assert_eq!(DimRange::contained_by(&(..1), U0), false); + assert_eq!(DimRange::contained_by(&(..0), U1), true); + assert_eq!(DimRange::contained_by(&(..(MAX - 1)), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&(..(MAX - 1)), Dynamic::new(MAX)), Dynamic::new(MAX - 1)); + assert_eq!(DimRange::length(&(..MAX), Dynamic::new(MAX)), Dynamic::new(MAX)); +} + +impl DimRange for ops::RangeToInclusive +{ + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + 0 + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + Dynamic::new(self.end + 1) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + self.end < dimension.value() + } +} + +#[test] +fn dimrange_rangetoinclusive_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(..=0), U0), false); + assert_eq!(DimRange::contained_by(&(..=1), U0), false); + assert_eq!(DimRange::contained_by(&(..=0), U1), true); + assert_eq!(DimRange::contained_by(&(..=(MAX)), Dynamic::new(MAX)), false); + assert_eq!(DimRange::contained_by(&(..=(MAX - 1)), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&(..=(MAX - 1)), Dynamic::new(MAX)), Dynamic::new(MAX)); +} + +/// A helper trait used for indexing operations. +pub trait MatrixIndex<'a, N: Scalar, R: Dim, C: Dim, S: Storage>: Sized { + + /// The output type returned by methods. + type Output : 'a; + + /// Produces true if the given matrix is contained by this index. + #[doc(hidden)] + fn contained_by(&self, matrix: &Matrix) -> bool; + + /// Produces a shared view of the data at this location if in bounds, + /// or `None`, otherwise. + #[doc(hidden)] + #[inline(always)] + fn get(self, matrix: &'a Matrix) -> Option { + if self.contained_by(matrix) { + Some(unsafe{self.get_unchecked(matrix)}) + } else { + None + } + } + + /// Produces a shared view of the data at this location if in bounds + /// without any bounds checking. + #[doc(hidden)] + unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output; + + /// Produces a shared view to the data at this location, or panics + /// if out of bounds. + #[doc(hidden)] + #[inline(always)] + fn index(self, matrix: &'a Matrix) -> Self::Output { + self.get(matrix).expect("Index out of bounds.") + } +} + +/// A helper trait used for indexing operations. +pub trait MatrixIndexMut<'a, N: Scalar, R: Dim, C: Dim, S: StorageMut>: MatrixIndex<'a, N, R, C, S> { + /// The output type returned by methods. + type OutputMut : 'a; + + /// Produces a mutable view of the data at this location, without + /// performing any bounds checking. + #[doc(hidden)] + unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut; + + /// Produces a mutable view of the data at this location, if in + /// bounds. + #[doc(hidden)] + #[inline(always)] + fn get_mut(self, matrix: &'a mut Matrix) -> Option { + if self.contained_by(matrix) { + Some(unsafe{self.get_unchecked_mut(matrix)}) + } else { + None + } + } + + /// Produces a mutable view of the data at this location, or panics + /// if out of bounds. + #[doc(hidden)] + #[inline(always)] + fn index_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut { + self.get_mut(matrix).expect("Index out of bounds.") + } +} + +/// # Indexing Operations +/// ## Indices to Individual Elements +/// ### Two-Dimensional Indices +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix2::new(0, 2, +/// 1, 3); +/// +/// assert_eq!(matrix.index((0, 0)), &0); +/// assert_eq!(matrix.index((1, 0)), &1); +/// assert_eq!(matrix.index((0, 1)), &2); +/// assert_eq!(matrix.index((1, 1)), &3); +/// ``` +/// +/// ### Linear Address Indexing +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix2::new(0, 2, +/// 1, 3); +/// +/// assert_eq!(matrix.get(0), Some(&0)); +/// assert_eq!(matrix.get(1), Some(&1)); +/// assert_eq!(matrix.get(2), Some(&2)); +/// assert_eq!(matrix.get(3), Some(&3)); +/// ``` +/// +/// ## Indices to Individual Rows and Columns +/// ### Index to a Row +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix2::new(0, 2, +/// 1, 3); +/// +/// assert!(matrix.index((0, ..)) +/// .eq(&Matrix1x2::new(0, 2))); +/// ``` +/// +/// ### Index to a Column +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix2::new(0, 2, +/// 1, 3); +/// +/// assert!(matrix.index((.., 0)) +/// .eq(&Matrix2x1::new(0, +/// 1))); +/// ``` +/// +/// ## Indices to Parts of Individual Rows and Columns +/// ### Index to a Partial Row +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix3::new(0, 3, 6, +/// 1, 4, 7, +/// 2, 5, 8); +/// +/// assert!(matrix.index((0, ..2)) +/// .eq(&Matrix1x2::new(0, 3))); +/// ``` +/// +/// ### Index to a Partial Column +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix3::new(0, 3, 6, +/// 1, 4, 7, +/// 2, 5, 8); +/// +/// assert!(matrix.index((..2, 0)) +/// .eq(&Matrix2x1::new(0, +/// 1))); +/// +/// assert!(matrix.index((U1.., 0)) +/// .eq(&Matrix2x1::new(1, +/// 2))); +/// ``` +/// ## Indices to Ranges of Rows and Columns +/// ### Index to a Range of Rows +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix3::new(0, 3, 6, +/// 1, 4, 7, +/// 2, 5, 8); +/// +/// assert!(matrix.index((1..3, ..)) +/// .eq(&Matrix2x3::new(1, 4, 7, +/// 2, 5, 8))); +/// ``` +/// ### Index to a Range of Columns +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix3::new(0, 3, 6, +/// 1, 4, 7, +/// 2, 5, 8); +/// +/// assert!(matrix.index((.., 1..3)) +/// .eq(&Matrix3x2::new(3, 6, +/// 4, 7, +/// 5, 8))); +/// ``` +impl> Matrix +{ + /// Produces a view of the data at the given index, or + /// `None` if the index is out of bounds. + #[inline] + pub fn get<'a, I>(&'a self, index: I) -> Option + where + I: MatrixIndex<'a, N, R, C, S> + { + index.get(self) + } + + /// Produces a mutable view of the data at the given index, or + /// `None` if the index is out of bounds. + #[inline] + pub fn get_mut<'a, I>(&'a mut self, index: I) -> Option + where + S: StorageMut, + I: MatrixIndexMut<'a, N, R, C, S> + { + index.get_mut(self) + } + + /// Produces a view of the data at the given index, or + /// panics if the index is out of bounds. + #[inline] + pub fn index<'a, I>(&'a self, index: I) -> I::Output + where + I: MatrixIndex<'a, N, R, C, S> + { + index.index(self) + } + + /// Produces a mutable view of the data at the given index, or + /// panics if the index is out of bounds. + #[inline] + pub fn index_mut<'a, I>(&'a mut self, index: I) -> I::OutputMut + where + S: StorageMut, + I: MatrixIndexMut<'a, N, R, C, S> + { + index.index_mut(self) + } + + /// Produces a view of the data at the given index, without doing + /// any bounds checking. + #[inline] + pub unsafe fn get_unchecked<'a, I>(&'a self, index: I) -> I::Output + where + I: MatrixIndex<'a, N, R, C, S> + { + index.get_unchecked(self) + } + + /// Returns a mutable view of the data at the given index, without doing + /// any bounds checking. + #[inline] + pub unsafe fn get_unchecked_mut<'a, I>(&'a mut self, index: I) -> I::OutputMut + where + S: StorageMut, + I: MatrixIndexMut<'a, N, R, C, S> + { + index.get_unchecked_mut(self) + } +} + +// EXTRACT A SINGLE ELEMENT BY 1D LINEAR ADDRESS + +impl<'a, N, R, C, S> MatrixIndex<'a, N, R, C, S> for usize +where + N: Scalar, + R: Dim, + C: Dim, + S: Storage +{ + type Output = &'a N; + + #[doc(hidden)] + #[inline(always)] + fn contained_by(&self, matrix: &Matrix) -> bool { + *self < matrix.len() + } + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output { + matrix.data.get_unchecked_linear(self) + } +} + +impl<'a, N, R, C, S> MatrixIndexMut<'a, N, R, C, S> for usize +where + N: Scalar, + R: Dim, + C: Dim, + S: StorageMut +{ + type OutputMut = &'a mut N; + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut + where S: StorageMut, + { + matrix.data.get_unchecked_linear_mut(self) + } +} + +// EXTRACT A SINGLE ELEMENT BY 2D COORDINATES + +impl<'a, N, R, C, S> MatrixIndex<'a, N, R, C, S> for (usize, usize) +where + N: Scalar, + R: Dim, + C: Dim, + S: Storage +{ + type Output = &'a N; + + #[doc(hidden)] + #[inline(always)] + fn contained_by(&self, matrix: &Matrix) -> bool { + let (rows, cols) = self; + let (nrows, ncols) = matrix.data.shape(); + DimRange::contained_by(rows, nrows) && DimRange::contained_by(cols, ncols) + } + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output { + let (row, col) = self; + matrix.data.get_unchecked(row, col) + } +} + +impl<'a, N, R, C, S> MatrixIndexMut<'a, N, R, C, S> for (usize, usize) +where + N: Scalar, + R: Dim, + C: Dim, + S: StorageMut +{ + type OutputMut = &'a mut N; + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut + where S: StorageMut, + { + let (row, col) = self; + matrix.data.get_unchecked_mut(row, col) + } +} + +macro_rules! impl_index_pair { + ( + $R: ident, + $C: ident, + [<$($RTyP: ident : $RTyPB: ty,)*> usize => $ROut: ty + $(where $RConstraintType: ty: $RConstraintBound: ident<$($RConstraintBoundParams: ty $( = $REqBound: ty )*),*>)*], + [<$($CTyP: ident : $CTyPB: ty,)*> usize => $COut: ty + $(where $CConstraintType: ty: $CConstraintBound: ident<$($CConstraintBoundParams: ty $( = $CEqBound: ty )*),*>)*] + ) => {}; + + ( + $R: ident, + $C: ident, + [<$($RTyP: ident: $RTyPB: tt),*> $RIdx: ty => $ROut: ty + $(where $RConstraintType: ty: $RConstraintBound: ident $(<$($RConstraintBoundParams: ty $( = $REqBound: ty )*),*>)* )*], + [<$($CTyP: ident: $CTyPB: tt),*> $CIdx: ty => $COut: ty + $(where $CConstraintType: ty: $CConstraintBound: ident $(<$($CConstraintBoundParams: ty $( = $CEqBound: ty )*),*>)* )*] + ) => + { + impl<'a, N, $R, $C, S, $($RTyP : $RTyPB,)* $($CTyP : $CTyPB),*> MatrixIndex<'a, N, $R, $C, S> for ($RIdx, $CIdx) + where + N: Scalar, + $R: Dim, + $C: Dim, + S: Storage, + $( $RConstraintType: $RConstraintBound $(<$( $RConstraintBoundParams $( = $REqBound )*),*>)* ,)* + $( $CConstraintType: $CConstraintBound $(<$( $CConstraintBoundParams $( = $CEqBound )*),*>)* ),* + { + type Output = MatrixSlice<'a, N, $ROut, $COut, S::RStride, S::CStride>; + + #[doc(hidden)] + #[inline(always)] + fn contained_by(&self, matrix: &Matrix) -> bool { + let (rows, cols) = self; + let (nrows, ncols) = matrix.data.shape(); + DimRange::contained_by(rows, nrows) && DimRange::contained_by(cols, ncols) + } + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output { + use base::SliceStorage; + + let (rows, cols) = self; + let (nrows, ncols) = matrix.data.shape(); + + let data = + SliceStorage::new_unchecked(&matrix.data, + (rows.lower(nrows), cols.lower(ncols)), + (rows.length(nrows), cols.length(ncols))); + + Matrix::from_data_statically_unchecked(data) + } + } + + impl<'a, N, $R, $C, S, $($RTyP : $RTyPB,)* $($CTyP : $CTyPB),*> MatrixIndexMut<'a, N, $R, $C, S> for ($RIdx, $CIdx) + where + N: Scalar, + $R: Dim, + $C: Dim, + S: StorageMut, + $( $RConstraintType: $RConstraintBound $(<$( $RConstraintBoundParams $( = $REqBound )*),*>)* ,)* + $( $CConstraintType: $CConstraintBound $(<$( $CConstraintBoundParams $( = $CEqBound )*),*>)* ),* + { + type OutputMut = MatrixSliceMut<'a, N, $ROut, $COut, S::RStride, S::CStride>; + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut { + use base::SliceStorageMut; + + let (rows, cols) = self; + let (nrows, ncols) = matrix.data.shape(); + + let data = + SliceStorageMut::new_unchecked(&mut matrix.data, + (rows.lower(nrows), cols.lower(ncols)), + (rows.length(nrows), cols.length(ncols))); + + Matrix::from_data_statically_unchecked(data) + } + } + } +} + +macro_rules! impl_index_pairs { + (index $R: ident with {} index $C: ident with {$($r: tt,)* }) => {}; + + (index $R: ident with {$lh : tt, $($lt : tt,)*} + index $C: ident with { $($r: tt,)* }) => + { + $( + impl_index_pair!{$R, $C, $lh, $r} + )* + impl_index_pairs!{index $R with {$($lt,)*} index $C with {$($r,)*}} + } +} + +impl_index_pairs!{ + index R with { + [<> usize => U1], + [<> ops::Range => Dynamic], + [<> ops::RangeFrom => Dynamic], + [<> ops::RangeFull => R], + [<> ops::RangeInclusive => Dynamic], + [<> ops::RangeTo => Dynamic], + [<> ops::RangeToInclusive => Dynamic], + + [ ops::RangeFrom + => DimDiff + where R: DimSub], + } + index C with { + [<> usize => U1], + [<> ops::Range => Dynamic], + [<> ops::RangeFrom => Dynamic], + [<> ops::RangeFull => C], + [<> ops::RangeInclusive => Dynamic], + [<> ops::RangeTo => Dynamic], + [<> ops::RangeToInclusive => Dynamic], + + [ ops::RangeFrom + => DimDiff + where C: DimSub], + } +} diff --git a/src/base/iter.rs b/src/base/iter.rs index fe8fd4f6..8a5f10ab 100644 --- a/src/base/iter.rs +++ b/src/base/iter.rs @@ -3,9 +3,9 @@ use std::marker::PhantomData; use std::mem; -use base::dimension::Dim; +use base::dimension::{Dim, U1}; use base::storage::{Storage, StorageMut}; -use base::Scalar; +use base::{Scalar, Matrix, MatrixSlice, MatrixSliceMut}; macro_rules! iterator { (struct $Name:ident for $Storage:ident.$ptr: ident -> $Ptr:ty, $Ref:ty, $SRef: ty) => { @@ -96,3 +96,226 @@ macro_rules! iterator { iterator!(struct MatrixIter for Storage.ptr -> *const N, &'a N, &'a S); iterator!(struct MatrixIterMut for StorageMut.ptr_mut -> *mut N, &'a mut N, &'a mut S); + + +/* + * + * Row iterators. + * + */ +#[derive(Clone)] +/// An iterator through the rows of a matrix. +pub struct RowIter<'a, N: Scalar, R: Dim, C: Dim, S: Storage> { + mat: &'a Matrix, + curr: usize +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> RowIter<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a Matrix) -> Self { + RowIter { + mat, curr: 0 + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> Iterator for RowIter<'a, N, R, C, S> { + type Item = MatrixSlice<'a, N, U1, C, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.mat.nrows() { + let res = self.mat.row(self.curr); + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.mat.nrows() - self.curr, Some(self.mat.nrows() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.mat.nrows() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> ExactSizeIterator for RowIter<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.mat.nrows() - self.curr + } +} + + +/// An iterator through the mutable rows of a matrix. +pub struct RowIterMut<'a, N: Scalar, R: Dim, C: Dim, S: StorageMut> { + mat: *mut Matrix, + curr: usize, + phantom: PhantomData<&'a mut Matrix> +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> RowIterMut<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a mut Matrix) -> Self { + RowIterMut { + mat, + curr: 0, + phantom: PhantomData + } + } + + fn nrows(&self) -> usize { + unsafe { + (*self.mat).nrows() + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> Iterator for RowIterMut<'a, N, R, C, S> { + type Item = MatrixSliceMut<'a, N, U1, C, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.nrows() { + let res = unsafe { (*self.mat).row_mut(self.curr) }; + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.nrows() - self.curr, Some(self.nrows() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.nrows() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> ExactSizeIterator for RowIterMut<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.nrows() - self.curr + } +} + + +/* + * + * Column iterators. + * + */ +#[derive(Clone)] +/// An iterator through the columns of a matrix. +pub struct ColumnIter<'a, N: Scalar, R: Dim, C: Dim, S: Storage> { + mat: &'a Matrix, + curr: usize +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> ColumnIter<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a Matrix) -> Self { + ColumnIter { + mat, curr: 0 + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> Iterator for ColumnIter<'a, N, R, C, S> { + type Item = MatrixSlice<'a, N, R, U1, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.mat.ncols() { + let res = self.mat.column(self.curr); + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.mat.ncols() - self.curr, Some(self.mat.ncols() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.mat.ncols() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> ExactSizeIterator for ColumnIter<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.mat.ncols() - self.curr + } +} + + +/// An iterator through the mutable columns of a matrix. +pub struct ColumnIterMut<'a, N: Scalar, R: Dim, C: Dim, S: StorageMut> { + mat: *mut Matrix, + curr: usize, + phantom: PhantomData<&'a mut Matrix> +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> ColumnIterMut<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a mut Matrix) -> Self { + ColumnIterMut { + mat, + curr: 0, + phantom: PhantomData + } + } + + fn ncols(&self) -> usize { + unsafe { + (*self.mat).ncols() + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> Iterator for ColumnIterMut<'a, N, R, C, S> { + type Item = MatrixSliceMut<'a, N, R, U1, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.ncols() { + let res = unsafe { (*self.mat).column_mut(self.curr) }; + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.ncols() - self.curr, Some(self.ncols() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.ncols() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> ExactSizeIterator for ColumnIterMut<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.ncols() - self.curr + } +} + diff --git a/src/base/matrix.rs b/src/base/matrix.rs index 784ae9a2..2e344669 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -1,4 +1,4 @@ -use num::Zero; +use num::{One, Zero}; use num_complex::Complex; #[cfg(feature = "abomonation-serialize")] use std::io::{Result as IOResult, Write}; @@ -7,6 +7,7 @@ use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use std::any::TypeId; use std::cmp::Ordering; use std::fmt; +use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::mem; @@ -16,12 +17,12 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "abomonation-serialize")] use abomonation::Abomonation; -use alga::general::{Real, Ring}; +use alga::general::{ClosedAdd, ClosedMul, ClosedSub, Real, Ring}; use base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR}; use base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; -use base::dimension::{Dim, DimAdd, DimSum, U1, U2, U3}; -use base::iter::{MatrixIter, MatrixIterMut}; +use base::dimension::{Dim, DimAdd, DimSum, IsNotStaticOne, U1, U2, U3}; +use base::iter::{MatrixIter, MatrixIterMut, RowIter, RowIterMut, ColumnIter, ColumnIterMut}; use base::storage::{ ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut, }; @@ -72,7 +73,7 @@ pub type MatrixCross = /// dynamically-sized column vector should be represented as a `Matrix` (given /// some concrete types for `N` and a compatible data storage type `S`). #[repr(C)] -#[derive(Hash, Clone, Copy)] +#[derive(Clone, Copy)] pub struct Matrix { /// The data storage that contains all the matrix components and informations about its number /// of rows and column (if needed). @@ -246,6 +247,37 @@ impl> Matrix { MatrixIter::new(&self.data) } + /// Iterate through the rows of this matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, row) in a.row_iter().enumerate() { + /// assert_eq!(row, a.row(i)) + /// } + /// ``` + #[inline] + pub fn row_iter(&self) -> RowIter { + RowIter::new(self) + } + + /// Iterate through the columns of this matrix. + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, column) in a.column_iter().enumerate() { + /// assert_eq!(column, a.column(i)) + /// } + /// ``` + #[inline] + pub fn column_iter(&self) -> ColumnIter { + ColumnIter::new(self) + } + /// Computes the row and column coordinates of the i-th element of this matrix seen as a /// vector. #[inline] @@ -263,17 +295,6 @@ impl> Matrix { } } - /// Gets a reference to the element of this matrix at row `irow` and column `icol` without - /// bound-checking. - #[inline] - pub unsafe fn get_unchecked(&self, irow: usize, icol: usize) -> &N { - debug_assert!( - irow < self.nrows() && icol < self.ncols(), - "Matrix index out of bounds." - ); - self.data.get_unchecked(irow, icol) - } - /// Tests whether `self` and `rhs` are equal up to a given epsilon. /// /// See `relative_eq` from the `RelativeEq` trait for more details. @@ -374,7 +395,7 @@ impl> Matrix { for j in 0..res.ncols() { for i in 0..res.nrows() { unsafe { - *res.get_unchecked_mut(i, j) = *self.get_unchecked(i, j); + *res.get_unchecked_mut((i, j)) = *self.get_unchecked((i, j)); } } } @@ -403,7 +424,7 @@ impl> Matrix { } /// Returns a matrix containing the result of `f` applied to each of its entries. Unlike `map`, - /// `f` also gets passed the row and column index, i.e. `f(value, row, col)`. + /// `f` also gets passed the row and column index, i.e. `f(row, col, value)`. #[inline] pub fn map_with_location N2>( &self, @@ -503,6 +524,57 @@ impl> Matrix { res } + /// Folds a function `f` on each entry of `self`. + #[inline] + pub fn fold(&self, init: Acc, mut f: impl FnMut(Acc, N) -> Acc) -> Acc { + let (nrows, ncols) = self.data.shape(); + + let mut res = init; + + for j in 0..ncols.value() { + for i in 0..nrows.value() { + unsafe { + let a = *self.data.get_unchecked(i, j); + res = f(res, a) + } + } + } + + res + } + + /// Folds a function `f` on each pairs of entries from `self` and `rhs`. + #[inline] + pub fn zip_fold(&self, rhs: &Matrix, init: Acc, mut f: impl FnMut(Acc, N, N2) -> Acc) -> Acc + where + N2: Scalar, + R2: Dim, + C2: Dim, + S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + { + let (nrows, ncols) = self.data.shape(); + + let mut res = init; + + assert!( + (nrows.value(), ncols.value()) == rhs.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + + for j in 0..ncols.value() { + for i in 0..nrows.value() { + unsafe { + let a = *self.data.get_unchecked(i, j); + let b = *rhs.data.get_unchecked(i, j); + res = f(res, a, b) + } + } + } + + res + } + /// Transposes `self` and store the result into `out`. #[inline] pub fn transpose_to(&self, out: &mut Matrix) @@ -522,7 +594,7 @@ impl> Matrix { for i in 0..nrows { for j in 0..ncols { unsafe { - *out.get_unchecked_mut(j, i) = *self.get_unchecked(i, j); + *out.get_unchecked_mut((j, i)) = *self.get_unchecked((i, j)); } } } @@ -550,14 +622,44 @@ impl> Matrix { MatrixIterMut::new(&mut self.data) } - /// Gets a mutable reference to the i-th element of this matrix. + /// Mutably iterates through this matrix rows. + /// + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, mut row) in a.row_iter_mut().enumerate() { + /// row *= (i + 1) * 10; + /// } + /// + /// let expected = Matrix2x3::new(10, 20, 30, + /// 80, 100, 120); + /// assert_eq!(a, expected); + /// ``` #[inline] - pub unsafe fn get_unchecked_mut(&mut self, irow: usize, icol: usize) -> &mut N { - debug_assert!( - irow < self.nrows() && icol < self.ncols(), - "Matrix index out of bounds." - ); - self.data.get_unchecked_mut(irow, icol) + pub fn row_iter_mut(&mut self) -> RowIterMut { + RowIterMut::new(self) + } + + /// Mutably iterates through this matrix columns. + /// + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, mut col) in a.column_iter_mut().enumerate() { + /// col *= (i + 1) * 10; + /// } + /// + /// let expected = Matrix2x3::new(10, 40, 90, + /// 40, 100, 180); + /// assert_eq!(a, expected); + /// ``` + #[inline] + pub fn column_iter_mut(&mut self) -> ColumnIterMut { + ColumnIterMut::new(self) } /// Swaps two entries without bound-checking. @@ -598,7 +700,7 @@ impl> Matrix { for j in 0..ncols { for i in 0..nrows { unsafe { - *self.get_unchecked_mut(i, j) = *slice.get_unchecked(i + j * nrows); + *self.get_unchecked_mut((i, j)) = *slice.get_unchecked(i + j * nrows); } } } @@ -621,7 +723,7 @@ impl> Matrix { for j in 0..self.ncols() { for i in 0..self.nrows() { unsafe { - *self.get_unchecked_mut(i, j) = *other.get_unchecked(i, j); + *self.get_unchecked_mut((i, j)) = *other.get_unchecked((i, j)); } } } @@ -645,7 +747,7 @@ impl> Matrix { for j in 0..ncols { for i in 0..nrows { unsafe { - *self.get_unchecked_mut(i, j) = *other.get_unchecked(j, i); + *self.get_unchecked_mut((i, j)) = *other.get_unchecked((j, i)); } } } @@ -653,8 +755,7 @@ impl> Matrix { /// Replaces each component of `self` by the result of a closure `f` applied on it. #[inline] - pub fn apply N>(&mut self, mut f: F) - where DefaultAllocator: Allocator { + pub fn apply N>(&mut self, mut f: F) { let (nrows, ncols) = self.shape(); for j in 0..ncols { @@ -666,6 +767,71 @@ impl> Matrix { } } } + + /// Replaces each component of `self` by the result of a closure `f` applied on its components + /// joined with the components from `rhs`. + #[inline] + pub fn zip_apply(&mut self, rhs: &Matrix, mut f: impl FnMut(N, N2) -> N) + where N2: Scalar, + R2: Dim, + C2: Dim, + S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + let (nrows, ncols) = self.shape(); + + assert!( + (nrows, ncols) == rhs.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + + for j in 0..ncols { + for i in 0..nrows { + unsafe { + let e = self.data.get_unchecked_mut(i, j); + let rhs = rhs.get_unchecked((i, j)); + *e = f(*e, *rhs) + } + } + } + } + + + /// Replaces each component of `self` by the result of a closure `f` applied on its components + /// joined with the components from `b` and `c`. + #[inline] + pub fn zip_zip_apply(&mut self, b: &Matrix, c: &Matrix, mut f: impl FnMut(N, N2, N3) -> N) + where N2: Scalar, + R2: Dim, + C2: Dim, + S2: Storage, + N3: Scalar, + R3: Dim, + C3: Dim, + S3: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + let (nrows, ncols) = self.shape(); + + assert!( + (nrows, ncols) == b.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + assert!( + (nrows, ncols) == c.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + + for j in 0..ncols { + for i in 0..nrows { + unsafe { + let e = self.data.get_unchecked_mut(i, j); + let b = b.get_unchecked((i, j)); + let c = c.get_unchecked((i, j)); + *e = f(*e, *b, *c) + } + } + } + } } impl> Vector { @@ -742,7 +908,7 @@ impl, R, C>> Matrix, R for i in 0..nrows { for j in 0..ncols { unsafe { - *out.get_unchecked_mut(j, i) = self.get_unchecked(i, j).conj(); + *out.get_unchecked_mut((j, i)) = self.get_unchecked((i, j)).conj(); } } } @@ -776,8 +942,8 @@ impl, D, D>> Matrix, D, D, for i in 1..dim { for j in 0..i { unsafe { - let ref_ij = self.get_unchecked_mut(i, j) as *mut Complex; - let ref_ji = self.get_unchecked_mut(j, i) as *mut Complex; + let ref_ij = self.get_unchecked_mut((i, j)) as *mut Complex; + let ref_ji = self.get_unchecked_mut((j, i)) as *mut Complex; let conj_ij = (*ref_ij).conj(); let conj_ji = (*ref_ji).conj(); *ref_ij = conj_ji; @@ -803,7 +969,7 @@ impl> SquareMatrix { for i in 0..dim.value() { unsafe { - *res.vget_unchecked_mut(i) = *self.get_unchecked(i, i); + *res.vget_unchecked_mut(i) = *self.get_unchecked((i, i)); } } @@ -823,27 +989,36 @@ impl> SquareMatrix { let mut res = N::zero(); for i in 0..dim.value() { - res += unsafe { *self.get_unchecked(i, i) }; + res += unsafe { *self.get_unchecked((i, i)) }; } res } } +impl + IsNotStaticOne, S: Storage> Matrix { + + /// Yields the homogeneous matrix for this matrix, i.e., appending an additional dimension and + /// and setting the diagonal element to `1`. + #[inline] + pub fn to_homogeneous(&self) -> MatrixN> + where DefaultAllocator: Allocator, DimSum> { + assert!(self.is_square(), "Only square matrices can currently be transformed to homogeneous coordinates."); + let dim = DimSum::::from_usize(self.nrows() + 1); + let mut res = MatrixN::identity_generic(dim, dim); + res.generic_slice_mut::((0, 0), self.data.shape()).copy_from(&self); + res + } + +} + impl, S: Storage> Vector { /// Computes the coordinates in projective space of this vector, i.e., appends a `0` to its /// coordinates. #[inline] pub fn to_homogeneous(&self) -> VectorN> where DefaultAllocator: Allocator> { - let len = self.len(); - let hnrows = DimSum::::from_usize(len + 1); - let mut res = unsafe { VectorN::::new_uninitialized_generic(hnrows, U1) }; - res.generic_slice_mut((0, 0), self.data.shape()) - .copy_from(self); - res[(len, 0)] = N::zero(); - - res + self.push(N::zero()) } /// Constructs a vector from coordinates in projective space, i.e., removes a `0` at the end of @@ -863,6 +1038,22 @@ impl, S: Storage> Vector { } } +impl, S: Storage> Vector { + /// Constructs a new vector of higher dimension by appending `element` to the end of `self`. + #[inline] + pub fn push(&self, element: N) -> VectorN> + where DefaultAllocator: Allocator> { + let len = self.len(); + let hnrows = DimSum::::from_usize(len + 1); + let mut res = unsafe { VectorN::::new_uninitialized_generic(hnrows, U1) }; + res.generic_slice_mut((0, 0), self.data.shape()) + .copy_from(self); + res[(len, 0)] = element; + + res + } +} + impl AbsDiffEq for Matrix where N: Scalar + AbsDiffEq, @@ -1019,8 +1210,7 @@ impl Eq for Matrix where N: Scalar + Eq, S: Storage, -{ -} +{} impl PartialEq for Matrix where @@ -1124,8 +1314,8 @@ impl> Matrix { assert!(self.shape() == (2, 1), "2D perpendicular product "); unsafe { - *self.get_unchecked(0, 0) * *b.get_unchecked(1, 0) - - *self.get_unchecked(1, 0) * *b.get_unchecked(0, 0) + *self.get_unchecked((0, 0)) * *b.get_unchecked((1, 0)) + - *self.get_unchecked((1, 0)) * *b.get_unchecked((0, 0)) } } @@ -1160,17 +1350,17 @@ impl> Matrix { let ncols = SameShapeC::::from_usize(1); let mut res = Matrix::new_uninitialized_generic(nrows, ncols); - let ax = *self.get_unchecked(0, 0); - let ay = *self.get_unchecked(1, 0); - let az = *self.get_unchecked(2, 0); + let ax = *self.get_unchecked((0, 0)); + let ay = *self.get_unchecked((1, 0)); + let az = *self.get_unchecked((2, 0)); - let bx = *b.get_unchecked(0, 0); - let by = *b.get_unchecked(1, 0); - let bz = *b.get_unchecked(2, 0); + let bx = *b.get_unchecked((0, 0)); + let by = *b.get_unchecked((1, 0)); + let bz = *b.get_unchecked((2, 0)); - *res.get_unchecked_mut(0, 0) = ay * bz - az * by; - *res.get_unchecked_mut(1, 0) = az * bx - ax * bz; - *res.get_unchecked_mut(2, 0) = ax * by - ay * bx; + *res.get_unchecked_mut((0, 0)) = ay * bz - az * by; + *res.get_unchecked_mut((1, 0)) = az * bx - ax * bz; + *res.get_unchecked_mut((2, 0)) = ax * by - ay * bx; res } @@ -1181,17 +1371,17 @@ impl> Matrix { let ncols = SameShapeC::::from_usize(3); let mut res = Matrix::new_uninitialized_generic(nrows, ncols); - let ax = *self.get_unchecked(0, 0); - let ay = *self.get_unchecked(0, 1); - let az = *self.get_unchecked(0, 2); + let ax = *self.get_unchecked((0, 0)); + let ay = *self.get_unchecked((0, 1)); + let az = *self.get_unchecked((0, 2)); - let bx = *b.get_unchecked(0, 0); - let by = *b.get_unchecked(0, 1); - let bz = *b.get_unchecked(0, 2); + let bx = *b.get_unchecked((0, 0)); + let by = *b.get_unchecked((0, 1)); + let bz = *b.get_unchecked((0, 2)); - *res.get_unchecked_mut(0, 0) = ay * bz - az * by; - *res.get_unchecked_mut(0, 1) = az * bx - ax * bz; - *res.get_unchecked_mut(0, 2) = ax * by - ay * bx; + *res.get_unchecked_mut((0, 0)) = ay * bz - az * by; + *res.get_unchecked_mut((0, 1)) = az * bx - ax * bz; + *res.get_unchecked_mut((0, 2)) = ax * by - ay * bx; res } @@ -1247,65 +1437,27 @@ impl> Matrix { } } -impl> Matrix { - /// The squared L2 norm of this vector. - #[inline] - pub fn norm_squared(&self) -> N { - let mut res = N::zero(); - - for i in 0..self.ncols() { - let col = self.column(i); - res += col.dot(&col) - } - +impl> + Vector +{ + /// Returns `self * (1.0 - t) + rhs * t`, i.e., the linear blend of the vectors x and y using the scalar value a. + /// + /// The value for a is not restricted to the range `[0, 1]`. + /// + /// # Examples: + /// + /// ``` + /// # use nalgebra::Vector3; + /// let x = Vector3::new(1.0, 2.0, 3.0); + /// let y = Vector3::new(10.0, 20.0, 30.0); + /// assert_eq!(x.lerp(&y, 0.1), Vector3::new(1.9, 3.8, 5.7)); + /// ``` + pub fn lerp>(&self, rhs: &Vector, t: N) -> VectorN + where DefaultAllocator: Allocator { + let mut res = self.clone_owned(); + res.axpy(t, rhs, N::one() - t); res } - - /// The L2 norm of this matrix. - #[inline] - pub fn norm(&self) -> N { - self.norm_squared().sqrt() - } - - /// A synonym for the norm of this matrix. - /// - /// Aka the length. - /// - /// This function is simply implemented as a call to `norm()` - #[inline] - pub fn magnitude(&self) -> N { - self.norm() - } - - /// A synonym for the squared norm of this matrix. - /// - /// Aka the squared length. - /// - /// This function is simply implemented as a call to `norm_squared()` - #[inline] - pub fn magnitude_squared(&self) -> N { - self.norm_squared() - } - - /// Returns a normalized version of this matrix. - #[inline] - pub fn normalize(&self) -> MatrixMN - where DefaultAllocator: Allocator { - self / self.norm() - } - - /// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`. - #[inline] - pub fn try_normalize(&self, min_norm: N) -> Option> - where DefaultAllocator: Allocator { - let n = self.norm(); - - if n <= min_norm { - None - } else { - Some(self / n) - } - } } impl> Unit> { @@ -1359,32 +1511,6 @@ impl> Unit> { } } -impl> Matrix { - /// Normalizes this matrix in-place and returns its norm. - #[inline] - pub fn normalize_mut(&mut self) -> N { - let n = self.norm(); - *self /= n; - - n - } - - /// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`. - /// - /// If the normalization succeeded, returns the old normal of this matrix. - #[inline] - pub fn try_normalize_mut(&mut self, min_norm: N) -> Option { - let n = self.norm(); - - if n <= min_norm { - None - } else { - *self /= n; - Some(n) - } - } -} - impl AbsDiffEq for Unit> where N: Scalar + AbsDiffEq, @@ -1444,3 +1570,24 @@ where self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) } } + +impl Hash for Matrix +where + N: Scalar + Hash, + R: Dim, + C: Dim, + S: Storage, +{ + fn hash(&self, state: &mut H) { + let (nrows, ncols) = self.shape(); + (nrows, ncols).hash(state); + + for j in 0..ncols { + for i in 0..nrows { + unsafe { + self.get_unchecked((i, j)).hash(state); + } + } + } + } +} diff --git a/src/base/mod.rs b/src/base/mod.rs index 2f573025..0ec6311c 100644 --- a/src/base/mod.rs +++ b/src/base/mod.rs @@ -18,16 +18,19 @@ mod construction; mod construction_slice; mod conversion; mod edition; +pub mod indexing; mod matrix; mod matrix_alga; -mod matrix_array; +mod array_storage; mod matrix_slice; #[cfg(any(feature = "std", feature = "alloc"))] -mod matrix_vec; +mod vec_storage; mod properties; mod scalar; mod swizzle; mod unit; +mod statistics; +mod norm; #[doc(hidden)] pub mod helper; @@ -35,13 +38,14 @@ pub mod helper; pub use self::matrix::*; pub use self::scalar::*; pub use self::unit::*; +pub use self::norm::*; pub use self::default_allocator::*; pub use self::dimension::*; pub use self::alias::*; pub use self::alias_slice::*; -pub use self::matrix_array::*; +pub use self::array_storage::*; pub use self::matrix_slice::*; #[cfg(any(feature = "std", feature = "alloc"))] -pub use self::matrix_vec::*; +pub use self::vec_storage::*; diff --git a/src/base/norm.rs b/src/base/norm.rs new file mode 100644 index 00000000..68fb88a1 --- /dev/null +++ b/src/base/norm.rs @@ -0,0 +1,238 @@ +use num::Signed; +use std::cmp::PartialOrd; + +use allocator::Allocator; +use ::{Real, Scalar}; +use storage::{Storage, StorageMut}; +use base::{DefaultAllocator, Matrix, Dim, MatrixMN}; +use constraint::{SameNumberOfRows, SameNumberOfColumns, ShapeConstraint}; + + +// FIXME: this should be be a trait on alga? +/// A trait for abstract matrix norms. +/// +/// This may be moved to the alga crate in the future. +pub trait Norm { + /// Apply this norm to the given matrix. + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage; + /// Use the metric induced by this norm to compute the metric distance between the two given matrices. + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns; +} + +/// Euclidean norm. +pub struct EuclideanNorm; +/// Lp norm. +pub struct LpNorm(pub i32); +/// L-infinite norm aka. Chebytchev norm aka. uniform norm aka. suppremum norm. +pub struct UniformNorm; + +impl Norm for EuclideanNorm { + #[inline] + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage { + m.norm_squared().sqrt() + } + + #[inline] + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + m1.zip_fold(m2, N::zero(), |acc, a, b| { + let diff = a - b; + acc + diff * diff + }).sqrt() + } +} + +impl Norm for LpNorm { + #[inline] + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage { + m.fold(N::zero(), |a, b| { + a + b.abs().powi(self.0) + }).powf(::convert(1.0 / (self.0 as f64))) + } + + #[inline] + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + m1.zip_fold(m2, N::zero(), |acc, a, b| { + let diff = a - b; + acc + diff.abs().powi(self.0) + }).powf(::convert(1.0 / (self.0 as f64))) + } +} + +impl Norm for UniformNorm { + #[inline] + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage { + m.amax() + } + + #[inline] + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + m1.zip_fold(m2, N::zero(), |acc, a, b| { + let val = (a - b).abs(); + if val > acc { + val + } else { + acc + } + }) + } +} + + +impl> Matrix { + /// The squared L2 norm of this vector. + #[inline] + pub fn norm_squared(&self) -> N { + let mut res = N::zero(); + + for i in 0..self.ncols() { + let col = self.column(i); + res += col.dot(&col) + } + + res + } + + /// The L2 norm of this matrix. + /// + /// Use `.apply_norm` to apply a custom norm. + #[inline] + pub fn norm(&self) -> N { + self.norm_squared().sqrt() + } + + /// Compute the distance between `self` and `rhs` using the metric induced by the euclidean norm. + /// + /// Use `.apply_metric_distance` to apply a custom norm. + #[inline] + pub fn metric_distance(&self, rhs: &Matrix) -> N + where R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + self.apply_metric_distance(rhs, &EuclideanNorm) + } + + /// Uses the given `norm` to compute the norm of `self`. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm}; + /// + /// let v = Vector3::new(1.0, 2.0, 3.0); + /// assert_eq!(v.apply_norm(&UniformNorm), 3.0); + /// assert_eq!(v.apply_norm(&LpNorm(1)), 6.0); + /// assert_eq!(v.apply_norm(&EuclideanNorm), v.norm()); + /// ``` + #[inline] + pub fn apply_norm(&self, norm: &impl Norm) -> N { + norm.norm(self) + } + + /// Uses the metric induced by the given `norm` to compute the metric distance between `self` and `rhs`. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm}; + /// + /// let v1 = Vector3::new(1.0, 2.0, 3.0); + /// let v2 = Vector3::new(10.0, 20.0, 30.0); + /// + /// assert_eq!(v1.apply_metric_distance(&v2, &UniformNorm), 27.0); + /// assert_eq!(v1.apply_metric_distance(&v2, &LpNorm(1)), 27.0 + 18.0 + 9.0); + /// assert_eq!(v1.apply_metric_distance(&v2, &EuclideanNorm), (v1 - v2).norm()); + /// ``` + #[inline] + pub fn apply_metric_distance(&self, rhs: &Matrix, norm: &impl Norm) -> N + where R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + norm.metric_distance(self,rhs) + } + + /// The Lp norm of this matrix. + #[inline] + pub fn lp_norm(&self, p: i32) -> N { + self.apply_norm(&LpNorm(p)) + } + + /// A synonym for the norm of this matrix. + /// + /// Aka the length. + /// + /// This function is simply implemented as a call to `norm()` + #[inline] + pub fn magnitude(&self) -> N { + self.norm() + } + + /// A synonym for the squared norm of this matrix. + /// + /// Aka the squared length. + /// + /// This function is simply implemented as a call to `norm_squared()` + #[inline] + pub fn magnitude_squared(&self) -> N { + self.norm_squared() + } + + /// Returns a normalized version of this matrix. + #[inline] + pub fn normalize(&self) -> MatrixMN + where DefaultAllocator: Allocator { + self / self.norm() + } + + /// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`. + #[inline] + pub fn try_normalize(&self, min_norm: N) -> Option> + where DefaultAllocator: Allocator { + let n = self.norm(); + + if n <= min_norm { + None + } else { + Some(self / n) + } + } +} + +impl> Matrix { + /// Normalizes this matrix in-place and returns its norm. + #[inline] + pub fn normalize_mut(&mut self) -> N { + let n = self.norm(); + *self /= n; + + n + } + + /// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`. + /// + /// If the normalization succeeded, returns the old normal of this matrix. + #[inline] + pub fn try_normalize_mut(&mut self, min_norm: N) -> Option { + let n = self.norm(); + + if n <= min_norm { + None + } else { + *self /= n; + Some(n) + } + } +} diff --git a/src/base/ops.rs b/src/base/ops.rs index 594c3da4..8167dd86 100644 --- a/src/base/ops.rs +++ b/src/base/ops.rs @@ -45,7 +45,7 @@ where "Matrix index out of bounds." ); - unsafe { self.get_unchecked(ij.0, ij.1) } + unsafe { self.get_unchecked((ij.0, ij.1)) } } } @@ -71,7 +71,7 @@ where "Matrix index out of bounds." ); - unsafe { self.get_unchecked_mut(ij.0, ij.1) } + unsafe { self.get_unchecked_mut((ij.0, ij.1)) } } } @@ -172,8 +172,8 @@ macro_rules! componentwise_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - let val = self.get_unchecked(i, j).$method(*rhs.get_unchecked(i, j)); - *out.get_unchecked_mut(i, j) = val; + let val = self.get_unchecked((i, j)).$method(*rhs.get_unchecked((i, j))); + *out.get_unchecked_mut((i, j)) = val; } } } @@ -204,7 +204,7 @@ macro_rules! componentwise_binop_impl( for j in 0 .. rhs.ncols() { for i in 0 .. rhs.nrows() { unsafe { - self.get_unchecked_mut(i, j).$method_assign(*rhs.get_unchecked(i, j)) + self.get_unchecked_mut((i, j)).$method_assign(*rhs.get_unchecked((i, j))) } } } @@ -235,8 +235,8 @@ macro_rules! componentwise_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - let r = rhs.get_unchecked_mut(i, j); - *r = self.get_unchecked(i, j).$method(*r) + let r = rhs.get_unchecked_mut((i, j)); + *r = self.get_unchecked((i, j)).$method(*r) } } } @@ -448,7 +448,7 @@ macro_rules! componentwise_scalarop_impl( fn $method_assign(&mut self, rhs: N) { for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { - unsafe { self.get_unchecked_mut(i, j).$method_assign(rhs) }; + unsafe { self.get_unchecked_mut((i, j)).$method_assign(rhs) }; } } } @@ -657,7 +657,7 @@ where for i in 0..ncols1 { for j in 0..ncols2 { let dot = self.column(i).dot(&rhs.column(j)); - unsafe { *out.get_unchecked_mut(i, j) = dot }; + unsafe { *out.get_unchecked_mut((i, j)) = dot }; } } } @@ -704,10 +704,10 @@ where for j2 in 0..ncols2.value() { for i1 in 0..nrows1.value() { unsafe { - let coeff = *self.get_unchecked(i1, j1); + let coeff = *self.get_unchecked((i1, j1)); for i2 in 0..nrows2.value() { - *data_res = coeff * *rhs.get_unchecked(i2, j2); + *data_res = coeff * *rhs.get_unchecked((i2, j2)); data_res = data_res.offset(1); } } diff --git a/src/base/statistics.rs b/src/base/statistics.rs new file mode 100644 index 00000000..11cc9e1c --- /dev/null +++ b/src/base/statistics.rs @@ -0,0 +1,309 @@ +use ::{Real, Dim, Matrix, VectorN, RowVectorN, DefaultAllocator, U1, VectorSliceN}; +use storage::Storage; +use allocator::Allocator; + +impl> Matrix { + /// Returns a row vector where each element is the result of the application of `f` on the + /// corresponding column of the original matrix. + #[inline] + pub fn compress_rows(&self, f: impl Fn(VectorSliceN) -> N) -> RowVectorN + where DefaultAllocator: Allocator { + + let ncols = self.data.shape().1; + let mut res = unsafe { RowVectorN::new_uninitialized_generic(U1, ncols) }; + + for i in 0..ncols.value() { + // FIXME: avoid bound checking of column. + unsafe { *res.get_unchecked_mut((0, i)) = f(self.column(i)); } + } + + res + } + + /// Returns a column vector where each element is the result of the application of `f` on the + /// corresponding column of the original matrix. + /// + /// This is the same as `self.compress_rows(f).transpose()`. + #[inline] + pub fn compress_rows_tr(&self, f: impl Fn(VectorSliceN) -> N) -> VectorN + where DefaultAllocator: Allocator { + + let ncols = self.data.shape().1; + let mut res = unsafe { VectorN::new_uninitialized_generic(ncols, U1) }; + + for i in 0..ncols.value() { + // FIXME: avoid bound checking of column. + unsafe { *res.vget_unchecked_mut(i) = f(self.column(i)); } + } + + res + } + + /// Returns a column vector resulting from the folding of `f` on each column of this matrix. + #[inline] + pub fn compress_columns(&self, init: VectorN, f: impl Fn(&mut VectorN, VectorSliceN)) -> VectorN + where DefaultAllocator: Allocator { + let mut res = init; + + for i in 0..self.ncols() { + f(&mut res, self.column(i)) + } + + res + } +} + +impl> Matrix { + /* + * + * Sum computation. + * + */ + /// The sum of all the elements of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2x3; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.sum(), 21.0); + /// ``` + #[inline] + pub fn sum(&self) -> N { + self.iter().cloned().fold(N::zero(), |a, b| a + b) + } + + /// The sum of all the rows of this matrix. + /// + /// Use `.row_variance_tr` if you need the result in a column vector instead. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, RowVector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_sum(), RowVector3::new(5.0, 7.0, 9.0)); + /// ``` + #[inline] + pub fn row_sum(&self) -> RowVectorN + where DefaultAllocator: Allocator { + self.compress_rows(|col| col.sum()) + } + + /// The sum of all the rows of this matrix. The result is transposed and returned as a column vector. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_sum_tr(), Vector3::new(5.0, 7.0, 9.0)); + /// ``` + #[inline] + pub fn row_sum_tr(&self) -> VectorN + where DefaultAllocator: Allocator { + self.compress_rows_tr(|col| col.sum()) + } + + /// The sum of all the columns of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector2}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.column_sum(), Vector2::new(6.0, 15.0)); + /// ``` + #[inline] + pub fn column_sum(&self) -> VectorN + where DefaultAllocator: Allocator { + let nrows = self.data.shape().0; + self.compress_columns(VectorN::zeros_generic(nrows, U1), |out, col| { + out.axpy(N::one(), &col, N::one()) + }) + } + + /* + * + * Variance computation. + * + */ + /// The variance of all the elements of this matrix. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Matrix2x3; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_relative_eq!(m.variance(), 35.0 / 12.0, epsilon = 1.0e-8); + /// ``` + #[inline] + pub fn variance(&self) -> N { + if self.len() == 0 { + N::zero() + } else { + let val = self.iter().cloned().fold((N::zero(), N::zero()), |a, b| (a.0 + b * b, a.1 + b)); + let denom = N::one() / ::convert::<_, N>(self.len() as f64); + val.0 * denom - (val.1 * denom) * (val.1 * denom) + } + } + + /// The variance of all the rows of this matrix. + /// + /// Use `.row_variance_tr` if you need the result in a column vector instead. + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, RowVector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_variance(), RowVector3::new(2.25, 2.25, 2.25)); + /// ``` + #[inline] + pub fn row_variance(&self) -> RowVectorN + where DefaultAllocator: Allocator { + self.compress_rows(|col| col.variance()) + } + + /// The variance of all the rows of this matrix. The result is transposed and returned as a column vector. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_variance_tr(), Vector3::new(2.25, 2.25, 2.25)); + /// ``` + #[inline] + pub fn row_variance_tr(&self) -> VectorN + where DefaultAllocator: Allocator { + self.compress_rows_tr(|col| col.variance()) + } + + /// The variance of all the columns of this matrix. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Matrix2x3, Vector2}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_relative_eq!(m.column_variance(), Vector2::new(2.0 / 3.0, 2.0 / 3.0), epsilon = 1.0e-8); + /// ``` + #[inline] + pub fn column_variance(&self) -> VectorN + where DefaultAllocator: Allocator { + let (nrows, ncols) = self.data.shape(); + + let mut mean = self.column_mean(); + mean.apply(|e| -(e * e)); + + let denom = N::one() / ::convert::<_, N>(ncols.value() as f64); + self.compress_columns(mean, |out, col| { + for i in 0..nrows.value() { + unsafe { + let val = col.vget_unchecked(i); + *out.vget_unchecked_mut(i) += denom * *val * *val + } + } + }) + } + + /* + * + * Mean computation. + * + */ + /// The mean of all the elements of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2x3; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.mean(), 3.5); + /// ``` + #[inline] + pub fn mean(&self) -> N { + if self.len() == 0 { + N::zero() + } else { + self.sum() / ::convert(self.len() as f64) + } + } + + /// The mean of all the rows of this matrix. + /// + /// Use `.row_mean_tr` if you need the result in a column vector instead. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, RowVector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_mean(), RowVector3::new(2.5, 3.5, 4.5)); + /// ``` + #[inline] + pub fn row_mean(&self) -> RowVectorN + where DefaultAllocator: Allocator { + self.compress_rows(|col| col.mean()) + } + + /// The mean of all the rows of this matrix. The result is transposed and returned as a column vector. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_mean_tr(), Vector3::new(2.5, 3.5, 4.5)); + /// ``` + #[inline] + pub fn row_mean_tr(&self) -> VectorN + where DefaultAllocator: Allocator { + self.compress_rows_tr(|col| col.mean()) + } + + /// The mean of all the columns of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector2}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.column_mean(), Vector2::new(2.0, 5.0)); + /// ``` + #[inline] + pub fn column_mean(&self) -> VectorN + where DefaultAllocator: Allocator { + let (nrows, ncols) = self.data.shape(); + let denom = N::one() / ::convert::<_, N>(ncols.value() as f64); + self.compress_columns(VectorN::zeros_generic(nrows, U1), |out, col| { + out.axpy(denom, &col, N::one()) + }) + } +} diff --git a/src/base/storage.rs b/src/base/storage.rs index b96f69d0..0a07713b 100644 --- a/src/base/storage.rs +++ b/src/base/storage.rs @@ -34,7 +34,7 @@ pub type CStride = /// Note that `Self` must always have a number of elements compatible with the matrix length (given /// by `R` and `C` if they are known at compile-time). For example, implementors of this trait /// should **not** allow the user to modify the size of the underlying buffer with safe methods -/// (for example the `MatrixVec::data_mut` method is unsafe because the user could change the +/// (for example the `VecStorage::data_mut` method is unsafe because the user could change the /// vector's size so that it no longer contains enough elements: this will lead to UB. pub unsafe trait Storage: Debug + Sized { /// The static stride of this storage's rows. diff --git a/src/base/swizzle.rs b/src/base/swizzle.rs index e1908e35..b09316d4 100644 --- a/src/base/swizzle.rs +++ b/src/base/swizzle.rs @@ -3,64 +3,60 @@ use storage::Storage; use typenum::{self, Cmp, Greater}; macro_rules! impl_swizzle { - ($(where $BaseDim: ident: $name: ident() -> $Result: ident[$($i: expr),*]);*) => { + ($( where $BaseDim: ident: $( $name: ident() -> $Result: ident[$($i: expr),+] ),+ ;)* ) => { $( - impl> Vector { - /// Builds a new vector from components of `self`. - #[inline] - pub fn $name(&self) -> $Result - where D::Value: Cmp { - $Result::new($(self[$i]),*) - } + impl> Vector + where D::Value: Cmp + { + $( + /// Builds a new vector from components of `self`. + #[inline] + pub fn $name(&self) -> $Result { + $Result::new($(self[$i]),*) + } + )* } )* } } impl_swizzle!( - where U0: xx() -> Vector2[0, 0]; - where U1: xy() -> Vector2[0, 1]; - where U2: xz() -> Vector2[0, 2]; - where U1: yx() -> Vector2[1, 0]; - where U1: yy() -> Vector2[1, 1]; - where U1: yz() -> Vector2[1, 2]; - where U2: zx() -> Vector2[2, 0]; - where U2: zy() -> Vector2[2, 1]; - where U2: zz() -> Vector2[2, 2]; + where U0: xx() -> Vector2[0, 0], + xxx() -> Vector3[0, 0, 0]; - where U0: xxx() -> Vector3[0, 0, 0]; - where U1: xxy() -> Vector3[0, 0, 1]; - where U2: xxz() -> Vector3[0, 0, 2]; + where U1: xy() -> Vector2[0, 1], + yx() -> Vector2[1, 0], + yy() -> Vector2[1, 1], + xxy() -> Vector3[0, 0, 1], + xyx() -> Vector3[0, 1, 0], + xyy() -> Vector3[0, 1, 1], + yxx() -> Vector3[1, 0, 0], + yxy() -> Vector3[1, 0, 1], + yyx() -> Vector3[1, 1, 0], + yyy() -> Vector3[1, 1, 1]; - where U1: xyx() -> Vector3[0, 1, 0]; - where U1: xyy() -> Vector3[0, 1, 1]; - where U2: xyz() -> Vector3[0, 1, 2]; - - where U2: xzx() -> Vector3[0, 2, 0]; - where U2: xzy() -> Vector3[0, 2, 1]; - where U2: xzz() -> Vector3[0, 2, 2]; - - where U1: yxx() -> Vector3[1, 0, 0]; - where U1: yxy() -> Vector3[1, 0, 1]; - where U2: yxz() -> Vector3[1, 0, 2]; - - where U1: yyx() -> Vector3[1, 1, 0]; - where U1: yyy() -> Vector3[1, 1, 1]; - where U2: yyz() -> Vector3[1, 1, 2]; - - where U2: yzx() -> Vector3[1, 2, 0]; - where U2: yzy() -> Vector3[1, 2, 1]; - where U2: yzz() -> Vector3[1, 2, 2]; - - where U2: zxx() -> Vector3[2, 0, 0]; - where U2: zxy() -> Vector3[2, 0, 1]; - where U2: zxz() -> Vector3[2, 0, 2]; - - where U2: zyx() -> Vector3[2, 1, 0]; - where U2: zyy() -> Vector3[2, 1, 1]; - where U2: zyz() -> Vector3[2, 1, 2]; - - where U2: zzx() -> Vector3[2, 2, 0]; - where U2: zzy() -> Vector3[2, 2, 1]; - where U2: zzz() -> Vector3[2, 2, 2] + where U2: xz() -> Vector2[0, 2], + yz() -> Vector2[1, 2], + zx() -> Vector2[2, 0], + zy() -> Vector2[2, 1], + zz() -> Vector2[2, 2], + xxz() -> Vector3[0, 0, 2], + xyz() -> Vector3[0, 1, 2], + xzx() -> Vector3[0, 2, 0], + xzy() -> Vector3[0, 2, 1], + xzz() -> Vector3[0, 2, 2], + yxz() -> Vector3[1, 0, 2], + yyz() -> Vector3[1, 1, 2], + yzx() -> Vector3[1, 2, 0], + yzy() -> Vector3[1, 2, 1], + yzz() -> Vector3[1, 2, 2], + zxx() -> Vector3[2, 0, 0], + zxy() -> Vector3[2, 0, 1], + zxz() -> Vector3[2, 0, 2], + zyx() -> Vector3[2, 1, 0], + zyy() -> Vector3[2, 1, 1], + zyz() -> Vector3[2, 1, 2], + zzx() -> Vector3[2, 2, 0], + zzy() -> Vector3[2, 2, 1], + zzz() -> Vector3[2, 2, 2]; ); diff --git a/src/base/unit.rs b/src/base/unit.rs index 1c2fa1e2..af400389 100644 --- a/src/base/unit.rs +++ b/src/base/unit.rs @@ -15,7 +15,7 @@ use alga::linear::NormedSpace; /// A wrapper that ensures the underlying algebraic entity has a unit norm. /// -/// Use `.as_ref()` or `.unwrap()` to obtain the underlying value by-reference or by-move. +/// Use `.as_ref()` or `.into_inner()` to obtain the underlying value by-reference or by-move. #[repr(transparent)] #[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)] pub struct Unit { @@ -113,6 +113,14 @@ impl Unit { /// Retrieves the underlying value. #[inline] + pub fn into_inner(self) -> T { + self.value + } + + /// Retrieves the underlying value. + /// Deprecated: use [Unit::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] + #[inline] pub fn unwrap(self) -> T { self.value } @@ -143,7 +151,7 @@ where T::Field: RelativeEq { #[inline] fn to_superset(&self) -> T { - self.clone().unwrap() + self.clone().into_inner() } #[inline] diff --git a/src/base/matrix_vec.rs b/src/base/vec_storage.rs similarity index 57% rename from src/base/matrix_vec.rs rename to src/base/vec_storage.rs index 73b77609..4a852f25 100644 --- a/src/base/matrix_vec.rs +++ b/src/base/vec_storage.rs @@ -1,6 +1,5 @@ #[cfg(feature = "abomonation-serialize")] use std::io::{Result as IOResult, Write}; -use std::ops::Deref; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; @@ -9,7 +8,8 @@ use base::allocator::Allocator; use base::default_allocator::DefaultAllocator; use base::dimension::{Dim, DimName, Dynamic, U1}; use base::storage::{ContiguousStorage, ContiguousStorageMut, Owned, Storage, StorageMut}; -use base::Scalar; +use base::{Scalar, Vector}; +use base::constraint::{SameNumberOfRows, ShapeConstraint}; #[cfg(feature = "abomonation-serialize")] use abomonation::Abomonation; @@ -23,21 +23,25 @@ use abomonation::Abomonation; #[repr(C)] #[derive(Eq, Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] -pub struct MatrixVec { +pub struct VecStorage { data: Vec, nrows: R, ncols: C, } -impl MatrixVec { +#[deprecated(note="renamed to `VecStorage`")] +/// Renamed to [VecStorage]. +pub type MatrixVec = VecStorage; + +impl VecStorage { /// Creates a new dynamic matrix data storage from the given vector and shape. #[inline] - pub fn new(nrows: R, ncols: C, data: Vec) -> MatrixVec { + pub fn new(nrows: R, ncols: C, data: Vec) -> VecStorage { assert!( nrows.value() * ncols.value() == data.len(), "Data storage buffer dimension mismatch." ); - MatrixVec { + VecStorage { data: data, nrows: nrows, ncols: ncols, @@ -46,15 +50,16 @@ impl MatrixVec { /// The underlying data storage. #[inline] - pub fn data(&self) -> &Vec { + pub fn as_vec(&self) -> &Vec { &self.data } /// The underlying mutable data storage. /// - /// This is unsafe because this may cause UB if the vector is modified by the user. + /// This is unsafe because this may cause UB if the size of the vector is changed + /// by the user. #[inline] - pub unsafe fn data_mut(&mut self) -> &mut Vec { + pub unsafe fn as_vec_mut(&mut self) -> &mut Vec { &mut self.data } @@ -76,14 +81,18 @@ impl MatrixVec { self.data } + + /// The number of elements on the underlying vector. + #[inline] + pub fn len(&self) -> usize { + self.data.len() + } } -impl Deref for MatrixVec { - type Target = Vec; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.data +impl Into> for VecStorage +{ + fn into(self) -> Vec { + self.data } } @@ -93,7 +102,7 @@ impl Deref for MatrixVec { * Dynamic − Dynamic * */ -unsafe impl Storage for MatrixVec +unsafe impl Storage for VecStorage where DefaultAllocator: Allocator { type RStride = U1; @@ -133,11 +142,11 @@ where DefaultAllocator: Allocator #[inline] fn as_slice(&self) -> &[N] { - &self[..] + &self.data } } -unsafe impl Storage for MatrixVec +unsafe impl Storage for VecStorage where DefaultAllocator: Allocator { type RStride = U1; @@ -177,7 +186,7 @@ where DefaultAllocator: Allocator #[inline] fn as_slice(&self) -> &[N] { - &self[..] + &self.data } } @@ -186,7 +195,7 @@ where DefaultAllocator: Allocator * StorageMut, ContiguousStorage. * */ -unsafe impl StorageMut for MatrixVec +unsafe impl StorageMut for VecStorage where DefaultAllocator: Allocator { #[inline] @@ -200,13 +209,13 @@ where DefaultAllocator: Allocator } } -unsafe impl ContiguousStorage for MatrixVec where DefaultAllocator: Allocator +unsafe impl ContiguousStorage for VecStorage where DefaultAllocator: Allocator {} -unsafe impl ContiguousStorageMut for MatrixVec where DefaultAllocator: Allocator +unsafe impl ContiguousStorageMut for VecStorage where DefaultAllocator: Allocator {} -unsafe impl StorageMut for MatrixVec +unsafe impl StorageMut for VecStorage where DefaultAllocator: Allocator { #[inline] @@ -221,7 +230,7 @@ where DefaultAllocator: Allocator } #[cfg(feature = "abomonation-serialize")] -impl Abomonation for MatrixVec { +impl Abomonation for VecStorage { unsafe fn entomb(&self, writer: &mut W) -> IOResult<()> { self.data.entomb(writer) } @@ -235,8 +244,66 @@ impl Abomonation for MatrixVec { } } -unsafe impl ContiguousStorage for MatrixVec where DefaultAllocator: Allocator +unsafe impl ContiguousStorage for VecStorage where DefaultAllocator: Allocator {} -unsafe impl ContiguousStorageMut for MatrixVec where DefaultAllocator: Allocator +unsafe impl ContiguousStorageMut for VecStorage where DefaultAllocator: Allocator {} + +impl Extend for VecStorage +{ + /// Extends the number of columns of the `VecStorage` with elements + /// from the given iterator. + /// + /// # Panics + /// This function panics if the number of elements yielded by the + /// given iterator is not a multiple of the number of rows of the + /// `VecStorage`. + fn extend>(&mut self, iter: I) + { + self.data.extend(iter); + self.ncols = Dynamic::new(self.data.len() / self.nrows.value()); + assert!(self.data.len() % self.nrows.value() == 0, + "The number of elements produced by the given iterator was not a multiple of the number of rows."); + } +} + +impl Extend> for VecStorage +where + N: Scalar, + R: Dim, + RV: Dim, + SV: Storage, + ShapeConstraint: SameNumberOfRows, +{ + /// Extends the number of columns of the `VecStorage` with vectors + /// from the given iterator. + /// + /// # Panics + /// This function panics if the number of rows of each `Vector` + /// yielded by the iterator is not equal to the number of rows + /// of this `VecStorage`. + fn extend>>(&mut self, iter: I) + { + let nrows = self.nrows.value(); + let iter = iter.into_iter(); + let (lower, _upper) = iter.size_hint(); + self.data.reserve(nrows * lower); + for vector in iter { + assert_eq!(nrows, vector.shape().0); + self.data.extend(vector.iter()); + } + self.ncols = Dynamic::new(self.data.len() / nrows); + } +} + +impl Extend for VecStorage +{ + /// Extends the number of rows of the `VecStorage` with elements + /// from the given iterator. + fn extend>(&mut self, iter: I) + { + self.data.extend(iter); + self.nrows = Dynamic::new(self.data.len()); + } +} diff --git a/src/debug/random_orthogonal.rs b/src/debug/random_orthogonal.rs index da06805b..6d393a24 100644 --- a/src/debug/random_orthogonal.rs +++ b/src/debug/random_orthogonal.rs @@ -48,6 +48,7 @@ where Owned: Clone + Send, { fn arbitrary(g: &mut G) -> Self { + use rand::Rng; let dim = D::try_to_usize().unwrap_or(g.gen_range(1, 50)); Self::new(D::from_usize(dim), || N::arbitrary(g)) } diff --git a/src/debug/random_sdp.rs b/src/debug/random_sdp.rs index c78d1fd1..7835c775 100644 --- a/src/debug/random_sdp.rs +++ b/src/debug/random_sdp.rs @@ -49,6 +49,7 @@ where Owned: Clone + Send, { fn arbitrary(g: &mut G) -> Self { + use rand::Rng; let dim = D::try_to_usize().unwrap_or(g.gen_range(1, 50)); Self::new(D::from_usize(dim), || N::arbitrary(g)) } diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs index 235049d8..1814efb5 100644 --- a/src/geometry/isometry.rs +++ b/src/geometry/isometry.rs @@ -108,6 +108,19 @@ impl>> Isometry where DefaultAllocator: Allocator { /// Creates a new isometry from its rotational and translational parts. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3, Point3}; + /// let tra = Translation3::new(0.0, 0.0, 3.0); + /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::PI); + /// let iso = Isometry3::from_parts(tra, rot); + /// + /// assert_relative_eq!(iso * Point3::new(1.0, 2.0, 3.0), Point3::new(-1.0, 2.0, 0.0), epsilon = 1.0e-6); + /// ``` #[inline] pub fn from_parts(translation: Translation, rotation: R) -> Isometry { Isometry { @@ -118,6 +131,18 @@ where DefaultAllocator: Allocator } /// Inverts `self`. + /// + /// # Example + /// + /// ``` + /// # use std::f32; + /// # use nalgebra::{Isometry2, Point2, Vector2}; + /// let iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); + /// let inv = iso.inverse(); + /// let pt = Point2::new(1.0, 2.0); + /// + /// assert_eq!(inv * (iso * pt), pt); + /// ``` #[inline] pub fn inverse(&self) -> Isometry { let mut res = self.clone(); @@ -125,7 +150,20 @@ where DefaultAllocator: Allocator res } - /// Inverts `self`. + /// Inverts `self` in-place. + /// + /// # Example + /// + /// ``` + /// # use std::f32; + /// # use nalgebra::{Isometry2, Point2, Vector2}; + /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); + /// let pt = Point2::new(1.0, 2.0); + /// let transformed_pt = iso * pt; + /// iso.inverse_mut(); + /// + /// assert_eq!(iso * transformed_pt, pt); + /// ``` #[inline] pub fn inverse_mut(&mut self) { self.rotation.inverse_mut(); @@ -134,12 +172,39 @@ where DefaultAllocator: Allocator } /// Appends to `self` the given translation in-place. + /// + /// # Example + /// + /// ``` + /// # use std::f32; + /// # use nalgebra::{Isometry2, Translation2, Vector2}; + /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); + /// let tra = Translation2::new(3.0, 4.0); + /// // Same as `iso = tra * iso`. + /// iso.append_translation_mut(&tra); + /// + /// assert_eq!(iso.translation, Translation2::new(4.0, 6.0)); + /// ``` #[inline] pub fn append_translation_mut(&mut self, t: &Translation) { self.translation.vector += &t.vector } /// Appends to `self` the given rotation in-place. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2}; + /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::PI / 6.0); + /// let rot = UnitComplex::new(f32::consts::PI / 2.0); + /// // Same as `iso = rot * iso`. + /// iso.append_rotation_mut(&rot); + /// + /// assert_relative_eq!(iso, Isometry2::new(Vector2::new(-2.0, 1.0), f32::consts::PI * 2.0 / 3.0), epsilon = 1.0e-6); + /// ``` #[inline] pub fn append_rotation_mut(&mut self, r: &R) { self.rotation = self.rotation.append_rotation(&r); @@ -148,6 +213,20 @@ where DefaultAllocator: Allocator /// Appends in-place to `self` a rotation centered at the point `p`, i.e., the rotation that /// lets `p` invariant. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2, Point2}; + /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); + /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); + /// let pt = Point2::new(1.0, 0.0); + /// iso.append_rotation_wrt_point_mut(&rot, &pt); + /// + /// assert_relative_eq!(iso * pt, Point2::new(-2.0, 0.0), epsilon = 1.0e-6); + /// ``` #[inline] pub fn append_rotation_wrt_point_mut(&mut self, r: &R, p: &Point) { self.translation.vector -= &p.coords; @@ -157,10 +236,23 @@ where DefaultAllocator: Allocator /// Appends in-place to `self` a rotation centered at the point with coordinates /// `self.translation`. + /// + /// # Example + /// + /// ``` + /// # use std::f32; + /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2, Point2}; + /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); + /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); + /// iso.append_rotation_wrt_center_mut(&rot); + /// + /// // The translation part should not have changed. + /// assert_eq!(iso.translation.vector, Vector2::new(1.0, 2.0)); + /// assert_eq!(iso.rotation, UnitComplex::new(f32::consts::PI)); + /// ``` #[inline] pub fn append_rotation_wrt_center_mut(&mut self, r: &R) { - let center = Point::from_coordinates(self.translation.vector.clone()); - self.append_rotation_wrt_point_mut(r, ¢er) + self.rotation = self.rotation.append_rotation(r); } } @@ -172,6 +264,20 @@ impl Isometry where DefaultAllocator: Allocator { /// Converts this isometry into its equivalent homogeneous transformation matrix. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Isometry2, Vector2, Matrix3}; + /// let iso = Isometry2::new(Vector2::new(10.0, 20.0), f32::consts::FRAC_PI_6); + /// let expected = Matrix3::new(0.8660254, -0.5, 10.0, + /// 0.5, 0.8660254, 20.0, + /// 0.0, 0.0, 1.0); + /// + /// assert_relative_eq!(iso.to_homogeneous(), expected, epsilon = 1.0e-6); + /// ``` #[inline] pub fn to_homogeneous(&self) -> MatrixN> where diff --git a/src/geometry/isometry_alga.rs b/src/geometry/isometry_alga.rs index 6a144550..7decea8e 100644 --- a/src/geometry/isometry_alga.rs +++ b/src/geometry/isometry_alga.rs @@ -143,10 +143,7 @@ where #[inline] fn append_rotation(&self, r: &Self::Rotation) -> Self { let shift = r.transform_vector(&self.translation.vector); - Isometry::from_parts( - Translation::from_vector(shift), - r.clone() * self.rotation.clone(), - ) + Isometry::from_parts(Translation::from(shift), r.clone() * self.rotation.clone()) } #[inline] diff --git a/src/geometry/isometry_construction.rs b/src/geometry/isometry_construction.rs index a60b0f63..5a402c0a 100644 --- a/src/geometry/isometry_construction.rs +++ b/src/geometry/isometry_construction.rs @@ -23,6 +23,20 @@ impl>> Isometry where DefaultAllocator: Allocator { /// Creates a new identity isometry. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Isometry2, Point2, Isometry3, Point3}; + /// + /// let iso = Isometry2::identity(); + /// let pt = Point2::new(1.0, 2.0); + /// assert_eq!(iso * pt, pt); + /// + /// let iso = Isometry3::identity(); + /// let pt = Point3::new(1.0, 2.0, 3.0); + /// assert_eq!(iso * pt, pt); + /// ``` #[inline] pub fn identity() -> Self { Self::from_parts(Translation::identity(), R::identity()) @@ -30,10 +44,24 @@ where DefaultAllocator: Allocator /// The isometry that applies the rotation `r` with its axis passing through the point `p`. /// This effectively lets `p` invariant. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Isometry2, Point2, UnitComplex}; + /// let rot = UnitComplex::new(f32::consts::PI); + /// let pt = Point2::new(1.0, 0.0); + /// let iso = Isometry2::rotation_wrt_point(rot, pt); + /// + /// assert_eq!(iso * pt, pt); // The rotation center is not affected. + /// assert_relative_eq!(iso * Point2::new(1.0, 2.0), Point2::new(1.0, -2.0), epsilon = 1.0e-6); + /// ``` #[inline] pub fn rotation_wrt_point(r: R, p: Point) -> Self { let shift = r.transform_vector(&-&p.coords); - Self::from_parts(Translation::from_vector(shift + p.coords), r) + Self::from_parts(Translation::from(shift + p.coords), r) } } @@ -81,11 +109,23 @@ where // 2D rotation. impl Isometry> { - /// Creates a new isometry from a translation and a rotation angle. + /// Creates a new 2D isometry from a translation and a rotation angle. + /// + /// Its rotational part is represented as a 2x2 rotation matrix. + /// + /// # Example + /// + /// ``` + /// # use std::f32; + /// # use nalgebra::{Isometry2, Vector2, Point2}; + /// let iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); + /// + /// assert_eq!(iso * Point2::new(3.0, 4.0), Point2::new(-3.0, 5.0)); + /// ``` #[inline] pub fn new(translation: Vector2, angle: N) -> Self { Self::from_parts( - Translation::from_vector(translation), + Translation::from(translation), Rotation::::new(angle), ) } @@ -104,11 +144,23 @@ impl Isometry> { } impl Isometry> { - /// Creates a new isometry from a translation and a rotation angle. + /// Creates a new 2D isometry from a translation and a rotation angle. + /// + /// Its rotational part is represented as an unit complex number. + /// + /// # Example + /// + /// ``` + /// # use std::f32; + /// # use nalgebra::{IsometryMatrix2, Point2, Vector2}; + /// let iso = IsometryMatrix2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); + /// + /// assert_eq!(iso * Point2::new(3.0, 4.0), Point2::new(-3.0, 5.0)); + /// ``` #[inline] pub fn new(translation: Vector2, angle: N) -> Self { Self::from_parts( - Translation::from_vector(translation), + Translation::from(translation), UnitComplex::from_angle(angle), ) } @@ -131,10 +183,33 @@ macro_rules! isometry_construction_impl( ($RotId: ident < $($RotParams: ident),*>, $RRDim: ty, $RCDim: ty) => { impl Isometry> { /// Creates a new isometry from a translation and a rotation axis-angle. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; + /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; + /// let translation = Vector3::new(1.0, 2.0, 3.0); + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// + /// // Isometry with its rotation part represented as a UnitQuaternion + /// let iso = Isometry3::new(translation, axisangle); + /// assert_relative_eq!(iso * pt, Point3::new(7.0, 7.0, -1.0), epsilon = 1.0e-6); + /// assert_relative_eq!(iso * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// + /// // Isometry with its rotation part represented as a Rotation3 (a 3x3 rotation matrix). + /// let iso = IsometryMatrix3::new(translation, axisangle); + /// assert_relative_eq!(iso * pt, Point3::new(7.0, 7.0, -1.0), epsilon = 1.0e-6); + /// assert_relative_eq!(iso * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// ``` #[inline] pub fn new(translation: Vector3, axisangle: Vector3) -> Self { Self::from_parts( - Translation::from_vector(translation), + Translation::from(translation), $RotId::<$($RotParams),*>::from_scaled_axis(axisangle)) } @@ -153,34 +228,85 @@ macro_rules! isometry_construction_impl( /// Creates an isometry that corresponds to the local frame of an observer standing at the /// point `eye` and looking toward `target`. /// - /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the - /// `eye`. + /// It maps the `z` axis to the view direction `target - eye`and the origin to the `eye`. /// /// # Arguments /// * eye - The observer position. /// * target - The target position. /// * up - Vertical direction. The only requirement of this parameter is to not be collinear /// to `eye - at`. Non-collinearity is not checked. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; + /// let eye = Point3::new(1.0, 2.0, 3.0); + /// let target = Point3::new(2.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// // Isometry with its rotation part represented as a UnitQuaternion + /// let iso = Isometry3::face_towards(&eye, &target, &up); + /// assert_eq!(iso * Point3::origin(), eye); + /// assert_relative_eq!(iso * Vector3::z(), Vector3::x()); + /// + /// // Isometry with its rotation part represented as Rotation3 (a 3x3 rotation matrix). + /// let iso = IsometryMatrix3::face_towards(&eye, &target, &up); + /// assert_eq!(iso * Point3::origin(), eye); + /// assert_relative_eq!(iso * Vector3::z(), Vector3::x()); + /// ``` #[inline] - pub fn new_observer_frame(eye: &Point3, + pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3) -> Self { Self::from_parts( - Translation::from_vector(eye.coords.clone()), - $RotId::new_observer_frame(&(target - eye), up)) + Translation::from(eye.coords.clone()), + $RotId::face_towards(&(target - eye), up)) + } + + /// Deprecated: Use [Isometry::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frame(eye: &Point3, + target: &Point3, + up: &Vector3) + -> Self { + Self::face_towards(eye, target, up) } /// Builds a right-handed look-at view matrix. /// - /// This conforms to the common notion of right handed look-at matrix from the computer - /// graphics community. + /// It maps the view direction `target - eye` to the **negative** `z` axis to and the `eye` to the origin. + /// This conforms to the common notion of right handed camera look-at **view matrix** from + /// the computer graphics community, i.e. the camera is assumed to look toward its local `-z` axis. /// /// # Arguments /// * eye - The eye position. /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; + /// let eye = Point3::new(1.0, 2.0, 3.0); + /// let target = Point3::new(2.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// // Isometry with its rotation part represented as a UnitQuaternion + /// let iso = Isometry3::look_at_rh(&eye, &target, &up); + /// assert_eq!(iso * eye, Point3::origin()); + /// assert_relative_eq!(iso * Vector3::x(), -Vector3::z()); + /// + /// // Isometry with its rotation part represented as Rotation3 (a 3x3 rotation matrix). + /// let iso = IsometryMatrix3::look_at_rh(&eye, &target, &up); + /// assert_eq!(iso * eye, Point3::origin()); + /// assert_relative_eq!(iso * Vector3::x(), -Vector3::z()); + /// ``` #[inline] pub fn look_at_rh(eye: &Point3, target: &Point3, @@ -189,19 +315,41 @@ macro_rules! isometry_construction_impl( let rotation = $RotId::look_at_rh(&(target - eye), up); let trans = &rotation * (-eye); - Self::from_parts(Translation::from_vector(trans.coords), rotation) + Self::from_parts(Translation::from(trans.coords), rotation) } /// Builds a left-handed look-at view matrix. /// - /// This conforms to the common notion of left handed look-at matrix from the computer - /// graphics community. + /// It maps the view direction `target - eye` to the **positive** `z` axis and the `eye` to the origin. + /// This conforms to the common notion of right handed camera look-at **view matrix** from + /// the computer graphics community, i.e. the camera is assumed to look toward its local `z` axis. /// /// # Arguments /// * eye - The eye position. /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; + /// let eye = Point3::new(1.0, 2.0, 3.0); + /// let target = Point3::new(2.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// // Isometry with its rotation part represented as a UnitQuaternion + /// let iso = Isometry3::look_at_lh(&eye, &target, &up); + /// assert_eq!(iso * eye, Point3::origin()); + /// assert_relative_eq!(iso * Vector3::x(), Vector3::z()); + /// + /// // Isometry with its rotation part represented as Rotation3 (a 3x3 rotation matrix). + /// let iso = IsometryMatrix3::look_at_lh(&eye, &target, &up); + /// assert_eq!(iso * eye, Point3::origin()); + /// assert_relative_eq!(iso * Vector3::x(), Vector3::z()); + /// ``` #[inline] pub fn look_at_lh(eye: &Point3, target: &Point3, @@ -210,7 +358,7 @@ macro_rules! isometry_construction_impl( let rotation = $RotId::look_at_lh(&(target - eye), up); let trans = &rotation * (-eye); - Self::from_parts(Translation::from_vector(trans.coords), rotation) + Self::from_parts(Translation::from(trans.coords), rotation) } } } diff --git a/src/geometry/isometry_conversion.rs b/src/geometry/isometry_conversion.rs index a896caf6..5dc46009 100644 --- a/src/geometry/isometry_conversion.rs +++ b/src/geometry/isometry_conversion.rs @@ -141,7 +141,9 @@ where #[inline] unsafe fn from_superset_unchecked(m: &MatrixN>) -> Self { let t = m.fixed_slice::(0, D::dim()).into_owned(); - let t = Translation::from_vector(::convert_unchecked(t)); + let t = Translation { + vector: ::convert_unchecked(t), + }; Self::from_parts(t, ::convert_unchecked(m.clone_owned())) } diff --git a/src/geometry/isometry_ops.rs b/src/geometry/isometry_ops.rs index c30c2ac1..a3608a63 100644 --- a/src/geometry/isometry_ops.rs +++ b/src/geometry/isometry_ops.rs @@ -142,7 +142,7 @@ isometry_binop_impl_all!( [ref ref] => { let shift = self.rotation.transform_vector(&rhs.translation.vector); - Isometry::from_parts(Translation::from_vector(&self.translation.vector + shift), + Isometry::from_parts(Translation::from(&self.translation.vector + shift), self.rotation.clone() * rhs.rotation.clone()) // FIXME: too bad we have to clone. }; ); @@ -267,7 +267,7 @@ isometry_binop_impl_all!( [val ref] => &self * right; [ref ref] => { let new_tr = &self.translation.vector + self.rotation.transform_vector(&right.vector); - Isometry::from_parts(Translation::from_vector(new_tr), self.rotation.clone()) + Isometry::from_parts(Translation::from(new_tr), self.rotation.clone()) }; ); @@ -339,10 +339,10 @@ isometry_from_composition_impl_all!( Mul, mul; (D, D), (D, U1) for D: DimName; self: Rotation, right: Translation, Output = Isometry>; - [val val] => Isometry::from_parts(Translation::from_vector(&self * right.vector), self); - [ref val] => Isometry::from_parts(Translation::from_vector(self * right.vector), self.clone()); - [val ref] => Isometry::from_parts(Translation::from_vector(&self * &right.vector), self); - [ref ref] => Isometry::from_parts(Translation::from_vector(self * &right.vector), self.clone()); + [val val] => Isometry::from_parts(Translation::from(&self * right.vector), self); + [ref val] => Isometry::from_parts(Translation::from(self * right.vector), self.clone()); + [val ref] => Isometry::from_parts(Translation::from(&self * &right.vector), self); + [ref ref] => Isometry::from_parts(Translation::from(self * &right.vector), self.clone()); ); // UnitQuaternion × Translation @@ -351,10 +351,10 @@ isometry_from_composition_impl_all!( (U4, U1), (U3, U1); self: UnitQuaternion, right: Translation, Output = Isometry>; - [val val] => Isometry::from_parts(Translation::from_vector(&self * right.vector), self); - [ref val] => Isometry::from_parts(Translation::from_vector( self * right.vector), self.clone()); - [val ref] => Isometry::from_parts(Translation::from_vector(&self * &right.vector), self); - [ref ref] => Isometry::from_parts(Translation::from_vector( self * &right.vector), self.clone()); + [val val] => Isometry::from_parts(Translation::from(&self * right.vector), self); + [ref val] => Isometry::from_parts(Translation::from( self * right.vector), self.clone()); + [val ref] => Isometry::from_parts(Translation::from(&self * &right.vector), self); + [ref ref] => Isometry::from_parts(Translation::from( self * &right.vector), self.clone()); ); // Rotation × Isometry @@ -368,7 +368,7 @@ isometry_from_composition_impl_all!( [val ref] => &self * right; [ref ref] => { let shift = self * &right.translation.vector; - Isometry::from_parts(Translation::from_vector(shift), self * &right.rotation) + Isometry::from_parts(Translation::from(shift), self * &right.rotation) }; ); @@ -396,7 +396,7 @@ isometry_from_composition_impl_all!( [val ref] => &self * right; [ref ref] => { let shift = self * &right.translation.vector; - Isometry::from_parts(Translation::from_vector(shift), self * &right.rotation) + Isometry::from_parts(Translation::from(shift), self * &right.rotation) }; ); diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index dd059959..9e25dc29 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -54,6 +54,8 @@ mod similarity_construction; mod similarity_conversion; mod similarity_ops; +mod swizzle; + mod transform; mod transform_alga; mod transform_alias; diff --git a/src/geometry/orthographic.rs b/src/geometry/orthographic.rs index d7e519cc..c6c5628b 100644 --- a/src/geometry/orthographic.rs +++ b/src/geometry/orthographic.rs @@ -63,21 +63,49 @@ impl<'a, N: Real + Deserialize<'a>> Deserialize<'a> for Orthographic3 { impl Orthographic3 { /// Creates a new orthographic projection matrix. + /// + /// This follows the OpenGL convention, so this will flip the `z` axis. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Orthographic3, Point3}; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// // Check this projection actually transforms the view cuboid into the double-unit cube. + /// // See https://www.nalgebra.org/projections/#orthographic-projection for more details. + /// let p1 = Point3::new(1.0, 2.0, -0.1); + /// let p2 = Point3::new(1.0, 2.0, -1000.0); + /// let p3 = Point3::new(1.0, 20.0, -0.1); + /// let p4 = Point3::new(1.0, 20.0, -1000.0); + /// let p5 = Point3::new(10.0, 2.0, -0.1); + /// let p6 = Point3::new(10.0, 2.0, -1000.0); + /// let p7 = Point3::new(10.0, 20.0, -0.1); + /// let p8 = Point3::new(10.0, 20.0, -1000.0); + /// + /// assert_relative_eq!(proj.project_point(&p1), Point3::new(-1.0, -1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p2), Point3::new(-1.0, -1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p3), Point3::new(-1.0, 1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p4), Point3::new(-1.0, 1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p5), Point3::new( 1.0, -1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p6), Point3::new( 1.0, -1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p7), Point3::new( 1.0, 1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p8), Point3::new( 1.0, 1.0, 1.0)); + /// + /// // This also works with flipped axis. In other words, we allow that + /// // `left > right`, `bottom > top`, and/or `znear > zfar`. + /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); + /// + /// assert_relative_eq!(proj.project_point(&p1), Point3::new( 1.0, 1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p2), Point3::new( 1.0, 1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p3), Point3::new( 1.0, -1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p4), Point3::new( 1.0, -1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p5), Point3::new(-1.0, 1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p6), Point3::new(-1.0, 1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p7), Point3::new(-1.0, -1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p8), Point3::new(-1.0, -1.0, -1.0)); + /// ``` #[inline] pub fn new(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> Self { - assert!( - left < right, - "The left corner must be farther than the right corner." - ); - assert!( - bottom < top, - "The top corner must be higher than the bottom corner." - ); - assert!( - znear < zfar, - "The far plane must be farther than the near plane." - ); - let matrix = Matrix4::::identity(); let mut res = Self::from_matrix_unchecked(matrix); @@ -92,6 +120,19 @@ impl Orthographic3 { /// /// It is not checked whether or not the given matrix actually represents an orthographic /// projection. + /// + /// # Example + /// ``` + /// # use nalgebra::{Orthographic3, Point3, Matrix4}; + /// let mat = Matrix4::new( + /// 2.0 / 9.0, 0.0, 0.0, -11.0 / 9.0, + /// 0.0, 2.0 / 18.0, 0.0, -22.0 / 18.0, + /// 0.0, 0.0, -2.0 / 999.9, -1000.1 / 999.9, + /// 0.0, 0.0, 0.0, 1.0 + /// ); + /// let proj = Orthographic3::from_matrix_unchecked(mat); + /// assert_eq!(proj, Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0)); + /// ``` #[inline] pub fn from_matrix_unchecked(matrix: Matrix4) -> Self { Orthographic3 { matrix: matrix } @@ -101,8 +142,8 @@ impl Orthographic3 { #[inline] pub fn from_fov(aspect: N, vfov: N, znear: N, zfar: N) -> Self { assert!( - znear < zfar, - "The far plane must be farther than the near plane." + znear != zfar, + "The far plane must not be equal to the near plane." ); assert!( !relative_eq!(aspect, N::zero()), @@ -124,6 +165,22 @@ impl Orthographic3 { } /// Retrieves the inverse of the underlying homogeneous matrix. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Orthographic3, Point3, Matrix4}; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// let inv = proj.inverse(); + /// + /// assert_relative_eq!(inv * proj.as_matrix(), Matrix4::identity()); + /// assert_relative_eq!(proj.as_matrix() * inv, Matrix4::identity()); + /// + /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); + /// let inv = proj.inverse(); + /// assert_relative_eq!(inv * proj.as_matrix(), Matrix4::identity()); + /// assert_relative_eq!(proj.as_matrix() * inv, Matrix4::identity()); + /// ``` #[inline] pub fn inverse(&self) -> Matrix4 { let mut res = self.to_homogeneous(); @@ -144,66 +201,188 @@ impl Orthographic3 { } /// Computes the corresponding homogeneous matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::{Orthographic3, Point3, Matrix4}; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// let expected = Matrix4::new( + /// 2.0 / 9.0, 0.0, 0.0, -11.0 / 9.0, + /// 0.0, 2.0 / 18.0, 0.0, -22.0 / 18.0, + /// 0.0, 0.0, -2.0 / 999.9, -1000.1 / 999.9, + /// 0.0, 0.0, 0.0, 1.0 + /// ); + /// assert_eq!(proj.to_homogeneous(), expected); + /// ``` #[inline] pub fn to_homogeneous(&self) -> Matrix4 { self.matrix } /// A reference to the underlying homogeneous transformation matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::{Orthographic3, Point3, Matrix4}; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// let expected = Matrix4::new( + /// 2.0 / 9.0, 0.0, 0.0, -11.0 / 9.0, + /// 0.0, 2.0 / 18.0, 0.0, -22.0 / 18.0, + /// 0.0, 0.0, -2.0 / 999.9, -1000.1 / 999.9, + /// 0.0, 0.0, 0.0, 1.0 + /// ); + /// assert_eq!(*proj.as_matrix(), expected); + /// ``` #[inline] pub fn as_matrix(&self) -> &Matrix4 { &self.matrix } /// A reference to this transformation seen as a `Projective3`. + /// + /// # Example + /// ``` + /// # use nalgebra::Orthographic3; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// assert_eq!(proj.as_projective().to_homogeneous(), proj.to_homogeneous()); + /// ``` #[inline] pub fn as_projective(&self) -> &Projective3 { unsafe { mem::transmute(self) } } /// This transformation seen as a `Projective3`. + /// + /// # Example + /// ``` + /// # use nalgebra::Orthographic3; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// assert_eq!(proj.to_projective().to_homogeneous(), proj.to_homogeneous()); + /// ``` #[inline] pub fn to_projective(&self) -> Projective3 { Projective3::from_matrix_unchecked(self.matrix) } /// Retrieves the underlying homogeneous matrix. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Orthographic3, Point3, Matrix4}; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// let expected = Matrix4::new( + /// 2.0 / 9.0, 0.0, 0.0, -11.0 / 9.0, + /// 0.0, 2.0 / 18.0, 0.0, -22.0 / 18.0, + /// 0.0, 0.0, -2.0 / 999.9, -1000.1 / 999.9, + /// 0.0, 0.0, 0.0, 1.0 + /// ); + /// assert_eq!(proj.into_inner(), expected); + /// ``` + #[inline] + pub fn into_inner(self) -> Matrix4 { + self.matrix + } + + /// Retrieves the underlying homogeneous matrix. + /// Deprecated: Use [Orthographic3::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] #[inline] pub fn unwrap(self) -> Matrix4 { self.matrix } - /// The smallest x-coordinate of the view cuboid. + /// The left offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// assert_relative_eq!(proj.left(), 1.0, epsilon = 1.0e-6); + /// + /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); + /// assert_relative_eq!(proj.left(), 10.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn left(&self) -> N { (-N::one() - self.matrix[(0, 3)]) / self.matrix[(0, 0)] } - /// The largest x-coordinate of the view cuboid. + /// The right offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// assert_relative_eq!(proj.right(), 10.0, epsilon = 1.0e-6); + /// + /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); + /// assert_relative_eq!(proj.right(), 1.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn right(&self) -> N { (N::one() - self.matrix[(0, 3)]) / self.matrix[(0, 0)] } - /// The smallest y-coordinate of the view cuboid. + /// The bottom offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// assert_relative_eq!(proj.bottom(), 2.0, epsilon = 1.0e-6); + /// + /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); + /// assert_relative_eq!(proj.bottom(), 20.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn bottom(&self) -> N { (-N::one() - self.matrix[(1, 3)]) / self.matrix[(1, 1)] } - /// The largest y-coordinate of the view cuboid. + /// The top offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// assert_relative_eq!(proj.top(), 20.0, epsilon = 1.0e-6); + /// + /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); + /// assert_relative_eq!(proj.top(), 2.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn top(&self) -> N { (N::one() - self.matrix[(1, 3)]) / self.matrix[(1, 1)] } /// The near plane offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// assert_relative_eq!(proj.znear(), 0.1, epsilon = 1.0e-6); + /// + /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); + /// assert_relative_eq!(proj.znear(), 1000.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn znear(&self) -> N { (N::one() + self.matrix[(2, 3)]) / self.matrix[(2, 2)] } /// The far plane offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// assert_relative_eq!(proj.zfar(), 1000.0, epsilon = 1.0e-6); + /// + /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); + /// assert_relative_eq!(proj.zfar(), 0.1, epsilon = 1.0e-6); + /// ``` #[inline] pub fn zfar(&self) -> N { (-N::one() + self.matrix[(2, 3)]) / self.matrix[(2, 2)] @@ -211,6 +390,31 @@ impl Orthographic3 { // FIXME: when we get specialization, specialize the Mul impl instead. /// Projects a point. Faster than matrix multiplication. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Orthographic3, Point3}; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// + /// let p1 = Point3::new(1.0, 2.0, -0.1); + /// let p2 = Point3::new(1.0, 2.0, -1000.0); + /// let p3 = Point3::new(1.0, 20.0, -0.1); + /// let p4 = Point3::new(1.0, 20.0, -1000.0); + /// let p5 = Point3::new(10.0, 2.0, -0.1); + /// let p6 = Point3::new(10.0, 2.0, -1000.0); + /// let p7 = Point3::new(10.0, 20.0, -0.1); + /// let p8 = Point3::new(10.0, 20.0, -1000.0); + /// + /// assert_relative_eq!(proj.project_point(&p1), Point3::new(-1.0, -1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p2), Point3::new(-1.0, -1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p3), Point3::new(-1.0, 1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p4), Point3::new(-1.0, 1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p5), Point3::new( 1.0, -1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p6), Point3::new( 1.0, -1.0, 1.0)); + /// assert_relative_eq!(proj.project_point(&p7), Point3::new( 1.0, 1.0, -1.0)); + /// assert_relative_eq!(proj.project_point(&p8), Point3::new( 1.0, 1.0, 1.0)); + /// ``` #[inline] pub fn project_point(&self, p: &Point3) -> Point3 { Point3::new( @@ -221,6 +425,31 @@ impl Orthographic3 { } /// Un-projects a point. Faster than multiplication by the underlying matrix inverse. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Orthographic3, Point3}; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// + /// let p1 = Point3::new(-1.0, -1.0, -1.0); + /// let p2 = Point3::new(-1.0, -1.0, 1.0); + /// let p3 = Point3::new(-1.0, 1.0, -1.0); + /// let p4 = Point3::new(-1.0, 1.0, 1.0); + /// let p5 = Point3::new( 1.0, -1.0, -1.0); + /// let p6 = Point3::new( 1.0, -1.0, 1.0); + /// let p7 = Point3::new( 1.0, 1.0, -1.0); + /// let p8 = Point3::new( 1.0, 1.0, 1.0); + /// + /// assert_relative_eq!(proj.unproject_point(&p1), Point3::new(1.0, 2.0, -0.1), epsilon = 1.0e-6); + /// assert_relative_eq!(proj.unproject_point(&p2), Point3::new(1.0, 2.0, -1000.0), epsilon = 1.0e-6); + /// assert_relative_eq!(proj.unproject_point(&p3), Point3::new(1.0, 20.0, -0.1), epsilon = 1.0e-6); + /// assert_relative_eq!(proj.unproject_point(&p4), Point3::new(1.0, 20.0, -1000.0), epsilon = 1.0e-6); + /// assert_relative_eq!(proj.unproject_point(&p5), Point3::new(10.0, 2.0, -0.1), epsilon = 1.0e-6); + /// assert_relative_eq!(proj.unproject_point(&p6), Point3::new(10.0, 2.0, -1000.0), epsilon = 1.0e-6); + /// assert_relative_eq!(proj.unproject_point(&p7), Point3::new(10.0, 20.0, -0.1), epsilon = 1.0e-6); + /// assert_relative_eq!(proj.unproject_point(&p8), Point3::new(10.0, 20.0, -1000.0), epsilon = 1.0e-6); + /// ``` #[inline] pub fn unproject_point(&self, p: &Point3) -> Point3 { Point3::new( @@ -232,6 +461,23 @@ impl Orthographic3 { // FIXME: when we get specialization, specialize the Mul impl instead. /// Projects a vector. Faster than matrix multiplication. + /// + /// Vectors are not affected by the translation part of the projection. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Orthographic3, Vector3}; + /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// + /// let v1 = Vector3::x(); + /// let v2 = Vector3::y(); + /// let v3 = Vector3::z(); + /// + /// assert_relative_eq!(proj.project_vector(&v1), Vector3::x() * 2.0 / 9.0); + /// assert_relative_eq!(proj.project_vector(&v2), Vector3::y() * 2.0 / 18.0); + /// assert_relative_eq!(proj.project_vector(&v3), Vector3::z() * -2.0 / 999.9); + /// ``` #[inline] pub fn project_vector(&self, p: &Vector) -> Vector3 where SB: Storage { @@ -242,28 +488,76 @@ impl Orthographic3 { ) } - /// Sets the smallest x-coordinate of the view cuboid. + /// Sets the left offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// proj.set_left(2.0); + /// assert_relative_eq!(proj.left(), 2.0, epsilon = 1.0e-6); + /// + /// // It is OK to set a left offset greater than the current right offset. + /// proj.set_left(20.0); + /// assert_relative_eq!(proj.left(), 20.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn set_left(&mut self, left: N) { let right = self.right(); self.set_left_and_right(left, right); } - /// Sets the largest x-coordinate of the view cuboid. + /// Sets the right offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// proj.set_right(15.0); + /// assert_relative_eq!(proj.right(), 15.0, epsilon = 1.0e-6); + /// + /// // It is OK to set a right offset smaller than the current left offset. + /// proj.set_right(-3.0); + /// assert_relative_eq!(proj.right(), -3.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn set_right(&mut self, right: N) { let left = self.left(); self.set_left_and_right(left, right); } - /// Sets the smallest y-coordinate of the view cuboid. + /// Sets the bottom offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// proj.set_bottom(8.0); + /// assert_relative_eq!(proj.bottom(), 8.0, epsilon = 1.0e-6); + /// + /// // It is OK to set a bottom offset greater than the current top offset. + /// proj.set_bottom(50.0); + /// assert_relative_eq!(proj.bottom(), 50.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn set_bottom(&mut self, bottom: N) { let top = self.top(); self.set_bottom_and_top(bottom, top); } - /// Sets the largest y-coordinate of the view cuboid. + /// Sets the top offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// proj.set_top(15.0); + /// assert_relative_eq!(proj.top(), 15.0, epsilon = 1.0e-6); + /// + /// // It is OK to set a top offset smaller than the current bottom offset. + /// proj.set_top(-3.0); + /// assert_relative_eq!(proj.top(), -3.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn set_top(&mut self, top: N) { let bottom = self.bottom(); @@ -271,6 +565,18 @@ impl Orthographic3 { } /// Sets the near plane offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// proj.set_znear(8.0); + /// assert_relative_eq!(proj.znear(), 8.0, epsilon = 1.0e-6); + /// + /// // It is OK to set a znear greater than the current zfar. + /// proj.set_znear(5000.0); + /// assert_relative_eq!(proj.znear(), 5000.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn set_znear(&mut self, znear: N) { let zfar = self.zfar(); @@ -278,39 +584,93 @@ impl Orthographic3 { } /// Sets the far plane offset of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// proj.set_zfar(15.0); + /// assert_relative_eq!(proj.zfar(), 15.0, epsilon = 1.0e-6); + /// + /// // It is OK to set a zfar smaller than the current znear. + /// proj.set_zfar(-3.0); + /// assert_relative_eq!(proj.zfar(), -3.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn set_zfar(&mut self, zfar: N) { let znear = self.znear(); self.set_znear_and_zfar(znear, zfar); } - /// Sets the view cuboid coordinates along the `x` axis. + /// Sets the view cuboid offsets along the `x` axis. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// proj.set_left_and_right(7.0, 70.0); + /// assert_relative_eq!(proj.left(), 7.0, epsilon = 1.0e-6); + /// assert_relative_eq!(proj.right(), 70.0, epsilon = 1.0e-6); + /// + /// // It is also OK to have `left > right`. + /// proj.set_left_and_right(70.0, 7.0); + /// assert_relative_eq!(proj.left(), 70.0, epsilon = 1.0e-6); + /// assert_relative_eq!(proj.right(), 7.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn set_left_and_right(&mut self, left: N, right: N) { assert!( - left < right, - "The left corner must be farther than the right corner." + left != right, + "The left corner must not be equal to the right corner." ); self.matrix[(0, 0)] = ::convert::<_, N>(2.0) / (right - left); self.matrix[(0, 3)] = -(right + left) / (right - left); } - /// Sets the view cuboid coordinates along the `y` axis. + /// Sets the view cuboid offsets along the `y` axis. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// proj.set_bottom_and_top(7.0, 70.0); + /// assert_relative_eq!(proj.bottom(), 7.0, epsilon = 1.0e-6); + /// assert_relative_eq!(proj.top(), 70.0, epsilon = 1.0e-6); + /// + /// // It is also OK to have `bottom > top`. + /// proj.set_bottom_and_top(70.0, 7.0); + /// assert_relative_eq!(proj.bottom(), 70.0, epsilon = 1.0e-6); + /// assert_relative_eq!(proj.top(), 7.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn set_bottom_and_top(&mut self, bottom: N, top: N) { assert!( - bottom < top, - "The top corner must be higher than the bottom corner." + bottom != top, + "The top corner must not be equal to the bottom corner." ); self.matrix[(1, 1)] = ::convert::<_, N>(2.0) / (top - bottom); self.matrix[(1, 3)] = -(top + bottom) / (top - bottom); } /// Sets the near and far plane offsets of the view cuboid. + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Orthographic3; + /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); + /// proj.set_znear_and_zfar(50.0, 5000.0); + /// assert_relative_eq!(proj.znear(), 50.0, epsilon = 1.0e-6); + /// assert_relative_eq!(proj.zfar(), 5000.0, epsilon = 1.0e-6); + /// + /// // It is also OK to have `znear > zfar`. + /// proj.set_znear_and_zfar(5000.0, 0.5); + /// assert_relative_eq!(proj.znear(), 5000.0, epsilon = 1.0e-6); + /// assert_relative_eq!(proj.zfar(), 0.5, epsilon = 1.0e-6); + /// ``` #[inline] pub fn set_znear_and_zfar(&mut self, znear: N, zfar: N) { assert!( - !relative_eq!(zfar - znear, N::zero()), + zfar != znear, "The near-plane and far-plane must not be superimposed." ); self.matrix[(2, 2)] = -::convert::<_, N>(2.0) / (zfar - znear); @@ -352,6 +712,6 @@ where Matrix4: Send impl From> for Matrix4 { #[inline] fn from(orth: Orthographic3) -> Self { - orth.unwrap() + orth.into_inner() } } diff --git a/src/geometry/perspective.rs b/src/geometry/perspective.rs index 77642554..b7e0d8ea 100644 --- a/src/geometry/perspective.rs +++ b/src/geometry/perspective.rs @@ -141,6 +141,14 @@ impl Perspective3 { /// Retrieves the underlying homogeneous matrix. #[inline] + pub fn into_inner(self) -> Matrix4 { + self.matrix + } + + /// Retrieves the underlying homogeneous matrix. + /// Deprecated: Use [Perspective3::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] + #[inline] pub fn unwrap(self) -> Matrix4 { self.matrix } @@ -279,6 +287,6 @@ impl Arbitrary for Perspective3 { impl From> for Matrix4 { #[inline] fn from(orth: Perspective3) -> Self { - orth.unwrap() + orth.into_inner() } } diff --git a/src/geometry/point.rs b/src/geometry/point.rs index 0530c0c4..0d24ab1f 100644 --- a/src/geometry/point.rs +++ b/src/geometry/point.rs @@ -19,7 +19,7 @@ use base::{DefaultAllocator, Scalar, VectorN}; /// A point in a n-dimensional euclidean space. #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Point where DefaultAllocator: Allocator { @@ -44,17 +44,6 @@ where { } -impl Clone for Point -where - DefaultAllocator: Allocator, - >::Buffer: Clone, -{ - #[inline] - fn clone(&self) -> Self { - Point::from_coordinates(self.coords.clone()) - } -} - #[cfg(feature = "serde-serialize")] impl Serialize for Point where @@ -77,7 +66,7 @@ where where Des: Deserializer<'a> { let coords = VectorN::::deserialize(deserializer)?; - Ok(Point::from_coordinates(coords)) + Ok(Point::from(coords)) } } @@ -105,14 +94,21 @@ where impl Point where DefaultAllocator: Allocator { - /// Clones this point into one that owns its data. - #[inline] - pub fn clone(&self) -> Point { - Point::from_coordinates(self.coords.clone_owned()) - } - /// Converts this point into a vector in homogeneous coordinates, i.e., appends a `1` at the /// end of it. + /// + /// This is the same as `.into()`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Point2, Point3, Vector3, Vector4}; + /// let p = Point2::new(10.0, 20.0); + /// assert_eq!(p.to_homogeneous(), Vector3::new(10.0, 20.0, 1.0)); + /// + /// // This works in any dimension. + /// let p = Point3::new(10.0, 20.0, 30.0); + /// assert_eq!(p.to_homogeneous(), Vector4::new(10.0, 20.0, 30.0, 1.0)); + /// ``` #[inline] pub fn to_homogeneous(&self) -> VectorN> where @@ -128,12 +124,24 @@ where DefaultAllocator: Allocator } /// Creates a new point with the given coordinates. + #[deprecated(note = "Use Point::from(vector) instead.")] #[inline] pub fn from_coordinates(coords: VectorN) -> Point { Point { coords: coords } } /// The dimension of this point. + /// + /// # Example + /// ``` + /// # use nalgebra::{Point2, Point3}; + /// let p = Point2::new(1.0, 2.0); + /// assert_eq!(p.len(), 2); + /// + /// // This works in any dimension. + /// let p = Point3::new(10.0, 20.0, 30.0); + /// assert_eq!(p.len(), 3); + /// ``` #[inline] pub fn len(&self) -> usize { self.coords.len() @@ -142,11 +150,23 @@ where DefaultAllocator: Allocator /// The stride of this point. This is the number of buffer element separating each component of /// this point. #[inline] + #[deprecated(note = "This methods is no longer significant and will always return 1.")] pub fn stride(&self) -> usize { self.coords.strides().0 } /// Iterates through this point coordinates. + /// + /// # Example + /// ``` + /// # use nalgebra::Point3; + /// let p = Point3::new(1.0, 2.0, 3.0); + /// let mut it = p.iter().cloned(); + /// + /// assert_eq!(it.next(), Some(1.0)); + /// assert_eq!(it.next(), Some(2.0)); + /// assert_eq!(it.next(), Some(3.0)); + /// assert_eq!(it.next(), None); #[inline] pub fn iter(&self) -> MatrixIter>::Buffer> { self.coords.iter() @@ -159,6 +179,17 @@ where DefaultAllocator: Allocator } /// Mutably iterates through this point coordinates. + /// + /// # Example + /// ``` + /// # use nalgebra::Point3; + /// let mut p = Point3::new(1.0, 2.0, 3.0); + /// + /// for e in p.iter_mut() { + /// *e *= 10.0; + /// } + /// + /// assert_eq!(p, Point3::new(10.0, 20.0, 30.0)); #[inline] pub fn iter_mut( &mut self, diff --git a/src/geometry/point_alga.rs b/src/geometry/point_alga.rs index 42a1088a..56940dd1 100644 --- a/src/geometry/point_alga.rs +++ b/src/geometry/point_alga.rs @@ -33,7 +33,7 @@ where DefaultAllocator: Allocator #[inline] fn from_coordinates(coords: Self::Coordinates) -> Self { - Self::from_coordinates(coords) + Self::from(coords) } #[inline] @@ -54,7 +54,7 @@ where { #[inline] fn meet(&self, other: &Self) -> Self { - Point::from_coordinates(self.coords.meet(&other.coords)) + Point::from(self.coords.meet(&other.coords)) } } @@ -65,7 +65,7 @@ where { #[inline] fn join(&self, other: &Self) -> Self { - Point::from_coordinates(self.coords.join(&other.coords)) + Point::from(self.coords.join(&other.coords)) } } @@ -78,6 +78,6 @@ where fn meet_join(&self, other: &Self) -> (Self, Self) { let (meet, join) = self.coords.meet_join(&other.coords); - (Point::from_coordinates(meet), Point::from_coordinates(join)) + (Point::from(meet), Point::from(join)) } } diff --git a/src/geometry/point_construction.rs b/src/geometry/point_construction.rs index c4f6bfc1..2c030f61 100644 --- a/src/geometry/point_construction.rs +++ b/src/geometry/point_construction.rs @@ -18,26 +18,79 @@ where DefaultAllocator: Allocator /// Creates a new point with uninitialized coordinates. #[inline] pub unsafe fn new_uninitialized() -> Self { - Self::from_coordinates(VectorN::new_uninitialized()) + Self::from(VectorN::new_uninitialized()) } /// Creates a new point with all coordinates equal to zero. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Point2, Point3}; + /// // This works in any dimension. + /// // The explicit :: type annotation may not always be needed, + /// // depending on the context of type inference. + /// let pt = Point2::::origin(); + /// assert!(pt.x == 0.0 && pt.y == 0.0); + /// + /// let pt = Point3::::origin(); + /// assert!(pt.x == 0.0 && pt.y == 0.0 && pt.z == 0.0); + /// ``` #[inline] pub fn origin() -> Self where N: Zero { - Self::from_coordinates(VectorN::from_element(N::zero())) + Self::from(VectorN::from_element(N::zero())) } /// Creates a new point from a slice. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Point2, Point3}; + /// let data = [ 1.0, 2.0, 3.0 ]; + /// + /// let pt = Point2::from_slice(&data[..2]); + /// assert_eq!(pt, Point2::new(1.0, 2.0)); + /// + /// let pt = Point3::from_slice(&data); + /// assert_eq!(pt, Point3::new(1.0, 2.0, 3.0)); + /// ``` #[inline] pub fn from_slice(components: &[N]) -> Self { - Self::from_coordinates(VectorN::from_row_slice(components)) + Self::from(VectorN::from_row_slice(components)) } /// Creates a new point from its homogeneous vector representation. /// /// In practice, this builds a D-dimensional points with the same first D component as `v` /// divided by the last component of `v`. Returns `None` if this divisor is zero. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Point2, Point3, Vector3, Vector4}; + /// + /// let coords = Vector4::new(1.0, 2.0, 3.0, 1.0); + /// let pt = Point3::from_homogeneous(coords); + /// assert_eq!(pt, Some(Point3::new(1.0, 2.0, 3.0))); + /// + /// // All component of the result will be divided by the + /// // last component of the vector, here 2.0. + /// let coords = Vector4::new(1.0, 2.0, 3.0, 2.0); + /// let pt = Point3::from_homogeneous(coords); + /// assert_eq!(pt, Some(Point3::new(0.5, 1.0, 1.5))); + /// + /// // Fails because the last component is zero. + /// let coords = Vector4::new(1.0, 2.0, 3.0, 0.0); + /// let pt = Point3::from_homogeneous(coords); + /// assert!(pt.is_none()); + /// + /// // Works also in other dimensions. + /// let coords = Vector3::new(1.0, 2.0, 1.0); + /// let pt = Point2::from_homogeneous(coords); + /// assert_eq!(pt, Some(Point2::new(1.0, 2.0))); + /// ``` #[inline] pub fn from_homogeneous(v: VectorN>) -> Option where @@ -47,7 +100,7 @@ where DefaultAllocator: Allocator { if !v[D::dim()].is_zero() { let coords = v.fixed_slice::(0, 0) / v[D::dim()]; - Some(Self::from_coordinates(coords)) + Some(Self::from(coords)) } else { None } @@ -64,12 +117,12 @@ where DefaultAllocator: Allocator { #[inline] fn max_value() -> Self { - Self::from_coordinates(VectorN::max_value()) + Self::from(VectorN::max_value()) } #[inline] fn min_value() -> Self { - Self::from_coordinates(VectorN::min_value()) + Self::from(VectorN::min_value()) } } @@ -80,7 +133,7 @@ where { #[inline] fn sample<'a, G: Rng + ?Sized>(&self, rng: &mut G) -> Point { - Point::from_coordinates(rng.gen::>()) + Point::from(rng.gen::>()) } } @@ -92,7 +145,7 @@ where { #[inline] fn arbitrary(g: &mut G) -> Self { - Point::from_coordinates(VectorN::arbitrary(g)) + Point::from(VectorN::arbitrary(g)) } } @@ -102,10 +155,13 @@ where * */ macro_rules! componentwise_constructors_impl( - ($($D: ty, $($args: ident:$irow: expr),*);* $(;)*) => {$( + ($($doc: expr; $D: ty, $($args: ident:$irow: expr),*);* $(;)*) => {$( impl Point where DefaultAllocator: Allocator { - /// Initializes this matrix from its components. + #[doc = "Initializes this point from its components."] + #[doc = "# Example\n```"] + #[doc = $doc] + #[doc = "```"] #[inline] pub fn new($($args: N),*) -> Point { unsafe { @@ -120,11 +176,17 @@ macro_rules! componentwise_constructors_impl( ); componentwise_constructors_impl!( + "# use nalgebra::Point1;\nlet p = Point1::new(1.0);\nassert!(p.x == 1.0);"; U1, x:0; + "# use nalgebra::Point2;\nlet p = Point2::new(1.0, 2.0);\nassert!(p.x == 1.0 && p.y == 2.0);"; U2, x:0, y:1; + "# use nalgebra::Point3;\nlet p = Point3::new(1.0, 2.0, 3.0);\nassert!(p.x == 1.0 && p.y == 2.0 && p.z == 3.0);"; U3, x:0, y:1, z:2; + "# use nalgebra::Point4;\nlet p = Point4::new(1.0, 2.0, 3.0, 4.0);\nassert!(p.x == 1.0 && p.y == 2.0 && p.z == 3.0 && p.w == 4.0);"; U4, x:0, y:1, z:2, w:3; + "# use nalgebra::Point5;\nlet p = Point5::new(1.0, 2.0, 3.0, 4.0, 5.0);\nassert!(p.x == 1.0 && p.y == 2.0 && p.z == 3.0 && p.w == 4.0 && p.a == 5.0);"; U5, x:0, y:1, z:2, w:3, a:4; + "# 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);"; U6, x:0, y:1, z:2, w:3, a:4, b:5; ); @@ -132,7 +194,9 @@ macro_rules! from_array_impl( ($($D: ty, $len: expr);*) => {$( impl From<[N; $len]> for Point { fn from (coords: [N; $len]) -> Self { - Point::from_coordinates(coords.into()) + Point { + coords: coords.into() + } } } )*} diff --git a/src/geometry/point_conversion.rs b/src/geometry/point_conversion.rs index 1b1cbaf7..84efab6d 100644 --- a/src/geometry/point_conversion.rs +++ b/src/geometry/point_conversion.rs @@ -33,7 +33,7 @@ where { #[inline] fn to_superset(&self) -> Point { - Point::from_coordinates(self.coords.to_superset()) + Point::from(self.coords.to_superset()) } #[inline] @@ -45,7 +45,7 @@ where #[inline] unsafe fn from_superset_unchecked(m: &Point) -> Self { - Point::from_coordinates(Matrix::from_superset_unchecked(&m.coords)) + Point::from(Matrix::from_superset_unchecked(&m.coords)) } } @@ -73,7 +73,9 @@ where #[inline] unsafe fn from_superset_unchecked(v: &VectorN>) -> Self { let coords = v.fixed_slice::(0, 0) / v[D::dim()]; - Self::from_coordinates(::convert_unchecked(coords)) + Self { + coords: ::convert_unchecked(coords) + } } } @@ -138,3 +140,15 @@ where t.to_homogeneous() } } + +impl From> for Point + where + DefaultAllocator: Allocator, +{ + #[inline] + fn from(coords: VectorN) -> Self { + Point { + coords + } + } +} diff --git a/src/geometry/point_ops.rs b/src/geometry/point_ops.rs index 03b07c39..a923fbf9 100644 --- a/src/geometry/point_ops.rs +++ b/src/geometry/point_ops.rs @@ -50,7 +50,7 @@ where DefaultAllocator: Allocator #[inline] fn neg(self) -> Self::Output { - Point::from_coordinates(-self.coords) + Point::from(-self.coords) } } @@ -61,7 +61,7 @@ where DefaultAllocator: Allocator #[inline] fn neg(self) -> Self::Output { - Point::from_coordinates(-&self.coords) + Point::from(-&self.coords) } } @@ -96,43 +96,43 @@ add_sub_impl!(Sub, sub, ClosedSub; add_sub_impl!(Sub, sub, ClosedSub; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage; self: &'a Point, right: &'b Vector, Output = Point; - Self::Output::from_coordinates(&self.coords - right); 'a, 'b); + Self::Output::from(&self.coords - right); 'a, 'b); add_sub_impl!(Sub, sub, ClosedSub; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage; self: &'a Point, right: Vector, Output = Point; - Self::Output::from_coordinates(&self.coords - &right); 'a); // FIXME: should not be a ref to `right`. + Self::Output::from(&self.coords - &right); 'a); // FIXME: should not be a ref to `right`. add_sub_impl!(Sub, sub, ClosedSub; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage; self: Point, right: &'b Vector, Output = Point; - Self::Output::from_coordinates(self.coords - right); 'b); + Self::Output::from(self.coords - right); 'b); add_sub_impl!(Sub, sub, ClosedSub; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage; self: Point, right: Vector, Output = Point; - Self::Output::from_coordinates(self.coords - right); ); + Self::Output::from(self.coords - right); ); // Point + Vector add_sub_impl!(Add, add, ClosedAdd; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage; self: &'a Point, right: &'b Vector, Output = Point; - Self::Output::from_coordinates(&self.coords + right); 'a, 'b); + Self::Output::from(&self.coords + right); 'a, 'b); add_sub_impl!(Add, add, ClosedAdd; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage; self: &'a Point, right: Vector, Output = Point; - Self::Output::from_coordinates(&self.coords + &right); 'a); // FIXME: should not be a ref to `right`. + Self::Output::from(&self.coords + &right); 'a); // FIXME: should not be a ref to `right`. add_sub_impl!(Add, add, ClosedAdd; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage; self: Point, right: &'b Vector, Output = Point; - Self::Output::from_coordinates(self.coords + right); 'b); + Self::Output::from(self.coords + right); 'b); add_sub_impl!(Add, add, ClosedAdd; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage; self: Point, right: Vector, Output = Point; - Self::Output::from_coordinates(self.coords + right); ); + Self::Output::from(self.coords + right); ); // XXX: replace by the shared macro: add_sub_assign_impl macro_rules! op_assign_impl( @@ -178,10 +178,10 @@ md_impl_all!( (R1, C1), (D2, U1) for R1: DimName, C1: Dim, D2: DimName, SA: Storage where ShapeConstraint: AreMultipliable; self: Matrix, right: Point, Output = Point; - [val val] => Point::from_coordinates(self * right.coords); - [ref val] => Point::from_coordinates(self * right.coords); - [val ref] => Point::from_coordinates(self * &right.coords); - [ref ref] => Point::from_coordinates(self * &right.coords); + [val val] => Point::from(self * right.coords); + [ref val] => Point::from(self * right.coords); + [val ref] => Point::from(self * &right.coords); + [ref ref] => Point::from(self * &right.coords); ); /* @@ -198,7 +198,7 @@ macro_rules! componentwise_scalarop_impl( #[inline] fn $method(self, right: N) -> Self::Output { - Point::from_coordinates(self.coords.$method(right)) + Point::from(self.coords.$method(right)) } } @@ -208,7 +208,7 @@ macro_rules! componentwise_scalarop_impl( #[inline] fn $method(self, right: N) -> Self::Output { - Point::from_coordinates((&self.coords).$method(right)) + Point::from((&self.coords).$method(right)) } } @@ -233,7 +233,7 @@ macro_rules! left_scalar_mul_impl( #[inline] fn mul(self, right: Point<$T, D>) -> Self::Output { - Point::from_coordinates(self * right.coords) + Point::from(self * right.coords) } } @@ -243,7 +243,7 @@ macro_rules! left_scalar_mul_impl( #[inline] fn mul(self, right: &'b Point<$T, D>) -> Self::Output { - Point::from_coordinates(self * &right.coords) + Point::from(self * &right.coords) } } )*} diff --git a/src/geometry/quaternion.rs b/src/geometry/quaternion.rs index 6fb70d2b..96e4f19a 100644 --- a/src/geometry/quaternion.rs +++ b/src/geometry/quaternion.rs @@ -68,7 +68,7 @@ impl Copy for Quaternion {} impl Clone for Quaternion { #[inline] fn clone(&self) -> Self { - Quaternion::from_vector(self.coords.clone()) + Quaternion::from(self.coords.clone()) } } @@ -90,7 +90,7 @@ where Owned: Deserialize<'a> where Des: Deserializer<'a> { let coords = Vector4::::deserialize(deserializer)?; - Ok(Quaternion::from_vector(coords)) + Ok(Quaternion::from(coords)) } } @@ -106,16 +106,33 @@ impl Quaternion { #[inline] #[deprecated(note = "This method is a no-op and will be removed in a future release.")] pub fn clone_owned(&self) -> Quaternion { - Quaternion::from_vector(self.coords.clone_owned()) + Quaternion::from(self.coords.clone_owned()) } /// Normalizes this quaternion. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// let q_normalized = q.normalize(); + /// relative_eq!(q_normalized.norm(), 1.0); + /// ``` #[inline] pub fn normalize(&self) -> Quaternion { - Quaternion::from_vector(self.coords.normalize()) + Quaternion::from(self.coords.normalize()) } - /// Compute the conjugate of this quaternion. + /// The conjugate of this quaternion. + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// let conj = q.conjugate(); + /// assert!(conj.i == -2.0 && conj.j == -3.0 && conj.k == -4.0 && conj.w == 1.0); + /// ``` #[inline] pub fn conjugate(&self) -> Quaternion { let v = Vector4::new( @@ -124,13 +141,30 @@ impl Quaternion { -self.coords[2], self.coords[3], ); - Quaternion::from_vector(v) + Quaternion::from(v) } /// Inverts this quaternion if it is not zero. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// let inv_q = q.try_inverse(); + /// + /// assert!(inv_q.is_some()); + /// assert_relative_eq!(inv_q.unwrap() * q, Quaternion::identity()); + /// + /// //Non-invertible case + /// let q = Quaternion::new(0.0, 0.0, 0.0, 0.0); + /// let inv_q = q.try_inverse(); + /// + /// assert!(inv_q.is_none()); + /// ``` #[inline] pub fn try_inverse(&self) -> Option> { - let mut res = Quaternion::from_vector(self.coords.clone_owned()); + let mut res = Quaternion::from(self.coords.clone_owned()); if res.try_inverse_mut() { Some(res) @@ -140,30 +174,74 @@ impl Quaternion { } /// Linear interpolation between two quaternion. + /// + /// Computes `self * (1 - t) + other * t`. + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let q1 = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// let q2 = Quaternion::new(10.0, 20.0, 30.0, 40.0); + /// + /// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(1.9, 3.8, 5.7, 7.6)); + /// ``` #[inline] pub fn lerp(&self, other: &Quaternion, t: N) -> Quaternion { self * (N::one() - t) + other * t } /// The vector part `(i, j, k)` of this quaternion. + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// assert_eq!(q.vector()[0], 2.0); + /// assert_eq!(q.vector()[1], 3.0); + /// assert_eq!(q.vector()[2], 4.0); + /// ``` #[inline] pub fn vector(&self) -> MatrixSlice, CStride> { self.coords.fixed_rows::(0) } /// The scalar part `w` of this quaternion. + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// assert_eq!(q.scalar(), 1.0); + /// ``` #[inline] pub fn scalar(&self) -> N { self.coords[3] } /// Reinterprets this quaternion as a 4D vector. + /// + /// # Example + /// ``` + /// # use nalgebra::{Vector4, Quaternion}; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// // Recall that the quaternion is stored internally as (i, j, k, w) + /// // while the ::new constructor takes the arguments as (w, i, j, k). + /// assert_eq!(*q.as_vector(), Vector4::new(2.0, 3.0, 4.0, 1.0)); + /// ``` #[inline] pub fn as_vector(&self) -> &Vector4 { &self.coords } /// The norm of this quaternion. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// assert_relative_eq!(q.norm(), 5.47722557, epsilon = 1.0e-6); + /// ``` #[inline] pub fn norm(&self) -> N { self.coords.norm() @@ -172,30 +250,58 @@ impl Quaternion { /// A synonym for the norm of this quaternion. /// /// Aka the length. + /// This is the same as `.norm()` /// - /// This function is simply implemented as a call to `norm()` + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// assert_relative_eq!(q.magnitude(), 5.47722557, epsilon = 1.0e-6); + /// ``` #[inline] pub fn magnitude(&self) -> N { self.norm() } - /// A synonym for the squared norm of this quaternion. - /// - /// Aka the squared length. - /// - /// This function is simply implemented as a call to `norm_squared()` - #[inline] - pub fn magnitude_squared(&self) -> N { - self.norm_squared() - } - /// The squared norm of this quaternion. + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// assert_eq!(q.magnitude_squared(), 30.0); + /// ``` #[inline] pub fn norm_squared(&self) -> N { self.coords.norm_squared() } + /// A synonym for the squared norm of this quaternion. + /// + /// Aka the squared length. + /// This is the same as `.norm_squared()` + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// assert_eq!(q.magnitude_squared(), 30.0); + /// ``` + #[inline] + pub fn magnitude_squared(&self) -> N { + self.norm_squared() + } + /// The dot product of two quaternions. + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let q1 = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// let q2 = Quaternion::new(5.0, 6.0, 7.0, 8.0); + /// assert_eq!(q1.dot(&q2), 70.0); + /// ``` #[inline] pub fn dot(&self, rhs: &Self) -> N { self.coords.dot(&rhs.coords) @@ -205,6 +311,17 @@ impl Quaternion { /// /// Returns, from left to right: the quaternion norm, the half rotation angle, the rotation /// axis. If the rotation angle is zero, the rotation axis is set to `None`. + /// + /// # Example + /// ``` + /// # use std::f32; + /// # use nalgebra::{Vector3, Quaternion}; + /// let q = Quaternion::new(0.0, 5.0, 0.0, 0.0); + /// let (norm, half_ang, axis) = q.polar_decomposition(); + /// assert_eq!(norm, 5.0); + /// assert_eq!(half_ang, f32::consts::FRAC_PI_2); + /// assert_eq!(axis, Some(Vector3::x_axis())); + /// ``` pub fn polar_decomposition(&self) -> (N, N, Option>>) { if let Some((q, n)) = Unit::try_new_and_get(*self, N::zero()) { if let Some(axis) = Unit::try_new(self.vector().clone_owned(), N::zero()) { @@ -219,13 +336,52 @@ impl Quaternion { } } + /// Compute the natural logarithm of a quaternion. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(2.0, 5.0, 0.0, 0.0); + /// assert_relative_eq!(q.ln(), Quaternion::new(1.683647, 1.190289, 0.0, 0.0), epsilon = 1.0e-6) + /// ``` + #[inline] + pub fn ln(&self) -> Quaternion { + let n = self.norm(); + let v = self.vector(); + let s = self.scalar(); + + Quaternion::from_parts(n.ln(), v.normalize() * (s / n).acos()) + } + /// Compute the exponential of a quaternion. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.683647, 1.190289, 0.0, 0.0); + /// assert_relative_eq!(q.exp(), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5) + /// ``` #[inline] pub fn exp(&self) -> Quaternion { self.exp_eps(N::default_epsilon()) } - /// Compute the exponential of a quaternion. + /// Compute the exponential of a quaternion. Returns the identity if the vector part of this quaternion + /// has a norm smaller than `eps`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.683647, 1.190289, 0.0, 0.0); + /// assert_relative_eq!(q.exp_eps(1.0e-6), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5); + /// + /// // Singular case. + /// let q = Quaternion::new(0.0000001, 0.0, 0.0, 0.0); + /// assert_eq!(q.exp_eps(1.0e-6), Quaternion::identity()); + /// ``` #[inline] pub fn exp_eps(&self, eps: N) -> Quaternion { let v = self.vector(); @@ -238,33 +394,52 @@ impl Quaternion { let n = nn.sqrt(); let nv = v * (w_exp * n.sin() / n); - Quaternion::from_parts(n.cos(), nv) + Quaternion::from_parts(w_exp * n.cos(), nv) } } - /// Compute the natural logarithm of a quaternion. - #[inline] - pub fn ln(&self) -> Quaternion { - let n = self.norm(); - let v = self.vector(); - let s = self.scalar(); - - Quaternion::from_parts(n.ln(), v.normalize() * (s / n).acos()) - } - /// Raise the quaternion to a given floating power. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// assert_relative_eq!(q.powf(1.5), Quaternion::new( -6.2576659, 4.1549037, 6.2323556, 8.3098075), epsilon = 1.0e-6); + /// ``` #[inline] pub fn powf(&self, n: N) -> Quaternion { (self.ln() * n).exp() } /// Transforms this quaternion into its 4D vector form (Vector part, Scalar part). + /// + /// # Example + /// ``` + /// # use nalgebra::{Quaternion, Vector4}; + /// let mut q = Quaternion::identity(); + /// *q.as_vector_mut() = Vector4::new(1.0, 2.0, 3.0, 4.0); + /// assert!(q.i == 1.0 && q.j == 2.0 && q.k == 3.0 && q.w == 4.0); + /// ``` #[inline] pub fn as_vector_mut(&mut self) -> &mut Vector4 { &mut self.coords } /// The mutable vector part `(i, j, k)` of this quaternion. + /// + /// # Example + /// ``` + /// # use nalgebra::{Quaternion, Vector4}; + /// let mut q = Quaternion::identity(); + /// { + /// let mut v = q.vector_mut(); + /// v[0] = 2.0; + /// v[1] = 3.0; + /// v[2] = 4.0; + /// } + /// assert!(q.i == 2.0 && q.j == 3.0 && q.k == 4.0 && q.w == 1.0); + /// ``` #[inline] pub fn vector_mut( &mut self, @@ -273,6 +448,14 @@ impl Quaternion { } /// Replaces this quaternion by its conjugate. + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// q.conjugate_mut(); + /// assert!(q.i == -2.0 && q.j == -3.0 && q.k == -4.0 && q.w == 1.0); + /// ``` #[inline] pub fn conjugate_mut(&mut self) { self.coords[0] = -self.coords[0]; @@ -281,6 +464,20 @@ impl Quaternion { } /// Inverts this quaternion in-place if it is not zero. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// + /// assert!(q.try_inverse_mut()); + /// assert_relative_eq!(q * Quaternion::new(1.0, 2.0, 3.0, 4.0), Quaternion::identity()); + /// + /// //Non-invertible case + /// let mut q = Quaternion::new(0.0, 0.0, 0.0, 0.0); + /// assert!(!q.try_inverse_mut()); + /// ``` #[inline] pub fn try_inverse_mut(&mut self) -> bool { let norm_squared = self.norm_squared(); @@ -296,6 +493,15 @@ impl Quaternion { } /// Normalizes this quaternion. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Quaternion; + /// let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// q.normalize_mut(); + /// assert_relative_eq!(q.norm(), 1.0); + /// ``` #[inline] pub fn normalize_mut(&mut self) -> N { self.coords.normalize_mut() @@ -368,19 +574,31 @@ pub type UnitQuaternion = Unit>; impl UnitQuaternion { /// Moves this unit quaternion into one that owns its data. #[inline] - #[deprecated(note = "This method is a no-op and will be removed in a future release.")] + #[deprecated( + note = "This method is unnecessary and will be removed in a future release. Use `.clone()` instead." + )] pub fn into_owned(self) -> UnitQuaternion { self } /// Clones this unit quaternion into one that owns its data. #[inline] - #[deprecated(note = "This method is a no-op and will be removed in a future release.")] + #[deprecated( + note = "This method is unnecessary and will be removed in a future release. Use `.clone()` instead." + )] pub fn clone_owned(&self) -> UnitQuaternion { *self } /// The rotation angle in [0; pi] of this unit quaternion. + /// + /// # Example + /// ``` + /// # use nalgebra::{Unit, UnitQuaternion, Vector3}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let rot = UnitQuaternion::from_axis_angle(&axis, 1.78); + /// assert_eq!(rot.angle(), 1.78); + /// ``` #[inline] pub fn angle(&self) -> N { let w = self.quaternion().scalar().abs(); @@ -396,24 +614,59 @@ impl UnitQuaternion { /// The underlying quaternion. /// /// Same as `self.as_ref()`. + /// + /// # Example + /// ``` + /// # use nalgebra::{UnitQuaternion, Quaternion}; + /// let axis = UnitQuaternion::identity(); + /// assert_eq!(*axis.quaternion(), Quaternion::new(1.0, 0.0, 0.0, 0.0)); + /// ``` #[inline] pub fn quaternion(&self) -> &Quaternion { self.as_ref() } /// Compute the conjugate of this unit quaternion. + /// + /// # Example + /// ``` + /// # use nalgebra::{Unit, UnitQuaternion, Vector3}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let rot = UnitQuaternion::from_axis_angle(&axis, 1.78); + /// let conj = rot.conjugate(); + /// assert_eq!(conj, UnitQuaternion::from_axis_angle(&-axis, 1.78)); + /// ``` #[inline] pub fn conjugate(&self) -> UnitQuaternion { UnitQuaternion::new_unchecked(self.as_ref().conjugate()) } /// Inverts this quaternion if it is not zero. + /// + /// # Example + /// ``` + /// # use nalgebra::{Unit, UnitQuaternion, Vector3}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let rot = UnitQuaternion::from_axis_angle(&axis, 1.78); + /// let inv = rot.inverse(); + /// assert_eq!(rot * inv, UnitQuaternion::identity()); + /// assert_eq!(inv * rot, UnitQuaternion::identity()); + /// ``` #[inline] pub fn inverse(&self) -> UnitQuaternion { self.conjugate() } /// The rotation angle needed to make `self` and `other` coincide. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{UnitQuaternion, Vector3}; + /// let rot1 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 1.0); + /// let rot2 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.1); + /// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6); + /// ``` #[inline] pub fn angle_to(&self, other: &UnitQuaternion) -> N { let delta = self.rotation_to(other); @@ -423,6 +676,16 @@ impl UnitQuaternion { /// The unit quaternion needed to make `self` and `other` coincide. /// /// The result is such that: `self.rotation_to(other) * self == other`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{UnitQuaternion, Vector3}; + /// let rot1 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 1.0); + /// let rot2 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.1); + /// let rot_to = rot1.rotation_to(&rot2); + /// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6); + /// ``` #[inline] pub fn rotation_to(&self, other: &UnitQuaternion) -> UnitQuaternion { other / self @@ -431,12 +694,30 @@ impl UnitQuaternion { /// Linear interpolation between two unit quaternions. /// /// The result is not normalized. + /// + /// # Example + /// ``` + /// # use nalgebra::{UnitQuaternion, Quaternion}; + /// let q1 = UnitQuaternion::new_normalize(Quaternion::new(1.0, 0.0, 0.0, 0.0)); + /// let q2 = UnitQuaternion::new_normalize(Quaternion::new(0.0, 1.0, 0.0, 0.0)); + /// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(0.9, 0.1, 0.0, 0.0)); + /// ``` #[inline] pub fn lerp(&self, other: &UnitQuaternion, t: N) -> Quaternion { self.as_ref().lerp(other.as_ref(), t) } /// Normalized linear interpolation between two unit quaternions. + /// + /// This is the same as `self.lerp` except that the result is normalized. + /// + /// # Example + /// ``` + /// # use nalgebra::{UnitQuaternion, Quaternion}; + /// let q1 = UnitQuaternion::new_normalize(Quaternion::new(1.0, 0.0, 0.0, 0.0)); + /// let q2 = UnitQuaternion::new_normalize(Quaternion::new(0.0, 1.0, 0.0, 0.0)); + /// assert_eq!(q1.nlerp(&q2, 0.1), UnitQuaternion::new_normalize(Quaternion::new(0.9, 0.1, 0.0, 0.0))); + /// ``` #[inline] pub fn nlerp(&self, other: &UnitQuaternion, t: N) -> UnitQuaternion { let mut res = self.lerp(other, t); @@ -448,13 +729,13 @@ impl UnitQuaternion { /// Spherical linear interpolation between two unit quaternions. /// /// Panics if the angle between both quaternion is 180 degrees (in which case the interpolation - /// is not well-defined). + /// is not well-defined). Use `.try_slerp` instead to avoid the panic. #[inline] pub fn slerp(&self, other: &UnitQuaternion, t: N) -> UnitQuaternion { - Unit::new_unchecked(Quaternion::from_vector( + Unit::new_unchecked(Quaternion::from( Unit::new_unchecked(self.coords) .slerp(&Unit::new_unchecked(other.coords), t) - .unwrap(), + .into_inner(), )) } @@ -478,7 +759,7 @@ impl UnitQuaternion { { Unit::new_unchecked(self.coords) .try_slerp(&Unit::new_unchecked(other.coords), t, epsilon) - .map(|q| Unit::new_unchecked(Quaternion::from_vector(q.unwrap()))) + .map(|q| Unit::new_unchecked(Quaternion::from(q.into_inner()))) } /// Compute the conjugate of this unit quaternion in-place. @@ -488,12 +769,36 @@ impl UnitQuaternion { } /// Inverts this quaternion if it is not zero. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; + /// let axisangle = Vector3::new(0.1, 0.2, 0.3); + /// let mut rot = UnitQuaternion::new(axisangle); + /// rot.inverse_mut(); + /// assert_relative_eq!(rot * UnitQuaternion::new(axisangle), UnitQuaternion::identity()); + /// assert_relative_eq!(UnitQuaternion::new(axisangle) * rot, UnitQuaternion::identity()); + /// ``` #[inline] pub fn inverse_mut(&mut self) { self.as_mut_unchecked().conjugate_mut() } /// The rotation axis of this unit quaternion or `None` if the rotation is zero. + /// + /// # Example + /// ``` + /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let angle = 1.2; + /// let rot = UnitQuaternion::from_axis_angle(&axis, angle); + /// assert_eq!(rot.axis(), Some(axis)); + /// + /// // Case with a zero angle. + /// let rot = UnitQuaternion::from_axis_angle(&axis, 0.0); + /// assert!(rot.axis().is_none()); + /// ``` #[inline] pub fn axis(&self) -> Option>> { let v = if self.quaternion().scalar() >= N::zero() { @@ -506,10 +811,19 @@ impl UnitQuaternion { } /// The rotation axis of this unit quaternion multiplied by the rotation angle. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; + /// let axisangle = Vector3::new(0.1, 0.2, 0.3); + /// let rot = UnitQuaternion::new(axisangle); + /// assert_relative_eq!(rot.scaled_axis(), axisangle, epsilon = 1.0e-6); + /// ``` #[inline] pub fn scaled_axis(&self) -> Vector3 { if let Some(axis) = self.axis() { - axis.unwrap() * self.angle() + axis.into_inner() * self.angle() } else { Vector3::zero() } @@ -518,6 +832,19 @@ impl UnitQuaternion { /// The rotation axis and angle in ]0, pi] of this unit quaternion. /// /// Returns `None` if the angle is zero. + /// + /// # Example + /// ``` + /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let angle = 1.2; + /// let rot = UnitQuaternion::from_axis_angle(&axis, angle); + /// assert_eq!(rot.axis_angle(), Some((axis, angle))); + /// + /// // Case with a zero angle. + /// let rot = UnitQuaternion::from_axis_angle(&axis, 0.0); + /// assert!(rot.axis_angle().is_none()); + /// ``` #[inline] pub fn axis_angle(&self) -> Option<(Unit>, N)> { if let Some(axis) = self.axis() { @@ -540,10 +867,19 @@ impl UnitQuaternion { /// Note that this function yields a `Quaternion` because it looses the unit property. /// The vector part of the return value corresponds to the axis-angle representation (divided /// by 2.0) of this unit quaternion. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Vector3, UnitQuaternion}; + /// let axisangle = Vector3::new(0.1, 0.2, 0.3); + /// let q = UnitQuaternion::new(axisangle); + /// assert_relative_eq!(q.ln().vector().into_owned(), axisangle, epsilon = 1.0e-6); + /// ``` #[inline] pub fn ln(&self) -> Quaternion { if let Some(v) = self.axis() { - Quaternion::from_parts(N::zero(), v.unwrap() * self.angle()) + Quaternion::from_parts(N::zero(), v.into_inner() * self.angle()) } else { Quaternion::zero() } @@ -553,6 +889,18 @@ impl UnitQuaternion { /// /// This returns the unit quaternion that identifies a rotation with axis `self.axis()` and /// angle `self.angle() × n`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let angle = 1.2; + /// let rot = UnitQuaternion::from_axis_angle(&axis, angle); + /// let pow = rot.powf(2.0); + /// assert_relative_eq!(pow.axis().unwrap(), axis, epsilon = 1.0e-6); + /// assert_eq!(pow.angle(), 2.4); + /// ``` #[inline] pub fn powf(&self, n: N) -> UnitQuaternion { if let Some(v) = self.axis() { @@ -563,6 +911,21 @@ impl UnitQuaternion { } /// Builds a rotation matrix from this unit quaternion. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Vector3, Matrix3}; + /// let q = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); + /// let rot = q.to_rotation_matrix(); + /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, + /// 0.5, 0.8660254, 0.0, + /// 0.0, 0.0, 1.0); + /// + /// assert_relative_eq!(*rot.matrix(), expected, epsilon = 1.0e-6); + /// ``` #[inline] pub fn to_rotation_matrix(&self) -> Rotation { let i = self.as_ref()[0]; @@ -596,13 +959,48 @@ impl UnitQuaternion { /// Converts this unit quaternion into its equivalent Euler angles. /// - /// The angles are produced in the form (roll, yaw, pitch). + /// The angles are produced in the form (roll, pitch, yaw). #[inline] + #[deprecated(note = "This is renamed to use `.euler_angles()`.")] pub fn to_euler_angles(&self) -> (N, N, N) { - self.to_rotation_matrix().to_euler_angles() + self.euler_angles() + } + + /// Retrieves the euler angles corresponding to this unit quaternion. + /// + /// The angles are produced in the form (roll, pitch, yaw). + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::UnitQuaternion; + /// let rot = UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3); + /// let euler = rot.euler_angles(); + /// assert_relative_eq!(euler.0, 0.1, epsilon = 1.0e-6); + /// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6); + /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); + /// ``` + #[inline] + pub fn euler_angles(&self) -> (N, N, N) { + self.to_rotation_matrix().euler_angles() } /// Converts this unit quaternion into its equivalent homogeneous transformation matrix. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Vector3, Matrix4}; + /// let rot = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); + /// let expected = Matrix4::new(0.8660254, -0.5, 0.0, 0.0, + /// 0.5, 0.8660254, 0.0, 0.0, + /// 0.0, 0.0, 1.0, 0.0, + /// 0.0, 0.0, 0.0, 1.0); + /// + /// assert_relative_eq!(rot.to_homogeneous(), expected, epsilon = 1.0e-6); + /// ``` #[inline] pub fn to_homogeneous(&self) -> MatrixN { self.to_rotation_matrix().to_homogeneous() @@ -612,7 +1010,7 @@ impl UnitQuaternion { impl fmt::Display for UnitQuaternion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(axis) = self.axis() { - let axis = axis.unwrap(); + let axis = axis.into_inner(); write!( f, "UnitQuaternion angle: {} − axis: ({}, {}, {})", diff --git a/src/geometry/quaternion_alga.rs b/src/geometry/quaternion_alga.rs index 4b926560..fe1a33b8 100644 --- a/src/geometry/quaternion_alga.rs +++ b/src/geometry/quaternion_alga.rs @@ -98,7 +98,7 @@ impl FiniteDimVectorSpace for Quaternion { #[inline] fn canonical_basis_element(i: usize) -> Self { - Self::from_vector(Vector4::canonical_basis_element(i)) + Self::from(Vector4::canonical_basis_element(i)) } #[inline] @@ -131,7 +131,7 @@ impl NormedSpace for Quaternion { #[inline] fn normalize(&self) -> Self { let v = self.coords.normalize(); - Self::from_vector(v) + Self::from(v) } #[inline] @@ -142,7 +142,7 @@ impl NormedSpace for Quaternion { #[inline] fn try_normalize(&self, min_norm: N) -> Option { if let Some(v) = self.coords.try_normalize(min_norm) { - Some(Self::from_vector(v)) + Some(Self::from(v)) } else { None } diff --git a/src/geometry/quaternion_construction.rs b/src/geometry/quaternion_construction.rs index 5daa0a3f..257cb719 100644 --- a/src/geometry/quaternion_construction.rs +++ b/src/geometry/quaternion_construction.rs @@ -23,6 +23,7 @@ impl Quaternion { /// Creates a quaternion from a 4D vector. The quaternion scalar part corresponds to the `w` /// vector component. #[inline] + #[deprecated(note = "Use `::from` instead.")] pub fn from_vector(vector: Vector4) -> Self { Quaternion { coords: vector } } @@ -30,17 +31,36 @@ impl Quaternion { /// Creates a new quaternion from its individual components. Note that the arguments order does /// **not** follow the storage order. /// - /// The storage order is `[ x, y, z, w ]`. + /// The storage order is `[ i, j, k, w ]` while the arguments for this functions are in the + /// order `(w, i, j, k)`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Quaternion, Vector4}; + /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// assert!(q.i == 2.0 && q.j == 3.0 && q.k == 4.0 && q.w == 1.0); + /// assert_eq!(*q.as_vector(), Vector4::new(2.0, 3.0, 4.0, 1.0)); + /// ``` #[inline] - pub fn new(w: N, x: N, y: N, z: N) -> Self { - let v = Vector4::::new(x, y, z, w); - Self::from_vector(v) + pub fn new(w: N, i: N, j: N, k: N) -> Self { + let v = Vector4::::new(i, j, k, w); + Self::from(v) } /// Creates a new quaternion from its scalar and vector parts. Note that the arguments order does /// **not** follow the storage order. /// /// The storage order is [ vector, scalar ]. + /// + /// # Example + /// ``` + /// # use nalgebra::{Quaternion, Vector3, Vector4}; + /// let w = 1.0; + /// let ijk = Vector3::new(2.0, 3.0, 4.0); + /// let q = Quaternion::from_parts(w, ijk); + /// assert!(q.i == 2.0 && q.j == 3.0 && q.k == 4.0 && q.w == 1.0); + /// assert_eq!(*q.as_vector(), Vector4::new(2.0, 3.0, 4.0, 1.0)); + /// ``` #[inline] // FIXME: take a reference to `vector`? pub fn from_parts(scalar: N, vector: Vector) -> Self @@ -56,10 +76,20 @@ impl Quaternion { where SB: Storage { let rot = UnitQuaternion::::from_axis_angle(&axis, theta * ::convert(2.0f64)); - rot.unwrap() * scale + rot.into_inner() * scale } /// The quaternion multiplicative identity. + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let q = Quaternion::identity(); + /// let q2 = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// + /// assert_eq!(q * q2, q2); + /// assert_eq!(q2 * q, q2); + /// ``` #[inline] pub fn identity() -> Self { Self::new(N::one(), N::zero(), N::zero(), N::zero()) @@ -110,7 +140,21 @@ where Owned: Send } impl UnitQuaternion { - /// The quaternion multiplicative identity. + /// The rotation identity. + /// + /// # Example + /// ``` + /// # use nalgebra::{UnitQuaternion, Vector3, Point3}; + /// let q = UnitQuaternion::identity(); + /// let q2 = UnitQuaternion::new(Vector3::new(1.0, 2.0, 3.0)); + /// let v = Vector3::new_random(); + /// let p = Point3::from(v); + /// + /// assert_eq!(q * q2, q2); + /// assert_eq!(q2 * q, q2); + /// assert_eq!(q * v, v); + /// assert_eq!(q * p, p); + /// ``` #[inline] pub fn identity() -> Self { Self::new_unchecked(Quaternion::identity()) @@ -118,6 +162,27 @@ impl UnitQuaternion { /// Creates a new quaternion from a unit vector (the rotation axis) and an angle /// (the rotation angle). + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; + /// let axis = Vector3::y_axis(); + /// let angle = f32::consts::FRAC_PI_2; + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// let q = UnitQuaternion::from_axis_angle(&axis, angle); + /// + /// assert_eq!(q.axis().unwrap(), axis); + /// assert_eq!(q.angle(), angle); + /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// + /// // A zero vector yields an identity. + /// assert_eq!(UnitQuaternion::from_scaled_axis(Vector3::::zeros()), UnitQuaternion::identity()); + /// ``` #[inline] pub fn from_axis_angle(axis: &Unit>, angle: N) -> Self where SB: Storage { @@ -138,6 +203,17 @@ impl UnitQuaternion { /// Creates a new unit quaternion from Euler angles. /// /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::UnitQuaternion; + /// let rot = UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3); + /// let euler = rot.euler_angles(); + /// assert_relative_eq!(euler.0, 0.1, epsilon = 1.0e-6); + /// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6); + /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); + /// ``` #[inline] pub fn from_euler_angles(roll: N, pitch: N, yaw: N) -> Self { let (sr, cr) = (roll * ::convert(0.5f64)).sin_cos(); @@ -155,6 +231,19 @@ impl UnitQuaternion { } /// Builds an unit quaternion from a rotation matrix. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation3, UnitQuaternion, Vector3}; + /// let axis = Vector3::y_axis(); + /// let angle = 0.1; + /// let rot = Rotation3::from_axis_angle(&axis, angle); + /// let q = UnitQuaternion::from_rotation_matrix(&rot); + /// assert_relative_eq!(q.to_rotation_matrix(), rot, epsilon = 1.0e-6); + /// assert_relative_eq!(q.axis().unwrap(), rot.axis().unwrap(), epsilon = 1.0e-6); + /// assert_relative_eq!(q.angle(), rot.angle(), epsilon = 1.0e-6); + /// ``` #[inline] pub fn from_rotation_matrix(rotmat: &Rotation) -> Self { // Robust matrix to quaternion transformation. @@ -206,6 +295,17 @@ impl UnitQuaternion { /// The unit quaternion needed to make `a` and `b` be collinear and point toward the same /// direction. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Vector3, UnitQuaternion}; + /// let a = Vector3::new(1.0, 2.0, 3.0); + /// let b = Vector3::new(3.0, 1.0, 2.0); + /// let q = UnitQuaternion::rotation_between(&a, &b).unwrap(); + /// assert_relative_eq!(q * a, b); + /// assert_relative_eq!(q.inverse() * b, a); + /// ``` #[inline] pub fn rotation_between(a: &Vector, b: &Vector) -> Option where @@ -217,6 +317,18 @@ impl UnitQuaternion { /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Vector3, UnitQuaternion}; + /// let a = Vector3::new(1.0, 2.0, 3.0); + /// let b = Vector3::new(3.0, 1.0, 2.0); + /// let q2 = UnitQuaternion::scaled_rotation_between(&a, &b, 0.2).unwrap(); + /// let q5 = UnitQuaternion::scaled_rotation_between(&a, &b, 0.5).unwrap(); + /// assert_relative_eq!(q2 * q2 * q2 * q2 * q2 * a, b, epsilon = 1.0e-6); + /// assert_relative_eq!(q5 * q5 * a, b, epsilon = 1.0e-6); + /// ``` #[inline] pub fn scaled_rotation_between( a: &Vector, @@ -240,6 +352,17 @@ impl UnitQuaternion { /// The unit quaternion needed to make `a` and `b` be collinear and point toward the same /// direction. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Unit, Vector3, UnitQuaternion}; + /// let a = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let b = Unit::new_normalize(Vector3::new(3.0, 1.0, 2.0)); + /// let q = UnitQuaternion::rotation_between(&a, &b).unwrap(); + /// assert_relative_eq!(q * a, b); + /// assert_relative_eq!(q.inverse() * b, a); + /// ``` #[inline] pub fn rotation_between_axis( a: &Unit>, @@ -254,6 +377,18 @@ impl UnitQuaternion { /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Unit, Vector3, UnitQuaternion}; + /// let a = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let b = Unit::new_normalize(Vector3::new(3.0, 1.0, 2.0)); + /// let q2 = UnitQuaternion::scaled_rotation_between(&a, &b, 0.2).unwrap(); + /// let q5 = UnitQuaternion::scaled_rotation_between(&a, &b, 0.5).unwrap(); + /// assert_relative_eq!(q2 * q2 * q2 * q2 * q2 * a, b, epsilon = 1.0e-6); + /// assert_relative_eq!(q5 * q5 * a, b, epsilon = 1.0e-6); + /// ``` #[inline] pub fn scaled_rotation_between_axis( na: &Unit>, @@ -293,63 +428,127 @@ impl UnitQuaternion { /// Creates an unit quaternion that corresponds to the local frame of an observer standing at the /// origin and looking toward `dir`. /// - /// It maps the view direction `dir` to the positive `z` axis. + /// It maps the `z` axis to the direction `dir`. /// /// # Arguments - /// * dir - The look direction, that is, direction the matrix `z` axis will be aligned with. - /// * up - The vertical direction. The only requirement of this parameter is to not be - /// collinear - /// to `dir`. Non-collinearity is not checked. + /// * dir - The look direction. It does not need to be normalized. + /// * up - The vertical direction. It does not need to be normalized. + /// The only requirement of this parameter is to not be collinear to `dir`. Non-collinearity + /// is not checked. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Vector3}; + /// let dir = Vector3::new(1.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// let q = UnitQuaternion::face_towards(&dir, &up); + /// assert_relative_eq!(q * Vector3::z(), dir.normalize()); + /// ``` #[inline] - pub fn new_observer_frame(dir: &Vector, up: &Vector) -> Self + pub fn face_towards(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { - Self::from_rotation_matrix(&Rotation::::new_observer_frame(dir, up)) + Self::from_rotation_matrix(&Rotation::::face_towards(dir, up)) + } + + /// Deprecated: Use [UnitQuaternion::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frames(dir: &Vector, up: &Vector) -> Self + where + SB: Storage, + SC: Storage, + { + Self::face_towards(dir, up) } /// Builds a right-handed look-at view matrix without translation. /// + /// It maps the view direction `dir` to the **negative** `z` axis. /// This conforms to the common notion of right handed look-at matrix from the computer /// graphics community. /// /// # Arguments - /// * eye - The eye position. - /// * target - The target position. - /// * up - A vector approximately aligned with required the vertical axis. The only - /// requirement of this parameter is to not be collinear to `target - eye`. + /// * dir − The view direction. It does not need to be normalized. + /// * up - A vector approximately aligned with required the vertical axis. It does not need + /// to be normalized. The only requirement of this parameter is to not be collinear to `dir`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Vector3}; + /// let dir = Vector3::new(1.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// let q = UnitQuaternion::look_at_rh(&dir, &up); + /// assert_relative_eq!(q * dir.normalize(), -Vector3::z()); + /// ``` #[inline] pub fn look_at_rh(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { - Self::new_observer_frame(&-dir, up).inverse() + Self::face_towards(&-dir, up).inverse() } /// Builds a left-handed look-at view matrix without translation. /// + /// It maps the view direction `dir` to the **positive** `z` axis. /// This conforms to the common notion of left handed look-at matrix from the computer /// graphics community. /// /// # Arguments - /// * eye - The eye position. - /// * target - The target position. + /// * dir − The view direction. It does not need to be normalized. /// * up - A vector approximately aligned with required the vertical axis. The only - /// requirement of this parameter is to not be collinear to `target - eye`. + /// requirement of this parameter is to not be collinear to `dir`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Vector3}; + /// let dir = Vector3::new(1.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// let q = UnitQuaternion::look_at_lh(&dir, &up); + /// assert_relative_eq!(q * dir.normalize(), Vector3::z()); + /// ``` #[inline] pub fn look_at_lh(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { - Self::new_observer_frame(dir, up).inverse() + Self::face_towards(dir, up).inverse() } /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. /// /// If `axisangle` has a magnitude smaller than `N::default_epsilon()`, this returns the identity rotation. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; + /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// let q = UnitQuaternion::new(axisangle); + /// + /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// + /// // A zero vector yields an identity. + /// assert_eq!(UnitQuaternion::new(Vector3::::zeros()), UnitQuaternion::identity()); + /// ``` #[inline] pub fn new(axisangle: Vector) -> Self where SB: Storage { @@ -361,6 +560,24 @@ impl UnitQuaternion { /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. /// /// If `axisangle` has a magnitude smaller than `eps`, this returns the identity rotation. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; + /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// let q = UnitQuaternion::new_eps(axisangle, 1.0e-6); + /// + /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// + /// // An almost zero vector yields an identity. + /// assert_eq!(UnitQuaternion::new_eps(Vector3::new(1.0e-8, 1.0e-9, 1.0e-7), 1.0e-6), UnitQuaternion::identity()); + /// ``` #[inline] pub fn new_eps(axisangle: Vector, eps: N) -> Self where SB: Storage { @@ -373,6 +590,24 @@ impl UnitQuaternion { /// /// If `axisangle` has a magnitude smaller than `N::default_epsilon()`, this returns the identity rotation. /// Same as `Self::new(axisangle)`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; + /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// let q = UnitQuaternion::from_scaled_axis(axisangle); + /// + /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// + /// // A zero vector yields an identity. + /// assert_eq!(UnitQuaternion::from_scaled_axis(Vector3::::zeros()), UnitQuaternion::identity()); + /// ``` #[inline] pub fn from_scaled_axis(axisangle: Vector) -> Self where SB: Storage { @@ -382,7 +617,25 @@ impl UnitQuaternion { /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. /// /// If `axisangle` has a magnitude smaller than `eps`, this returns the identity rotation. - /// Same as `Self::new(axisangle)`. + /// Same as `Self::new_eps(axisangle, eps)`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; + /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// let q = UnitQuaternion::from_scaled_axis_eps(axisangle, 1.0e-6); + /// + /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// + /// // An almost zero vector yields an identity. + /// assert_eq!(UnitQuaternion::from_scaled_axis_eps(Vector3::new(1.0e-8, 1.0e-9, 1.0e-7), 1.0e-6), UnitQuaternion::identity()); + /// ``` #[inline] pub fn from_scaled_axis_eps(axisangle: Vector, eps: N) -> Self where SB: Storage { @@ -436,15 +689,16 @@ where #[cfg(test)] mod tests { + extern crate rand_xorshift; use super::*; - use rand::{self, SeedableRng}; + use rand::SeedableRng; #[test] fn random_unit_quats_are_unit() { - let mut rng = rand::prng::XorShiftRng::from_seed([0xAB; 16]); + let mut rng = rand_xorshift::XorShiftRng::from_seed([0xAB; 16]); for _ in 0..1000 { let x = rng.gen::>(); - assert!(relative_eq!(x.unwrap().norm(), 1.0)) + assert!(relative_eq!(x.into_inner().norm(), 1.0)) } } } diff --git a/src/geometry/quaternion_conversion.rs b/src/geometry/quaternion_conversion.rs index 451bc9c0..c2d70726 100644 --- a/src/geometry/quaternion_conversion.rs +++ b/src/geometry/quaternion_conversion.rs @@ -39,7 +39,7 @@ where { #[inline] fn to_superset(&self) -> Quaternion { - Quaternion::from_vector(self.coords.to_superset()) + Quaternion::from(self.coords.to_superset()) } #[inline] @@ -49,7 +49,9 @@ where #[inline] unsafe fn from_superset_unchecked(q: &Quaternion) -> Self { - Self::from_vector(q.coords.to_subset_unchecked()) + Self { + coords: q.coords.to_subset_unchecked(), + } } } @@ -226,6 +228,13 @@ impl From> for Matrix4 { impl From> for Matrix3 { #[inline] fn from(q: UnitQuaternion) -> Matrix3 { - q.to_rotation_matrix().unwrap() + q.to_rotation_matrix().into_inner() + } +} + +impl From> for Quaternion { + #[inline] + fn from(coords: Vector4) -> Self { + Quaternion { coords } } } diff --git a/src/geometry/quaternion_ops.rs b/src/geometry/quaternion_ops.rs index 498a0def..842b4edd 100644 --- a/src/geometry/quaternion_ops.rs +++ b/src/geometry/quaternion_ops.rs @@ -103,28 +103,28 @@ quaternion_op_impl!( Add, add; (U4, U1), (U4, U1); self: &'a Quaternion, rhs: &'b Quaternion, Output = Quaternion; - Quaternion::from_vector(&self.coords + &rhs.coords); + Quaternion::from(&self.coords + &rhs.coords); 'a, 'b); quaternion_op_impl!( Add, add; (U4, U1), (U4, U1); self: &'a Quaternion, rhs: Quaternion, Output = Quaternion; - Quaternion::from_vector(&self.coords + rhs.coords); + Quaternion::from(&self.coords + rhs.coords); 'a); quaternion_op_impl!( Add, add; (U4, U1), (U4, U1); self: Quaternion, rhs: &'b Quaternion, Output = Quaternion; - Quaternion::from_vector(self.coords + &rhs.coords); + Quaternion::from(self.coords + &rhs.coords); 'b); quaternion_op_impl!( Add, add; (U4, U1), (U4, U1); self: Quaternion, rhs: Quaternion, Output = Quaternion; - Quaternion::from_vector(self.coords + rhs.coords); + Quaternion::from(self.coords + rhs.coords); ); // Quaternion - Quaternion @@ -132,28 +132,28 @@ quaternion_op_impl!( Sub, sub; (U4, U1), (U4, U1); self: &'a Quaternion, rhs: &'b Quaternion, Output = Quaternion; - Quaternion::from_vector(&self.coords - &rhs.coords); + Quaternion::from(&self.coords - &rhs.coords); 'a, 'b); quaternion_op_impl!( Sub, sub; (U4, U1), (U4, U1); self: &'a Quaternion, rhs: Quaternion, Output = Quaternion; - Quaternion::from_vector(&self.coords - rhs.coords); + Quaternion::from(&self.coords - rhs.coords); 'a); quaternion_op_impl!( Sub, sub; (U4, U1), (U4, U1); self: Quaternion, rhs: &'b Quaternion, Output = Quaternion; - Quaternion::from_vector(self.coords - &rhs.coords); + Quaternion::from(self.coords - &rhs.coords); 'b); quaternion_op_impl!( Sub, sub; (U4, U1), (U4, U1); self: Quaternion, rhs: Quaternion, Output = Quaternion; - Quaternion::from_vector(self.coords - rhs.coords); + Quaternion::from(self.coords - rhs.coords); ); // Quaternion × Quaternion @@ -428,7 +428,7 @@ quaternion_op_impl!( (U4, U1), (U3, U1); self: &'a UnitQuaternion, rhs: &'b Point3, Output = Point3 => U3, U4; - Point3::from_coordinates(self * &rhs.coords); + Point3::from(self * &rhs.coords); 'a, 'b); quaternion_op_impl!( @@ -436,7 +436,7 @@ quaternion_op_impl!( (U4, U1), (U3, U1); self: &'a UnitQuaternion, rhs: Point3, Output = Point3 => U3, U4; - Point3::from_coordinates(self * rhs.coords); + Point3::from(self * rhs.coords); 'a); quaternion_op_impl!( @@ -444,7 +444,7 @@ quaternion_op_impl!( (U4, U1), (U3, U1); self: UnitQuaternion, rhs: &'b Point3, Output = Point3 => U3, U4; - Point3::from_coordinates(self * &rhs.coords); + Point3::from(self * &rhs.coords); 'b); quaternion_op_impl!( @@ -452,7 +452,7 @@ quaternion_op_impl!( (U4, U1), (U3, U1); self: UnitQuaternion, rhs: Point3, Output = Point3 => U3, U4; - Point3::from_coordinates(self * rhs.coords); + Point3::from(self * rhs.coords); ); // UnitQuaternion × Unit @@ -469,7 +469,7 @@ quaternion_op_impl!( (U4, U1), (U3, U1) for SB: Storage ; self: &'a UnitQuaternion, rhs: Unit>, Output = Unit> => U3, U4; - Unit::new_unchecked(self * rhs.unwrap()); + Unit::new_unchecked(self * rhs.into_inner()); 'a); quaternion_op_impl!( @@ -485,7 +485,7 @@ quaternion_op_impl!( (U4, U1), (U3, U1) for SB: Storage ; self: UnitQuaternion, rhs: Unit>, Output = Unit> => U3, U4; - Unit::new_unchecked(self * rhs.unwrap()); + Unit::new_unchecked(self * rhs.into_inner()); ); macro_rules! scalar_op_impl( @@ -495,7 +495,7 @@ macro_rules! scalar_op_impl( #[inline] fn $op(self, n: N) -> Self::Output { - Quaternion::from_vector(self.coords.$op(n)) + Quaternion::from(self.coords.$op(n)) } } @@ -504,7 +504,7 @@ macro_rules! scalar_op_impl( #[inline] fn $op(self, n: N) -> Self::Output { - Quaternion::from_vector((&self.coords).$op(n)) + Quaternion::from((&self.coords).$op(n)) } } @@ -530,7 +530,7 @@ macro_rules! left_scalar_mul_impl( #[inline] fn mul(self, right: Quaternion<$T>) -> Self::Output { - Quaternion::from_vector(self * right.coords) + Quaternion::from(self * right.coords) } } @@ -539,7 +539,7 @@ macro_rules! left_scalar_mul_impl( #[inline] fn mul(self, right: &'b Quaternion<$T>) -> Self::Output { - Quaternion::from_vector(self * &right.coords) + Quaternion::from(self * &right.coords) } } )*} @@ -552,7 +552,7 @@ impl Neg for Quaternion { #[inline] fn neg(self) -> Self::Output { - Quaternion::from_vector(-self.coords) + Quaternion::from(-self.coords) } } @@ -561,7 +561,7 @@ impl<'a, N: Real> Neg for &'a Quaternion { #[inline] fn neg(self) -> Self::Output { - Quaternion::from_vector(-&self.coords) + Quaternion::from(-&self.coords) } } diff --git a/src/geometry/reflection.rs b/src/geometry/reflection.rs index bd4da0cd..fa08dcdd 100644 --- a/src/geometry/reflection.rs +++ b/src/geometry/reflection.rs @@ -20,7 +20,7 @@ impl> Reflection { /// represents a plane that passes through the origin. pub fn new(axis: Unit>, bias: N) -> Reflection { Reflection { - axis: axis.unwrap(), + axis: axis.into_inner(), bias: bias, } } diff --git a/src/geometry/rotation.rs b/src/geometry/rotation.rs index 7dfa56f3..6c50230e 100644 --- a/src/geometry/rotation.rs +++ b/src/geometry/rotation.rs @@ -108,28 +108,100 @@ impl Rotation where DefaultAllocator: Allocator { /// A reference to the underlying matrix representation of this rotation. + /// + /// # Example + /// ``` + /// # use nalgebra::{Rotation2, Rotation3, Vector3, Matrix2, Matrix3}; + /// # use std::f32; + /// let rot = Rotation3::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); + /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, + /// 0.5, 0.8660254, 0.0, + /// 0.0, 0.0, 1.0); + /// assert_eq!(*rot.matrix(), expected); + /// + /// + /// let rot = Rotation2::new(f32::consts::FRAC_PI_6); + /// let expected = Matrix2::new(0.8660254, -0.5, + /// 0.5, 0.8660254); + /// assert_eq!(*rot.matrix(), expected); + /// ``` #[inline] pub fn matrix(&self) -> &MatrixN { &self.matrix } /// A mutable reference to the underlying matrix representation of this rotation. - /// - /// This is unsafe because this allows the user to replace the matrix by another one that is - /// non-square, non-inversible, or non-orthonormal. If one of those properties is broken, - /// subsequent method calls may be UB. #[inline] + #[deprecated(note = "Use `.matrix_mut_unchecked()` instead.")] pub unsafe fn matrix_mut(&mut self) -> &mut MatrixN { &mut self.matrix } + /// A mutable reference to the underlying matrix representation of this rotation. + /// + /// This is suffixed by "_unchecked" because this allows the user to replace the matrix by another one that is + /// non-square, non-inversible, or non-orthonormal. If one of those properties is broken, + /// subsequent method calls may be UB. + #[inline] + pub fn matrix_mut_unchecked(&mut self) -> &mut MatrixN { + &mut self.matrix + } + /// Unwraps the underlying matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::{Rotation2, Rotation3, Vector3, Matrix2, Matrix3}; + /// # use std::f32; + /// let rot = Rotation3::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); + /// let mat = rot.into_inner(); + /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, + /// 0.5, 0.8660254, 0.0, + /// 0.0, 0.0, 1.0); + /// assert_eq!(mat, expected); + /// + /// + /// let rot = Rotation2::new(f32::consts::FRAC_PI_6); + /// let mat = rot.into_inner(); + /// let expected = Matrix2::new(0.8660254, -0.5, + /// 0.5, 0.8660254); + /// assert_eq!(mat, expected); + /// ``` + #[inline] + pub fn into_inner(self) -> MatrixN { + self.matrix + } + + /// Unwraps the underlying matrix. + /// Deprecated: Use [Rotation::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] #[inline] pub fn unwrap(self) -> MatrixN { self.matrix } /// Converts this rotation into its equivalent homogeneous transformation matrix. + /// + /// This is the same as `self.into()`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Rotation2, Rotation3, Vector3, Matrix3, Matrix4}; + /// # use std::f32; + /// let rot = Rotation3::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); + /// let expected = Matrix4::new(0.8660254, -0.5, 0.0, 0.0, + /// 0.5, 0.8660254, 0.0, 0.0, + /// 0.0, 0.0, 1.0, 0.0, + /// 0.0, 0.0, 0.0, 1.0); + /// assert_eq!(rot.to_homogeneous(), expected); + /// + /// + /// let rot = Rotation2::new(f32::consts::FRAC_PI_6); + /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, + /// 0.5, 0.8660254, 0.0, + /// 0.0, 0.0, 1.0); + /// assert_eq!(rot.to_homogeneous(), expected); + /// ``` #[inline] pub fn to_homogeneous(&self) -> MatrixN> where @@ -137,6 +209,9 @@ where DefaultAllocator: Allocator D: DimNameAdd, DefaultAllocator: Allocator, DimNameSum>, { + // We could use `MatrixN::to_homogeneous()` here, but that would imply + // adding the additional traits `DimAdd` and `IsNotStaticOne`. Maybe + // these things will get nicer once specialization lands in Rust. let mut res = MatrixN::>::identity(); res.fixed_slice_mut::(0, 0).copy_from(&self.matrix); @@ -146,6 +221,25 @@ where DefaultAllocator: Allocator /// Creates a new rotation from the given square matrix. /// /// The matrix squareness is checked but not its orthonormality. + /// + /// # Example + /// ``` + /// # use nalgebra::{Rotation2, Rotation3, Matrix2, Matrix3}; + /// # use std::f32; + /// let mat = Matrix3::new(0.8660254, -0.5, 0.0, + /// 0.5, 0.8660254, 0.0, + /// 0.0, 0.0, 1.0); + /// let rot = Rotation3::from_matrix_unchecked(mat); + /// + /// assert_eq!(*rot.matrix(), mat); + /// + /// + /// let mat = Matrix2::new(0.8660254, -0.5, + /// 0.5, 0.8660254); + /// let rot = Rotation2::from_matrix_unchecked(mat); + /// + /// assert_eq!(*rot.matrix(), mat); + /// ``` #[inline] pub fn from_matrix_unchecked(matrix: MatrixN) -> Rotation { assert!( @@ -157,24 +251,100 @@ where DefaultAllocator: Allocator } /// Transposes `self`. + /// + /// Same as `.inverse()` because the inverse of a rotation matrix is its transform. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation2, Rotation3, Vector3}; + /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); + /// let tr_rot = rot.transpose(); + /// assert_relative_eq!(rot * tr_rot, Rotation3::identity(), epsilon = 1.0e-6); + /// assert_relative_eq!(tr_rot * rot, Rotation3::identity(), epsilon = 1.0e-6); + /// + /// let rot = Rotation2::new(1.2); + /// let tr_rot = rot.transpose(); + /// assert_relative_eq!(rot * tr_rot, Rotation2::identity(), epsilon = 1.0e-6); + /// assert_relative_eq!(tr_rot * rot, Rotation2::identity(), epsilon = 1.0e-6); + /// ``` #[inline] pub fn transpose(&self) -> Rotation { Rotation::from_matrix_unchecked(self.matrix.transpose()) } /// Inverts `self`. + /// + /// Same as `.transpose()` because the inverse of a rotation matrix is its transform. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation2, Rotation3, Vector3}; + /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); + /// let inv = rot.inverse(); + /// assert_relative_eq!(rot * inv, Rotation3::identity(), epsilon = 1.0e-6); + /// assert_relative_eq!(inv * rot, Rotation3::identity(), epsilon = 1.0e-6); + /// + /// let rot = Rotation2::new(1.2); + /// let inv = rot.inverse(); + /// assert_relative_eq!(rot * inv, Rotation2::identity(), epsilon = 1.0e-6); + /// assert_relative_eq!(inv * rot, Rotation2::identity(), epsilon = 1.0e-6); + /// ``` #[inline] pub fn inverse(&self) -> Rotation { self.transpose() } /// Transposes `self` in-place. + /// + /// Same as `.inverse_mut()` because the inverse of a rotation matrix is its transform. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation2, Rotation3, Vector3}; + /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); + /// let mut tr_rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); + /// tr_rot.transpose_mut(); + /// + /// assert_relative_eq!(rot * tr_rot, Rotation3::identity(), epsilon = 1.0e-6); + /// assert_relative_eq!(tr_rot * rot, Rotation3::identity(), epsilon = 1.0e-6); + /// + /// let rot = Rotation2::new(1.2); + /// let mut tr_rot = Rotation2::new(1.2); + /// tr_rot.transpose_mut(); + /// + /// assert_relative_eq!(rot * tr_rot, Rotation2::identity(), epsilon = 1.0e-6); + /// assert_relative_eq!(tr_rot * rot, Rotation2::identity(), epsilon = 1.0e-6); + /// ``` #[inline] pub fn transpose_mut(&mut self) { self.matrix.transpose_mut() } /// Inverts `self` in-place. + /// + /// Same as `.transpose_mut()` because the inverse of a rotation matrix is its transform. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation2, Rotation3, Vector3}; + /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); + /// let mut inv = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); + /// inv.inverse_mut(); + /// + /// assert_relative_eq!(rot * inv, Rotation3::identity(), epsilon = 1.0e-6); + /// assert_relative_eq!(inv * rot, Rotation3::identity(), epsilon = 1.0e-6); + /// + /// let rot = Rotation2::new(1.2); + /// let mut inv = Rotation2::new(1.2); + /// inv.inverse_mut(); + /// + /// assert_relative_eq!(rot * inv, Rotation2::identity(), epsilon = 1.0e-6); + /// assert_relative_eq!(inv * rot, Rotation2::identity(), epsilon = 1.0e-6); + /// ``` #[inline] pub fn inverse_mut(&mut self) { self.transpose_mut() diff --git a/src/geometry/rotation_alga.rs b/src/geometry/rotation_alga.rs index 4f02bbae..b3bf7477 100644 --- a/src/geometry/rotation_alga.rs +++ b/src/geometry/rotation_alga.rs @@ -89,7 +89,7 @@ where DefaultAllocator: Allocator + Allocator { #[inline] fn inverse_transform_point(&self, pt: &Point) -> Point { - Point::from_coordinates(self.inverse_transform_vector(&pt.coords)) + Point::from(self.inverse_transform_vector(&pt.coords)) } #[inline] diff --git a/src/geometry/rotation_construction.rs b/src/geometry/rotation_construction.rs index 54cfc3c7..0da2cb29 100644 --- a/src/geometry/rotation_construction.rs +++ b/src/geometry/rotation_construction.rs @@ -14,6 +14,16 @@ where DefaultAllocator: Allocator, { /// Creates a new square identity rotation of the given `dimension`. + /// + /// # Example + /// ``` + /// # use nalgebra::Quaternion; + /// let rot1 = Quaternion::identity(); + /// let rot2 = Quaternion::new(1.0, 2.0, 3.0, 4.0); + /// + /// assert_eq!(rot1 * rot2, rot2); + /// assert_eq!(rot2 * rot1, rot2); + /// ``` #[inline] pub fn identity() -> Rotation { Self::from_matrix_unchecked(MatrixN::::identity()) diff --git a/src/geometry/rotation_conversion.rs b/src/geometry/rotation_conversion.rs index ecabb71f..3b574041 100644 --- a/src/geometry/rotation_conversion.rs +++ b/src/geometry/rotation_conversion.rs @@ -227,7 +227,7 @@ impl From> for Matrix3 { impl From> for Matrix2 { #[inline] fn from(q: Rotation2) -> Matrix2 { - q.unwrap() + q.into_inner() } } @@ -241,6 +241,6 @@ impl From> for Matrix4 { impl From> for Matrix3 { #[inline] fn from(q: Rotation3) -> Matrix3 { - q.unwrap() + q.into_inner() } } diff --git a/src/geometry/rotation_ops.rs b/src/geometry/rotation_ops.rs index 344d6b3f..fae70921 100644 --- a/src/geometry/rotation_ops.rs +++ b/src/geometry/rotation_ops.rs @@ -46,9 +46,9 @@ md_impl_all!( Mul, mul; (D, D), (D, D) for D: DimName; self: Rotation, right: Rotation, Output = Rotation; - [val val] => Rotation::from_matrix_unchecked(self.unwrap() * right.unwrap()); - [ref val] => Rotation::from_matrix_unchecked(self.matrix() * right.unwrap()); - [val ref] => Rotation::from_matrix_unchecked(self.unwrap() * right.matrix()); + [val val] => Rotation::from_matrix_unchecked(self.into_inner() * right.into_inner()); + [ref val] => Rotation::from_matrix_unchecked(self.matrix() * right.into_inner()); + [val ref] => Rotation::from_matrix_unchecked(self.into_inner() * right.matrix()); [ref ref] => Rotation::from_matrix_unchecked(self.matrix() * right.matrix()); ); @@ -71,9 +71,9 @@ md_impl_all!( where DefaultAllocator: Allocator where ShapeConstraint: AreMultipliable; self: Rotation, right: Matrix, Output = MatrixMN; - [val val] => self.unwrap() * right; + [val val] => self.into_inner() * right; [ref val] => self.matrix() * right; - [val ref] => self.unwrap() * right; + [val ref] => self.into_inner() * right; [ref ref] => self.matrix() * right; ); @@ -84,8 +84,8 @@ md_impl_all!( where DefaultAllocator: Allocator where ShapeConstraint: AreMultipliable; self: Matrix, right: Rotation, Output = MatrixMN; - [val val] => self * right.unwrap(); - [ref val] => self * right.unwrap(); + [val val] => self * right.into_inner(); + [ref val] => self * right.into_inner(); [val ref] => self * right.matrix(); [ref ref] => self * right.matrix(); ); @@ -112,9 +112,9 @@ md_impl_all!( where DefaultAllocator: Allocator where ShapeConstraint: AreMultipliable; self: Rotation, right: Point, Output = Point; - [val val] => self.unwrap() * right; + [val val] => self.into_inner() * right; [ref val] => self.matrix() * right; - [val ref] => self.unwrap() * right; + [val ref] => self.into_inner() * right; [ref ref] => self.matrix() * right; ); @@ -125,9 +125,9 @@ md_impl_all!( where DefaultAllocator: Allocator where ShapeConstraint: AreMultipliable; self: Rotation, right: Unit>, Output = Unit>; - [val val] => Unit::new_unchecked(self.unwrap() * right.unwrap()); - [ref val] => Unit::new_unchecked(self.matrix() * right.unwrap()); - [val ref] => Unit::new_unchecked(self.unwrap() * right.as_ref()); + [val val] => Unit::new_unchecked(self.into_inner() * right.into_inner()); + [ref val] => Unit::new_unchecked(self.matrix() * right.into_inner()); + [val ref] => Unit::new_unchecked(self.into_inner() * right.as_ref()); [ref ref] => Unit::new_unchecked(self.matrix() * right.as_ref()); ); @@ -138,16 +138,16 @@ md_assign_impl_all!( MulAssign, mul_assign; (D, D), (D, D) for D: DimName; self: Rotation, right: Rotation; - [val] => unsafe { self.matrix_mut().mul_assign(right.unwrap()) }; - [ref] => unsafe { self.matrix_mut().mul_assign(right.matrix()) }; + [val] => self.matrix_mut_unchecked().mul_assign(right.into_inner()); + [ref] => self.matrix_mut_unchecked().mul_assign(right.matrix()); ); md_assign_impl_all!( DivAssign, div_assign; (D, D), (D, D) for D: DimName; self: Rotation, right: Rotation; - [val] => unsafe { self.matrix_mut().mul_assign(right.inverse().unwrap()) }; - [ref] => unsafe { self.matrix_mut().mul_assign(right.inverse().matrix()) }; + [val] => self.matrix_mut_unchecked().mul_assign(right.inverse().into_inner()); + [ref] => self.matrix_mut_unchecked().mul_assign(right.inverse().matrix()); ); // Matrix *= Rotation @@ -160,7 +160,7 @@ md_assign_impl_all!( MulAssign, mul_assign; (R1, C1), (C1, C1) for R1: DimName, C1: DimName; self: MatrixMN, right: Rotation; - [val] => self.mul_assign(right.unwrap()); + [val] => self.mul_assign(right.into_inner()); [ref] => self.mul_assign(right.matrix()); ); @@ -168,6 +168,6 @@ md_assign_impl_all!( DivAssign, div_assign; (R1, C1), (C1, C1) for R1: DimName, C1: DimName; self: MatrixMN, right: Rotation; - [val] => self.mul_assign(right.inverse().unwrap()); + [val] => self.mul_assign(right.inverse().into_inner()); [ref] => self.mul_assign(right.inverse().matrix()); ); diff --git a/src/geometry/rotation_specialization.rs b/src/geometry/rotation_specialization.rs index e03cd97d..35ae1ea2 100644 --- a/src/geometry/rotation_specialization.rs +++ b/src/geometry/rotation_specialization.rs @@ -22,6 +22,17 @@ use geometry::{Rotation2, Rotation3, UnitComplex}; */ impl Rotation2 { /// Builds a 2 dimensional rotation matrix from an angle in radian. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Rotation2, Point2}; + /// let rot = Rotation2::new(f32::consts::FRAC_PI_2); + /// + /// assert_relative_eq!(rot * Point2::new(3.0, 4.0), Point2::new(-4.0, 3.0)); + /// ``` pub fn new(angle: N) -> Self { let (sia, coa) = angle.sin_cos(); Self::from_matrix_unchecked(MatrixN::::new(coa, -sia, sia, coa)) @@ -29,7 +40,9 @@ impl Rotation2 { /// Builds a 2 dimensional rotation matrix from an angle in radian wrapped in a 1-dimensional vector. /// - /// Equivalent to `Self::new(axisangle[0])`. + /// + /// This is generally used in the context of generic programming. Using + /// the `::new(angle)` method instead is more common. #[inline] pub fn from_scaled_axis>(axisangle: Vector) -> Self { Self::new(axisangle[0]) @@ -38,6 +51,17 @@ impl Rotation2 { /// The rotation matrix required to align `a` and `b` but with its angle. /// /// This is the rotation `R` such that `(R * a).angle(b) == 0 && (R * a).dot(b).is_positive()`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Vector2, Rotation2}; + /// let a = Vector2::new(1.0, 2.0); + /// let b = Vector2::new(2.0, 1.0); + /// let rot = Rotation2::rotation_between(&a, &b); + /// assert_relative_eq!(rot * a, b); + /// assert_relative_eq!(rot.inverse() * b, a); + /// ``` #[inline] pub fn rotation_between(a: &Vector, b: &Vector) -> Self where @@ -49,6 +73,18 @@ impl Rotation2 { /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Vector2, Rotation2}; + /// let a = Vector2::new(1.0, 2.0); + /// let b = Vector2::new(2.0, 1.0); + /// let rot2 = Rotation2::scaled_rotation_between(&a, &b, 0.2); + /// let rot5 = Rotation2::scaled_rotation_between(&a, &b, 0.5); + /// assert_relative_eq!(rot2 * rot2 * rot2 * rot2 * rot2 * a, b, epsilon = 1.0e-6); + /// assert_relative_eq!(rot5 * rot5 * a, b, epsilon = 1.0e-6); + /// ``` #[inline] pub fn scaled_rotation_between( a: &Vector, @@ -65,12 +101,29 @@ impl Rotation2 { impl Rotation2 { /// The rotation angle. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Rotation2; + /// let rot = Rotation2::new(1.78); + /// assert_relative_eq!(rot.angle(), 1.78); + /// ``` #[inline] pub fn angle(&self) -> N { self.matrix()[(1, 0)].atan2(self.matrix()[(0, 0)]) } /// The rotation angle needed to make `self` and `other` coincide. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Rotation2; + /// let rot1 = Rotation2::new(0.1); + /// let rot2 = Rotation2::new(1.7); + /// assert_relative_eq!(rot1.angle_to(&rot2), 1.6); + /// ``` #[inline] pub fn angle_to(&self, other: &Rotation2) -> N { self.rotation_to(other).angle() @@ -79,6 +132,18 @@ impl Rotation2 { /// The rotation matrix needed to make `self` and `other` coincide. /// /// The result is such that: `self.rotation_to(other) * self == other`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Rotation2; + /// let rot1 = Rotation2::new(0.1); + /// let rot2 = Rotation2::new(1.7); + /// let rot_to = rot1.rotation_to(&rot2); + /// + /// assert_relative_eq!(rot_to * rot1, rot2); + /// assert_relative_eq!(rot_to.inverse() * rot2, rot1); + /// ``` #[inline] pub fn rotation_to(&self, other: &Rotation2) -> Rotation2 { other * self.inverse() @@ -86,12 +151,24 @@ impl Rotation2 { /// Raise the quaternion to a given floating power, i.e., returns the rotation with the angle /// of `self` multiplied by `n`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Rotation2; + /// let rot = Rotation2::new(0.78); + /// let pow = rot.powf(2.0); + /// assert_relative_eq!(pow.angle(), 2.0 * 0.78); + /// ``` #[inline] pub fn powf(&self, n: N) -> Rotation2 { Self::new(self.angle() * n) } /// The rotation angle returned as a 1-dimensional vector. + /// + /// This is generally used in the context of generic programming. Using + /// the `.angle()` method instead is more common. #[inline] pub fn scaled_axis(&self) -> VectorN { Vector1::new(self.angle()) @@ -129,6 +206,24 @@ impl Rotation3 { /// # Arguments /// * `axisangle` - A vector representing the rotation. Its magnitude is the amount of rotation /// in radian. Its direction is the axis of rotation. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Rotation3, Point3, Vector3}; + /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// let rot = Rotation3::new(axisangle); + /// + /// assert_relative_eq!(rot * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// assert_relative_eq!(rot * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// + /// // A zero vector yields an identity. + /// assert_eq!(Rotation3::new(Vector3::::zeros()), Rotation3::identity()); + /// ``` pub fn new>(axisangle: Vector) -> Self { let axisangle = axisangle.into_owned(); let (axis, angle) = Unit::new_and_get(axisangle); @@ -136,11 +231,52 @@ impl Rotation3 { } /// Builds a 3D rotation matrix from an axis scaled by the rotation angle. + /// + /// This is the same as `Self::new(axisangle)`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Rotation3, Point3, Vector3}; + /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// let rot = Rotation3::new(axisangle); + /// + /// assert_relative_eq!(rot * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// assert_relative_eq!(rot * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// + /// // A zero vector yields an identity. + /// assert_eq!(Rotation3::from_scaled_axis(Vector3::::zeros()), Rotation3::identity()); + /// ``` pub fn from_scaled_axis>(axisangle: Vector) -> Self { Self::new(axisangle) } /// Builds a 3D rotation matrix from an axis and a rotation angle. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Rotation3, Point3, Vector3}; + /// let axis = Vector3::y_axis(); + /// let angle = f32::consts::FRAC_PI_2; + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// let rot = Rotation3::from_axis_angle(&axis, angle); + /// + /// assert_eq!(rot.axis().unwrap(), axis); + /// assert_eq!(rot.angle(), angle); + /// assert_relative_eq!(rot * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// assert_relative_eq!(rot * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); + /// + /// // A zero vector yields an identity. + /// assert_eq!(Rotation3::from_scaled_axis(Vector3::::zeros()), Rotation3::identity()); + /// ``` pub fn from_axis_angle(axis: &Unit>, angle: N) -> Self where SB: Storage { if angle.is_zero() { @@ -172,6 +308,17 @@ impl Rotation3 { /// Creates a new rotation from Euler angles. /// /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Rotation3; + /// let rot = Rotation3::from_euler_angles(0.1, 0.2, 0.3); + /// let euler = rot.euler_angles(); + /// assert_relative_eq!(euler.0, 0.1, epsilon = 1.0e-6); + /// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6); + /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); + /// ``` pub fn from_euler_angles(roll: N, pitch: N, yaw: N) -> Self { let (sr, cr) = roll.sin_cos(); let (sp, cp) = pitch.sin_cos(); @@ -192,16 +339,35 @@ impl Rotation3 { /// Creates Euler angles from a rotation. /// - /// The angles are produced in the form (roll, yaw, pitch). + /// The angles are produced in the form (roll, pitch, yaw). + #[deprecated(note = "This is renamed to use `.euler_angles()`.")] pub fn to_euler_angles(&self) -> (N, N, N) { + self.euler_angles() + } + + /// Euler angles corresponding to this rotation from a rotation. + /// + /// The angles are produced in the form (roll, pitch, yaw). + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Rotation3; + /// let rot = Rotation3::from_euler_angles(0.1, 0.2, 0.3); + /// let euler = rot.euler_angles(); + /// assert_relative_eq!(euler.0, 0.1, epsilon = 1.0e-6); + /// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6); + /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); + /// ``` + pub fn euler_angles(&self) -> (N, N, N) { // Implementation informed by "Computing Euler angles from a rotation matrix", by Gregory G. Slabaugh // http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.371.6578 - if self[(2, 0)].abs() != N::one() { + if self[(2, 0)].abs() < N::one() { let yaw = -self[(2, 0)].asin(); let roll = (self[(2, 1)] / yaw.cos()).atan2(self[(2, 2)] / yaw.cos()); let pitch = (self[(1, 0)] / yaw.cos()).atan2(self[(0, 0)] / yaw.cos()); (roll, yaw, pitch) - } else if self[(2, 0)] == -N::one() { + } else if self[(2, 0)] <= -N::one() { (self[(0, 1)].atan2(self[(0, 2)]), N::frac_pi_2(), N::zero()) } else { ( @@ -215,15 +381,26 @@ impl Rotation3 { /// Creates a rotation that corresponds to the local frame of an observer standing at the /// origin and looking toward `dir`. /// - /// It maps the view direction `dir` to the positive `z` axis. + /// It maps the `z` axis to the direction `dir`. /// /// # Arguments /// * dir - The look direction, that is, direction the matrix `z` axis will be aligned with. /// * up - The vertical direction. The only requirement of this parameter is to not be - /// collinear - /// to `dir`. Non-collinearity is not checked. + /// collinear to `dir`. Non-collinearity is not checked. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Rotation3, Vector3}; + /// let dir = Vector3::new(1.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// let rot = Rotation3::face_towards(&dir, &up); + /// assert_relative_eq!(rot * Vector3::z(), dir.normalize()); + /// ``` #[inline] - pub fn new_observer_frame(dir: &Vector, up: &Vector) -> Self + pub fn face_towards(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, @@ -237,47 +414,92 @@ impl Rotation3 { )) } + /// Deprecated: Use [Rotation3::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frames(dir: &Vector, up: &Vector) -> Self + where + SB: Storage, + SC: Storage, + { + Self::face_towards(dir, up) + } + /// Builds a right-handed look-at view matrix without translation. /// + /// It maps the view direction `dir` to the **negative** `z` axis. /// This conforms to the common notion of right handed look-at matrix from the computer /// graphics community. /// /// # Arguments - /// * eye - The eye position. - /// * target - The target position. + /// * dir - The direction toward which the camera looks. /// * up - A vector approximately aligned with required the vertical axis. The only - /// requirement of this parameter is to not be collinear to `target - eye`. + /// requirement of this parameter is to not be collinear to `dir`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Rotation3, Vector3}; + /// let dir = Vector3::new(1.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// let rot = Rotation3::look_at_rh(&dir, &up); + /// assert_relative_eq!(rot * dir.normalize(), -Vector3::z()); + /// ``` #[inline] pub fn look_at_rh(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { - Self::new_observer_frame(&dir.neg(), up).inverse() + Self::face_towards(&dir.neg(), up).inverse() } /// Builds a left-handed look-at view matrix without translation. /// + /// It maps the view direction `dir` to the **positive** `z` axis. /// This conforms to the common notion of left handed look-at matrix from the computer /// graphics community. /// /// # Arguments - /// * eye - The eye position. - /// * target - The target position. + /// * dir - The direction toward which the camera looks. /// * up - A vector approximately aligned with required the vertical axis. The only - /// requirement of this parameter is to not be collinear to `target - eye`. + /// requirement of this parameter is to not be collinear to `dir`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Rotation3, Vector3}; + /// let dir = Vector3::new(1.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// let rot = Rotation3::look_at_lh(&dir, &up); + /// assert_relative_eq!(rot * dir.normalize(), Vector3::z()); + /// ``` #[inline] pub fn look_at_lh(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { - Self::new_observer_frame(dir, up).inverse() + Self::face_towards(dir, up).inverse() } /// The rotation matrix required to align `a` and `b` but with its angle. /// /// This is the rotation `R` such that `(R * a).angle(b) == 0 && (R * a).dot(b).is_positive()`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Vector3, Rotation3}; + /// let a = Vector3::new(1.0, 2.0, 3.0); + /// let b = Vector3::new(3.0, 1.0, 2.0); + /// let rot = Rotation3::rotation_between(&a, &b).unwrap(); + /// assert_relative_eq!(rot * a, b, epsilon = 1.0e-6); + /// assert_relative_eq!(rot.inverse() * b, a, epsilon = 1.0e-6); + /// ``` #[inline] pub fn rotation_between(a: &Vector, b: &Vector) -> Option where @@ -289,6 +511,18 @@ impl Rotation3 { /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Vector3, Rotation3}; + /// let a = Vector3::new(1.0, 2.0, 3.0); + /// let b = Vector3::new(3.0, 1.0, 2.0); + /// let rot2 = Rotation3::scaled_rotation_between(&a, &b, 0.2).unwrap(); + /// let rot5 = Rotation3::scaled_rotation_between(&a, &b, 0.5).unwrap(); + /// assert_relative_eq!(rot2 * rot2 * rot2 * rot2 * rot2 * a, b, epsilon = 1.0e-6); + /// assert_relative_eq!(rot5 * rot5 * a, b, epsilon = 1.0e-6); + /// ``` #[inline] pub fn scaled_rotation_between( a: &Vector, @@ -320,7 +554,16 @@ impl Rotation3 { Some(Self::identity()) } - /// The rotation angle. + /// The rotation angle in [0; pi]. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Unit, Rotation3, Vector3}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let rot = Rotation3::from_axis_angle(&axis, 1.78); + /// assert_relative_eq!(rot.angle(), 1.78); + /// ``` #[inline] pub fn angle(&self) -> N { ((self.matrix()[(0, 0)] + self.matrix()[(1, 1)] + self.matrix()[(2, 2)] - N::one()) @@ -329,6 +572,20 @@ impl Rotation3 { } /// The rotation axis. Returns `None` if the rotation angle is zero or PI. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation3, Vector3, Unit}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let angle = 1.2; + /// let rot = Rotation3::from_axis_angle(&axis, angle); + /// assert_relative_eq!(rot.axis().unwrap(), axis); + /// + /// // Case with a zero angle. + /// let rot = Rotation3::from_axis_angle(&axis, 0.0); + /// assert!(rot.axis().is_none()); + /// ``` #[inline] pub fn axis(&self) -> Option>> { let axis = VectorN::::new( @@ -341,16 +598,62 @@ impl Rotation3 { } /// The rotation axis multiplied by the rotation angle. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation3, Vector3, Unit}; + /// let axisangle = Vector3::new(0.1, 0.2, 0.3); + /// let rot = Rotation3::new(axisangle); + /// assert_relative_eq!(rot.scaled_axis(), axisangle, epsilon = 1.0e-6); + /// ``` #[inline] pub fn scaled_axis(&self) -> Vector3 { if let Some(axis) = self.axis() { - axis.unwrap() * self.angle() + axis.into_inner() * self.angle() } else { Vector::zero() } } + /// The rotation axis and angle in ]0, pi] of this unit quaternion. + /// + /// Returns `None` if the angle is zero. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation3, Vector3, Unit}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let angle = 1.2; + /// let rot = Rotation3::from_axis_angle(&axis, angle); + /// let axis_angle = rot.axis_angle().unwrap(); + /// assert_relative_eq!(axis_angle.0, axis); + /// assert_relative_eq!(axis_angle.1, angle); + /// + /// // Case with a zero angle. + /// let rot = Rotation3::from_axis_angle(&axis, 0.0); + /// assert!(rot.axis_angle().is_none()); + /// ``` + #[inline] + pub fn axis_angle(&self) -> Option<(Unit>, N)> { + if let Some(axis) = self.axis() { + Some((axis, self.angle())) + } else { + None + } + } + /// The rotation angle needed to make `self` and `other` coincide. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation3, Vector3}; + /// let rot1 = Rotation3::from_axis_angle(&Vector3::y_axis(), 1.0); + /// let rot2 = Rotation3::from_axis_angle(&Vector3::x_axis(), 0.1); + /// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6); + /// ``` #[inline] pub fn angle_to(&self, other: &Rotation3) -> N { self.rotation_to(other).angle() @@ -359,6 +662,16 @@ impl Rotation3 { /// The rotation matrix needed to make `self` and `other` coincide. /// /// The result is such that: `self.rotation_to(other) * self == other`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation3, Vector3}; + /// let rot1 = Rotation3::from_axis_angle(&Vector3::y_axis(), 1.0); + /// let rot2 = Rotation3::from_axis_angle(&Vector3::x_axis(), 0.1); + /// let rot_to = rot1.rotation_to(&rot2); + /// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6); + /// ``` #[inline] pub fn rotation_to(&self, other: &Rotation3) -> Rotation3 { other * self.inverse() @@ -366,6 +679,18 @@ impl Rotation3 { /// Raise the quaternion to a given floating power, i.e., returns the rotation with the same /// axis as `self` and an angle equal to `self.angle()` multiplied by `n`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Rotation3, Vector3, Unit}; + /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); + /// let angle = 1.2; + /// let rot = Rotation3::from_axis_angle(&axis, angle); + /// let pow = rot.powf(2.0); + /// assert_relative_eq!(pow.axis().unwrap(), axis, epsilon = 1.0e-6); + /// assert_eq!(pow.angle(), 2.4); + /// ``` #[inline] pub fn powf(&self, n: N) -> Rotation3 { if let Some(axis) = self.axis() { diff --git a/src/geometry/similarity.rs b/src/geometry/similarity.rs index 6592e6ed..f321d575 100644 --- a/src/geometry/similarity.rs +++ b/src/geometry/similarity.rs @@ -25,17 +25,21 @@ use geometry::{Isometry, Point, Translation}; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "N: Serialize, + serde(bound( + serialize = "N: Serialize, R: Serialize, DefaultAllocator: Allocator, - Owned: Serialize")) + Owned: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "N: Deserialize<'de>, + serde(bound( + deserialize = "N: Deserialize<'de>, R: Deserialize<'de>, DefaultAllocator: Allocator, - Owned: Deserialize<'de>")) + Owned: Deserialize<'de>" + )) )] pub struct Similarity where DefaultAllocator: Allocator @@ -80,8 +84,7 @@ impl> + Copy> Copy for Simil where DefaultAllocator: Allocator, Owned: Copy, -{ -} +{} impl> + Clone> Clone for Similarity where DefaultAllocator: Allocator @@ -181,7 +184,7 @@ where ); Self::from_parts( - Translation::from_vector(&self.isometry.translation.vector * scaling), + Translation::from(&self.isometry.translation.vector * scaling), self.isometry.rotation.clone(), self.scaling * scaling, ) @@ -266,8 +269,7 @@ impl Eq for Similarity where R: Rotation> + Eq, DefaultAllocator: Allocator, -{ -} +{} impl PartialEq for Similarity where diff --git a/src/geometry/similarity_construction.rs b/src/geometry/similarity_construction.rs index 99e18407..47bb46c7 100644 --- a/src/geometry/similarity_construction.rs +++ b/src/geometry/similarity_construction.rs @@ -25,6 +25,20 @@ where DefaultAllocator: Allocator, { /// Creates a new identity similarity. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Similarity2, Point2, Similarity3, Point3}; + /// + /// let sim = Similarity2::identity(); + /// let pt = Point2::new(1.0, 2.0); + /// assert_eq!(sim * pt, pt); + /// + /// let sim = Similarity3::identity(); + /// let pt = Point3::new(1.0, 2.0, 3.0); + /// assert_eq!(sim * pt, pt); + /// ``` #[inline] pub fn identity() -> Self { Self::from_isometry(Isometry::identity(), N::one()) @@ -67,10 +81,23 @@ where { /// The similarity that applies the scaling factor `scaling`, followed by the rotation `r` with /// its axis passing through the point `p`. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Similarity2, Point2, UnitComplex}; + /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); + /// let pt = Point2::new(3.0, 2.0); + /// let sim = Similarity2::rotation_wrt_point(rot, pt, 4.0); + /// + /// assert_relative_eq!(sim * Point2::new(1.0, 2.0), Point2::new(-3.0, 3.0), epsilon = 1.0e-6); + /// ``` #[inline] pub fn rotation_wrt_point(r: R, p: Point, scaling: N) -> Self { let shift = r.transform_vector(&-&p.coords); - Self::from_parts(Translation::from_vector(shift + p.coords), r, scaling) + Self::from_parts(Translation::from(shift + p.coords), r, scaling) } } @@ -101,11 +128,22 @@ where // 2D rotation. impl Similarity> { - /// Creates a new similarity from a translation and a rotation angle. + /// Creates a new similarity from a translation, a rotation, and an uniform scaling factor. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{SimilarityMatrix2, Vector2, Point2}; + /// let sim = SimilarityMatrix2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2, 3.0); + /// + /// assert_relative_eq!(sim * Point2::new(2.0, 4.0), Point2::new(-11.0, 8.0), epsilon = 1.0e-6); + /// ``` #[inline] pub fn new(translation: Vector2, angle: N, scaling: N) -> Self { Self::from_parts( - Translation::from_vector(translation), + Translation::from(translation), Rotation2::new(angle), scaling, ) @@ -114,10 +152,21 @@ impl Similarity> { impl Similarity> { /// Creates a new similarity from a translation and a rotation angle. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Similarity2, Vector2, Point2}; + /// let sim = Similarity2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2, 3.0); + /// + /// assert_relative_eq!(sim * Point2::new(2.0, 4.0), Point2::new(-11.0, 8.0), epsilon = 1.0e-6); + /// ``` #[inline] pub fn new(translation: Vector2, angle: N, scaling: N) -> Self { Self::from_parts( - Translation::from_vector(translation), + Translation::from(translation), UnitComplex::new(angle), scaling, ) @@ -130,6 +179,29 @@ macro_rules! similarity_construction_impl( impl Similarity { /// Creates a new similarity from a translation, rotation axis-angle, and scaling /// factor. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; + /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; + /// let translation = Vector3::new(1.0, 2.0, 3.0); + /// // Point and vector being transformed in the tests. + /// let pt = Point3::new(4.0, 5.0, 6.0); + /// let vec = Vector3::new(4.0, 5.0, 6.0); + /// + /// // Similarity with its rotation part represented as a UnitQuaternion + /// let sim = Similarity3::new(translation, axisangle, 3.0); + /// assert_relative_eq!(sim * pt, Point3::new(19.0, 17.0, -9.0), epsilon = 1.0e-5); + /// assert_relative_eq!(sim * vec, Vector3::new(18.0, 15.0, -12.0), epsilon = 1.0e-5); + /// + /// // Similarity with its rotation part represented as a Rotation3 (a 3x3 rotation matrix). + /// let sim = SimilarityMatrix3::new(translation, axisangle, 3.0); + /// assert_relative_eq!(sim * pt, Point3::new(19.0, 17.0, -9.0), epsilon = 1.0e-5); + /// assert_relative_eq!(sim * vec, Vector3::new(18.0, 15.0, -12.0), epsilon = 1.0e-5); + /// ``` #[inline] pub fn new(translation: Vector3, axisangle: Vector3, scaling: N) -> Self { Self::from_isometry(Isometry::<_, U3, $Rot>::new(translation, axisangle), scaling) @@ -146,13 +218,44 @@ macro_rules! similarity_construction_impl( /// * target - The target position. /// * up - Vertical direction. The only requirement of this parameter is to not be collinear /// to `eye - at`. Non-collinearity is not checked. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; + /// let eye = Point3::new(1.0, 2.0, 3.0); + /// let target = Point3::new(2.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// // Similarity with its rotation part represented as a UnitQuaternion + /// let sim = Similarity3::face_towards(&eye, &target, &up, 3.0); + /// assert_eq!(sim * Point3::origin(), eye); + /// assert_relative_eq!(sim * Vector3::z(), Vector3::x() * 3.0, epsilon = 1.0e-6); + /// + /// // Similarity with its rotation part represented as Rotation3 (a 3x3 rotation matrix). + /// let sim = SimilarityMatrix3::face_towards(&eye, &target, &up, 3.0); + /// assert_eq!(sim * Point3::origin(), eye); + /// assert_relative_eq!(sim * Vector3::z(), Vector3::x() * 3.0, epsilon = 1.0e-6); + /// ``` #[inline] - pub fn new_observer_frame(eye: &Point3, + pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3, scaling: N) -> Self { - Self::from_isometry(Isometry::<_, U3, $Rot>::new_observer_frame(eye, target, up), scaling) + Self::from_isometry(Isometry::<_, U3, $Rot>::face_towards(eye, target, up), scaling) + } + + /// Deprecated: Use [SimilarityMatrix3::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frames(eye: &Point3, + target: &Point3, + up: &Vector3, + scaling: N) + -> Self { + Self::face_towards(eye, target, up, scaling) } /// Builds a right-handed look-at view matrix including scaling factor. @@ -165,6 +268,25 @@ macro_rules! similarity_construction_impl( /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; + /// let eye = Point3::new(1.0, 2.0, 3.0); + /// let target = Point3::new(2.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// // Similarity with its rotation part represented as a UnitQuaternion + /// let iso = Similarity3::look_at_rh(&eye, &target, &up, 3.0); + /// assert_relative_eq!(iso * Vector3::x(), -Vector3::z() * 3.0, epsilon = 1.0e-6); + /// + /// // Similarity with its rotation part represented as Rotation3 (a 3x3 rotation matrix). + /// let iso = SimilarityMatrix3::look_at_rh(&eye, &target, &up, 3.0); + /// assert_relative_eq!(iso * Vector3::x(), -Vector3::z() * 3.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn look_at_rh(eye: &Point3, target: &Point3, @@ -184,6 +306,25 @@ macro_rules! similarity_construction_impl( /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; + /// let eye = Point3::new(1.0, 2.0, 3.0); + /// let target = Point3::new(2.0, 2.0, 3.0); + /// let up = Vector3::y(); + /// + /// // Similarity with its rotation part represented as a UnitQuaternion + /// let sim = Similarity3::look_at_lh(&eye, &target, &up, 3.0); + /// assert_relative_eq!(sim * Vector3::x(), Vector3::z() * 3.0, epsilon = 1.0e-6); + /// + /// // Similarity with its rotation part represented as Rotation3 (a 3x3 rotation matrix). + /// let sim = SimilarityMatrix3::look_at_lh(&eye, &target, &up, 3.0); + /// assert_relative_eq!(sim * Vector3::x(), Vector3::z() * 3.0, epsilon = 1.0e-6); + /// ``` #[inline] pub fn look_at_lh(eye: &Point3, target: &Point3, diff --git a/src/geometry/similarity_conversion.rs b/src/geometry/similarity_conversion.rs index 8da75f38..34943fee 100644 --- a/src/geometry/similarity_conversion.rs +++ b/src/geometry/similarity_conversion.rs @@ -155,7 +155,9 @@ where } let t = m.fixed_slice::(0, D::dim()).into_owned(); - let t = Translation::from_vector(::convert_unchecked(t)); + let t = Translation { + vector: ::convert_unchecked(t), + }; Self::from_parts(t, ::convert_unchecked(mm), ::convert_unchecked(scale)) } diff --git a/src/geometry/similarity_ops.rs b/src/geometry/similarity_ops.rs index 7469c4cd..57fdc05f 100644 --- a/src/geometry/similarity_ops.rs +++ b/src/geometry/similarity_ops.rs @@ -269,7 +269,7 @@ similarity_binop_impl_all!( [ref ref] => { let shift = self.isometry.rotation.transform_vector(&rhs.translation.vector) * self.scaling(); Similarity::from_parts( - Translation::from_vector(&self.isometry.translation.vector + shift), + Translation::from(&self.isometry.translation.vector + shift), self.isometry.rotation.clone() * rhs.rotation.clone(), self.scaling()) }; @@ -352,7 +352,7 @@ similarity_binop_impl_all!( [ref ref] => { let shift = self.isometry.rotation.transform_vector(&right.vector) * self.scaling(); Similarity::from_parts( - Translation::from_vector(&self.isometry.translation.vector + shift), + Translation::from(&self.isometry.translation.vector + shift), self.isometry.rotation.clone(), self.scaling()) }; diff --git a/src/geometry/swizzle.rs b/src/geometry/swizzle.rs new file mode 100644 index 00000000..20c8dffd --- /dev/null +++ b/src/geometry/swizzle.rs @@ -0,0 +1,65 @@ +use base::allocator::Allocator; +use base::{DefaultAllocator, DimName, Scalar}; +use geometry::{Point, Point2, Point3}; +use typenum::{self, Cmp, Greater}; + +macro_rules! impl_swizzle { + ($( where $BaseDim: ident: $( $name: ident() -> $Result: ident[$($i: expr),+] ),+ ;)* ) => { + $( + impl Point + where + DefaultAllocator: Allocator, + D::Value: Cmp + { + $( + /// Builds a new point from components of `self`. + #[inline] + pub fn $name(&self) -> $Result { + $Result::new($(self[$i]),*) + } + )* + } + )* + } +} + +impl_swizzle!( + where U0: xx() -> Point2[0, 0], + xxx() -> Point3[0, 0, 0]; + + where U1: xy() -> Point2[0, 1], + yx() -> Point2[1, 0], + yy() -> Point2[1, 1], + xxy() -> Point3[0, 0, 1], + xyx() -> Point3[0, 1, 0], + xyy() -> Point3[0, 1, 1], + yxx() -> Point3[1, 0, 0], + yxy() -> Point3[1, 0, 1], + yyx() -> Point3[1, 1, 0], + yyy() -> Point3[1, 1, 1]; + + where U2: xz() -> Point2[0, 2], + yz() -> Point2[1, 2], + zx() -> Point2[2, 0], + zy() -> Point2[2, 1], + zz() -> Point2[2, 2], + xxz() -> Point3[0, 0, 2], + xyz() -> Point3[0, 1, 2], + xzx() -> Point3[0, 2, 0], + xzy() -> Point3[0, 2, 1], + xzz() -> Point3[0, 2, 2], + yxz() -> Point3[1, 0, 2], + yyz() -> Point3[1, 1, 2], + yzx() -> Point3[1, 2, 0], + yzy() -> Point3[1, 2, 1], + yzz() -> Point3[1, 2, 2], + zxx() -> Point3[2, 0, 0], + zxy() -> Point3[2, 0, 1], + zxz() -> Point3[2, 0, 2], + zyx() -> Point3[2, 1, 0], + zyy() -> Point3[2, 1, 1], + zyz() -> Point3[2, 1, 2], + zzx() -> Point3[2, 2, 0], + zzy() -> Point3[2, 2, 1], + zzz() -> Point3[2, 2, 2]; +); diff --git a/src/geometry/transform.rs b/src/geometry/transform.rs index 4326c915..0d5d9c4a 100644 --- a/src/geometry/transform.rs +++ b/src/geometry/transform.rs @@ -1,3 +1,4 @@ +use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use std::any::Any; use std::fmt::Debug; use std::marker::PhantomData; @@ -14,7 +15,7 @@ use base::{DefaultAllocator, MatrixN}; /// Trait implemented by phantom types identifying the projective transformation type. /// -/// NOTE: this trait is not intended to be implementable outside of the `nalgebra` crate. +/// NOTE: this trait is not intended to be implemented outside of the `nalgebra` crate. pub trait TCategory: Any + Debug + Copy + PartialEq + Send { /// Indicates whether a `Transform` with the category `Self` has a bottom-row different from /// `0 0 .. 1`. @@ -237,13 +238,43 @@ where DefaultAllocator: Allocator, DimNameSum> } } - /// The underlying matrix. + /// Retrieves the underlying matrix. + /// + /// # Examples + /// ``` + /// # use nalgebra::{Matrix3, Transform2}; + /// + /// let m = Matrix3::new(1.0, 2.0, 0.0, + /// 3.0, 4.0, 0.0, + /// 0.0, 0.0, 1.0); + /// let t = Transform2::from_matrix_unchecked(m); + /// assert_eq!(t.into_inner(), m); + /// ``` + #[inline] + pub fn into_inner(self) -> MatrixN> { + self.matrix + } + + /// Retrieves the underlying matrix. + /// Deprecated: Use [Transform::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] #[inline] pub fn unwrap(self) -> MatrixN> { self.matrix } /// A reference to the underlying matrix. + /// + /// # Examples + /// ``` + /// # use nalgebra::{Matrix3, Transform2}; + /// + /// let m = Matrix3::new(1.0, 2.0, 0.0, + /// 3.0, 4.0, 0.0, + /// 0.0, 0.0, 1.0); + /// let t = Transform2::from_matrix_unchecked(m); + /// assert_eq!(*t.matrix(), m); + /// ``` #[inline] pub fn matrix(&self) -> &MatrixN> { &self.matrix @@ -253,6 +284,24 @@ where DefaultAllocator: Allocator, DimNameSum> /// /// It is `_unchecked` because direct modifications of this matrix may break invariants /// identified by this transformation category. + /// + /// # Examples + /// ``` + /// # use nalgebra::{Matrix3, Transform2}; + /// + /// let m = Matrix3::new(1.0, 2.0, 0.0, + /// 3.0, 4.0, 0.0, + /// 0.0, 0.0, 1.0); + /// let mut t = Transform2::from_matrix_unchecked(m); + /// t.matrix_mut_unchecked().m12 = 42.0; + /// t.matrix_mut_unchecked().m23 = 90.0; + /// + /// + /// let expected = Matrix3::new(1.0, 42.0, 0.0, + /// 3.0, 4.0, 90.0, + /// 0.0, 0.0, 1.0); + /// assert_eq!(*t.matrix(), expected); + /// ``` #[inline] pub fn matrix_mut_unchecked(&mut self) -> &mut MatrixN> { &mut self.matrix @@ -271,19 +320,53 @@ where DefaultAllocator: Allocator, DimNameSum> /// Clones this transform into one that owns its data. #[inline] - #[deprecated(note = "This method is a no-op and will be removed in a future release.")] + #[deprecated( + note = "This method is redundant with automatic `Copy` and the `.clone()` method and will be removed in a future release." + )] pub fn clone_owned(&self) -> Transform { Transform::from_matrix_unchecked(self.matrix.clone_owned()) } /// Converts this transform into its equivalent homogeneous transformation matrix. + /// + /// # Examples + /// ``` + /// # use nalgebra::{Matrix3, Transform2}; + /// + /// let m = Matrix3::new(1.0, 2.0, 0.0, + /// 3.0, 4.0, 0.0, + /// 0.0, 0.0, 1.0); + /// let t = Transform2::from_matrix_unchecked(m); + /// assert_eq!(t.into_inner(), m); + /// ``` #[inline] pub fn to_homogeneous(&self) -> MatrixN> { self.matrix().clone_owned() } /// Attempts to invert this transformation. You may use `.inverse` instead of this - /// transformation has a subcategory of `TProjective`. + /// transformation has a subcategory of `TProjective` (i.e. if it is a `Projective{2,3}` or `Affine{2,3}`). + /// + /// # Examples + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Matrix3, Transform2}; + /// + /// let m = Matrix3::new(2.0, 2.0, -0.3, + /// 3.0, 4.0, 0.1, + /// 0.0, 0.0, 1.0); + /// let t = Transform2::from_matrix_unchecked(m); + /// let inv_t = t.try_inverse().unwrap(); + /// assert_relative_eq!(t * inv_t, Transform2::identity()); + /// assert_relative_eq!(inv_t * t, Transform2::identity()); + /// + /// // Non-invertible case. + /// let m = Matrix3::new(0.0, 2.0, 1.0, + /// 3.0, 0.0, 5.0, + /// 0.0, 0.0, 0.0); + /// let t = Transform2::from_matrix_unchecked(m); + /// assert!(t.try_inverse().is_none()); + /// ``` #[inline] pub fn try_inverse(self) -> Option> { if let Some(m) = self.matrix.try_inverse() { @@ -294,7 +377,21 @@ where DefaultAllocator: Allocator, DimNameSum> } /// Inverts this transformation. Use `.try_inverse` if this transform has the `TGeneral` - /// category (it may not be invertible). + /// category (i.e., a `Transform{2,3}` may not be invertible). + /// + /// # Examples + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Matrix3, Projective2}; + /// + /// let m = Matrix3::new(2.0, 2.0, -0.3, + /// 3.0, 4.0, 0.1, + /// 0.0, 0.0, 1.0); + /// let proj = Projective2::from_matrix_unchecked(m); + /// let inv_t = proj.inverse(); + /// assert_relative_eq!(proj * inv_t, Projective2::identity()); + /// assert_relative_eq!(inv_t * proj, Projective2::identity()); + /// ``` #[inline] pub fn inverse(self) -> Transform where C: SubTCategoryOf { @@ -304,6 +401,28 @@ where DefaultAllocator: Allocator, DimNameSum> /// Attempts to invert this transformation in-place. You may use `.inverse_mut` instead of this /// transformation has a subcategory of `TProjective`. + /// + /// # Examples + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Matrix3, Transform2}; + /// + /// let m = Matrix3::new(2.0, 2.0, -0.3, + /// 3.0, 4.0, 0.1, + /// 0.0, 0.0, 1.0); + /// let t = Transform2::from_matrix_unchecked(m); + /// let mut inv_t = t; + /// assert!(inv_t.try_inverse_mut()); + /// assert_relative_eq!(t * inv_t, Transform2::identity()); + /// assert_relative_eq!(inv_t * t, Transform2::identity()); + /// + /// // Non-invertible case. + /// let m = Matrix3::new(0.0, 2.0, 1.0, + /// 3.0, 0.0, 5.0, + /// 0.0, 0.0, 0.0); + /// let mut t = Transform2::from_matrix_unchecked(m); + /// assert!(!t.try_inverse_mut()); + /// ``` #[inline] pub fn try_inverse_mut(&mut self) -> bool { self.matrix.try_inverse_mut() @@ -311,6 +430,21 @@ where DefaultAllocator: Allocator, DimNameSum> /// Inverts this transformation in-place. Use `.try_inverse_mut` if this transform has the /// `TGeneral` category (it may not be invertible). + /// + /// # Examples + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Matrix3, Projective2}; + /// + /// let m = Matrix3::new(2.0, 2.0, -0.3, + /// 3.0, 4.0, 0.1, + /// 0.0, 0.0, 1.0); + /// let proj = Projective2::from_matrix_unchecked(m); + /// let mut inv_t = proj; + /// inv_t.inverse_mut(); + /// assert_relative_eq!(proj * inv_t, Projective2::identity()); + /// assert_relative_eq!(inv_t * proj, Projective2::identity()); + /// ``` #[inline] pub fn inverse_mut(&mut self) where C: SubTCategoryOf { @@ -329,6 +463,63 @@ where DefaultAllocator: Allocator, DimNameSum> } } +impl, C: TCategory> AbsDiffEq for Transform +where + N::Epsilon: Copy, + DefaultAllocator: Allocator, DimNameSum>, +{ + type Epsilon = N::Epsilon; + + #[inline] + fn default_epsilon() -> Self::Epsilon { + N::default_epsilon() + } + + #[inline] + fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { + self.matrix.abs_diff_eq(&other.matrix, epsilon) + } +} + +impl, C: TCategory> RelativeEq for Transform +where + N::Epsilon: Copy, + DefaultAllocator: Allocator, DimNameSum>, +{ + #[inline] + fn default_max_relative() -> Self::Epsilon { + N::default_max_relative() + } + + #[inline] + fn relative_eq( + &self, + other: &Self, + epsilon: Self::Epsilon, + max_relative: Self::Epsilon, + ) -> bool + { + self.matrix + .relative_eq(&other.matrix, epsilon, max_relative) + } +} + +impl, C: TCategory> UlpsEq for Transform +where + N::Epsilon: Copy, + DefaultAllocator: Allocator, DimNameSum>, +{ + #[inline] + fn default_max_ulps() -> u32 { + N::default_max_ulps() + } + + #[inline] + fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { + self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/geometry/transform_alias.rs b/src/geometry/transform_alias.rs index 999b5224..6f529966 100644 --- a/src/geometry/transform_alias.rs +++ b/src/geometry/transform_alias.rs @@ -2,16 +2,16 @@ use base::dimension::{U2, U3}; use geometry::{TAffine, TGeneral, TProjective, Transform}; -/// A 2D general transformation that may not be inversible. Stored as an homogeneous 3x3 matrix. +/// A 2D general transformation that may not be invertible. Stored as an homogeneous 3x3 matrix. pub type Transform2 = Transform; -/// An inversible 2D general transformation. Stored as an homogeneous 3x3 matrix. +/// An invertible 2D general transformation. Stored as an homogeneous 3x3 matrix. pub type Projective2 = Transform; /// A 2D affine transformation. Stored as an homogeneous 3x3 matrix. pub type Affine2 = Transform; /// A 3D general transformation that may not be inversible. Stored as an homogeneous 4x4 matrix. pub type Transform3 = Transform; -/// An inversible 3D general transformation. Stored as an homogeneous 4x4 matrix. +/// An invertible 3D general transformation. Stored as an homogeneous 4x4 matrix. pub type Projective3 = Transform; /// A 3D affine transformation. Stored as an homogeneous 4x4 matrix. pub type Affine3 = Transform; diff --git a/src/geometry/transform_construction.rs b/src/geometry/transform_construction.rs index 0ab9e5f2..3244ad8a 100644 --- a/src/geometry/transform_construction.rs +++ b/src/geometry/transform_construction.rs @@ -12,6 +12,33 @@ impl, C: TCategory> Transform where DefaultAllocator: Allocator, DimNameSum> { /// Creates a new identity transform. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Transform2, Projective2, Affine2, Transform3, Projective3, Affine3, Point2, Point3}; + /// + /// let pt = Point2::new(1.0, 2.0); + /// let t = Projective2::identity(); + /// assert_eq!(t * pt, pt); + /// + /// let aff = Affine2::identity(); + /// assert_eq!(aff * pt, pt); + /// + /// let aff = Transform2::identity(); + /// assert_eq!(aff * pt, pt); + /// + /// // Also works in 3D. + /// let pt = Point3::new(1.0, 2.0, 3.0); + /// let t = Projective3::identity(); + /// assert_eq!(t * pt, pt); + /// + /// let aff = Affine3::identity(); + /// assert_eq!(aff * pt, pt); + /// + /// let aff = Transform3::identity(); + /// assert_eq!(aff * pt, pt); + /// ``` #[inline] pub fn identity() -> Self { Self::from_matrix_unchecked(MatrixN::<_, DimNameSum>::identity()) diff --git a/src/geometry/transform_ops.rs b/src/geometry/transform_ops.rs index 6fc4ec32..2ede5c60 100644 --- a/src/geometry/transform_ops.rs +++ b/src/geometry/transform_ops.rs @@ -143,7 +143,7 @@ md_impl_all!( if C::has_normalizer() { let normalizer = self.matrix().fixed_slice::(D::dim(), 0); - let n = normalizer.tr_dot(&rhs.coords) + unsafe { *self.matrix().get_unchecked(D::dim(), D::dim()) }; + let n = normalizer.tr_dot(&rhs.coords) + unsafe { *self.matrix().get_unchecked((D::dim(), D::dim())) }; if !n.is_zero() { return (transform * rhs + translation) / n; @@ -159,9 +159,9 @@ md_impl_all!( Mul, mul where N: Real; (DimNameSum, DimNameSum), (DimNameSum, DimNameSum) for D: DimNameAdd, CA: TCategoryMul, CB: TCategory; self: Transform, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.unwrap()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.matrix()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.into_inner()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.matrix()); ); @@ -170,9 +170,9 @@ md_impl_all!( Mul, mul where N: Real; (DimNameSum, DimNameSum), (D, D) for D: DimNameAdd, C: TCategoryMul; self: Transform, rhs: Rotation, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -181,8 +181,8 @@ md_impl_all!( Mul, mul where N: Real; (D, D), (DimNameSum, DimNameSum) for D: DimNameAdd, C: TCategoryMul; self: Rotation, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -192,9 +192,9 @@ md_impl_all!( Mul, mul where N: Real; (U4, U4), (U4, U1) for C: TCategoryMul; self: Transform, rhs: UnitQuaternion, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -203,8 +203,8 @@ md_impl_all!( Mul, mul where N: Real; (U4, U1), (U4, U4) for C: TCategoryMul; self: UnitQuaternion, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -215,9 +215,9 @@ md_impl_all!( (DimNameSum, DimNameSum), (D, U1) for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> >; self: Transform, rhs: Isometry, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -227,8 +227,8 @@ md_impl_all!( (D, U1), (DimNameSum, DimNameSum) for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> >; self: Isometry, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -239,9 +239,9 @@ md_impl_all!( (DimNameSum, DimNameSum), (D, U1) for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> >; self: Transform, rhs: Similarity, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -251,8 +251,8 @@ md_impl_all!( (D, U1), (DimNameSum, DimNameSum) for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> >; self: Similarity, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -270,9 +270,9 @@ md_impl_all!( Mul, mul where N: Real; (DimNameSum, DimNameSum), (D, U1) for D: DimNameAdd, C: TCategoryMul; self: Transform, rhs: Translation, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -282,8 +282,8 @@ md_impl_all!( (D, U1), (DimNameSum, DimNameSum) for D: DimNameAdd, C: TCategoryMul; self: Translation, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -350,9 +350,9 @@ md_impl_all!( // for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> > // where SB::Alloc: Allocator, DimNameSum >; // self: Transform, rhs: Isometry, Output = Transform; -// [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.inverse().to_homogeneous()); +// [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.inverse().to_homogeneous()); // [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.inverse().to_homogeneous()); -// [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.inverse().to_homogeneous()); +// [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.inverse().to_homogeneous()); // [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.inverse().to_homogeneous()); // ); @@ -363,8 +363,8 @@ md_impl_all!( // for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> > // where SA::Alloc: Allocator, DimNameSum >; // self: Isometry, rhs: Transform, Output = Transform; -// [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); -// [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); +// [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); +// [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); // [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // ); @@ -377,9 +377,9 @@ md_impl_all!( // where SB::Alloc: Allocator // where SB::Alloc: Allocator, DimNameSum >; // self: Transform, rhs: Similarity, Output = Transform; -// [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); +// [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); // [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); -// [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); +// [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); // [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); // ); @@ -391,8 +391,8 @@ md_impl_all!( // where SA::Alloc: Allocator // where SA::Alloc: Allocator, DimNameSum >; // self: Similarity, rhs: Transform, Output = Transform; -// [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); -// [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); +// [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); +// [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); // [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // ); @@ -425,7 +425,7 @@ md_assign_impl_all!( MulAssign, mul_assign where N: Real; (DimNameSum, DimNameSum), (DimNameSum, DimNameSum) for D: DimNameAdd, CA: TCategory, CB: SubTCategoryOf; self: Transform, rhs: Transform; - [val] => *self.matrix_mut_unchecked() *= rhs.unwrap(); + [val] => *self.matrix_mut_unchecked() *= rhs.into_inner(); [ref] => *self.matrix_mut_unchecked() *= rhs.matrix(); ); diff --git a/src/geometry/translation.rs b/src/geometry/translation.rs index 1200d251..7b1a8957 100644 --- a/src/geometry/translation.rs +++ b/src/geometry/translation.rs @@ -43,8 +43,7 @@ impl Copy for Translation where DefaultAllocator: Allocator, Owned: Copy, -{ -} +{} impl Clone for Translation where @@ -53,7 +52,7 @@ where { #[inline] fn clone(&self) -> Self { - Translation::from_vector(self.vector.clone()) + Translation::from(self.vector.clone()) } } @@ -100,7 +99,7 @@ where where Des: Deserializer<'a> { let matrix = VectorN::::deserialize(deserializer)?; - Ok(Translation::from_vector(matrix)) + Ok(Translation::from(matrix)) } } @@ -109,18 +108,49 @@ where DefaultAllocator: Allocator { /// Creates a new translation from the given vector. #[inline] + #[deprecated(note = "Use `::from` instead.")] pub fn from_vector(vector: VectorN) -> Translation { Translation { vector: vector } } /// Inverts `self`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Translation2, Translation3}; + /// let t = Translation3::new(1.0, 2.0, 3.0); + /// assert_eq!(t * t.inverse(), Translation3::identity()); + /// assert_eq!(t.inverse() * t, Translation3::identity()); + /// + /// // Work in all dimensions. + /// let t = Translation2::new(1.0, 2.0); + /// assert_eq!(t * t.inverse(), Translation2::identity()); + /// assert_eq!(t.inverse() * t, Translation2::identity()); + /// ``` #[inline] pub fn inverse(&self) -> Translation where N: ClosedNeg { - Translation::from_vector(-&self.vector) + Translation::from(-&self.vector) } /// Converts this translation into its equivalent homogeneous transformation matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::{Translation2, Translation3, Matrix3, Matrix4}; + /// let t = Translation3::new(10.0, 20.0, 30.0); + /// let expected = Matrix4::new(1.0, 0.0, 0.0, 10.0, + /// 0.0, 1.0, 0.0, 20.0, + /// 0.0, 0.0, 1.0, 30.0, + /// 0.0, 0.0, 0.0, 1.0); + /// assert_eq!(t.to_homogeneous(), expected); + /// + /// let t = Translation2::new(10.0, 20.0); + /// let expected = Matrix3::new(1.0, 0.0, 10.0, + /// 0.0, 1.0, 20.0, + /// 0.0, 0.0, 1.0); + /// assert_eq!(t.to_homogeneous(), expected); + /// ``` #[inline] pub fn to_homogeneous(&self) -> MatrixN> where @@ -136,6 +166,23 @@ where DefaultAllocator: Allocator } /// Inverts `self` in-place. + /// + /// # Example + /// ``` + /// # use nalgebra::{Translation2, Translation3}; + /// let t = Translation3::new(1.0, 2.0, 3.0); + /// let mut inv_t = Translation3::new(1.0, 2.0, 3.0); + /// inv_t.inverse_mut(); + /// assert_eq!(t * inv_t, Translation3::identity()); + /// assert_eq!(inv_t * t, Translation3::identity()); + /// + /// // Work in all dimensions. + /// let t = Translation2::new(1.0, 2.0); + /// let mut inv_t = Translation2::new(1.0, 2.0); + /// inv_t.inverse_mut(); + /// assert_eq!(t * inv_t, Translation2::identity()); + /// assert_eq!(inv_t * t, Translation2::identity()); + /// ``` #[inline] pub fn inverse_mut(&mut self) where N: ClosedNeg { diff --git a/src/geometry/translation_alga.rs b/src/geometry/translation_alga.rs index 896e7a8b..24aa28d2 100644 --- a/src/geometry/translation_alga.rs +++ b/src/geometry/translation_alga.rs @@ -183,16 +183,16 @@ where DefaultAllocator: Allocator #[inline] fn from_vector(v: VectorN) -> Option { - Some(Self::from_vector(v)) + Some(Self::from(v)) } #[inline] fn powf(&self, n: N) -> Option { - Some(Self::from_vector(&self.vector * n)) + Some(Self::from(&self.vector * n)) } #[inline] fn translation_between(a: &Point, b: &Point) -> Option { - Some(Self::from_vector(b - a)) + Some(Self::from(b - a)) } } diff --git a/src/geometry/translation_alias.rs b/src/geometry/translation_alias.rs index 331c1405..41885db7 100644 --- a/src/geometry/translation_alias.rs +++ b/src/geometry/translation_alias.rs @@ -1,9 +1,21 @@ -use base::dimension::{U2, U3}; +use base::dimension::{U1, U2, U3, U4, U5, U6}; use geometry::Translation; +/// A 1-dimensional translation. +pub type Translation1 = Translation; + /// A 2-dimensional translation. pub type Translation2 = Translation; /// A 3-dimensional translation. pub type Translation3 = Translation; + +/// A 4-dimensional translation. +pub type Translation4 = Translation; + +/// A 5-dimensional translation. +pub type Translation5 = Translation; + +/// A 6-dimensional translation. +pub type Translation6 = Translation; diff --git a/src/geometry/translation_construction.rs b/src/geometry/translation_construction.rs index cc67507d..a973603d 100644 --- a/src/geometry/translation_construction.rs +++ b/src/geometry/translation_construction.rs @@ -18,10 +18,23 @@ use geometry::Translation; impl Translation where DefaultAllocator: Allocator { - /// Creates a new square identity rotation of the given `dimension`. + /// Creates a new identity translation. + /// + /// # Example + /// ``` + /// # use nalgebra::{Point2, Point3, Translation2, Translation3}; + /// let t = Translation2::identity(); + /// let p = Point2::new(1.0, 2.0); + /// assert_eq!(t * p, p); + /// + /// // Works in all dimensions. + /// let t = Translation3::identity(); + /// let p = Point3::new(1.0, 2.0, 3.0); + /// assert_eq!(t * p, p); + /// ``` #[inline] pub fn identity() -> Translation { - Self::from_vector(VectorN::::from_element(N::zero())) + Self::from(VectorN::::from_element(N::zero())) } } @@ -41,7 +54,7 @@ where { #[inline] fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Translation { - Translation::from_vector(rng.gen::>()) + Translation::from(rng.gen::>()) } } @@ -53,7 +66,8 @@ where { #[inline] fn arbitrary(rng: &mut G) -> Self { - Self::from_vector(Arbitrary::arbitrary(rng)) + let v: VectorN = Arbitrary::arbitrary(rng); + Self::from(v) } } @@ -63,23 +77,32 @@ where * */ macro_rules! componentwise_constructors_impl( - ($($D: ty, $($args: ident:$irow: expr),*);* $(;)*) => {$( + ($($doc: expr; $D: ty, $($args: ident:$irow: expr),*);* $(;)*) => {$( impl Translation where DefaultAllocator: Allocator { - /// Initializes this matrix from its components. + #[doc = "Initializes this translation from its components."] + #[doc = "# Example\n```"] + #[doc = $doc] + #[doc = "```"] #[inline] pub fn new($($args: N),*) -> Self { - Self::from_vector(VectorN::::new($($args),*)) + Self::from(VectorN::::new($($args),*)) } } )*} ); componentwise_constructors_impl!( + "# use nalgebra::Translation1;\nlet t = Translation1::new(1.0);\nassert!(t.vector.x == 1.0);"; U1, x:0; + "# use nalgebra::Translation2;\nlet t = Translation2::new(1.0, 2.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0);"; U2, x:0, y:1; + "# use nalgebra::Translation3;\nlet t = Translation3::new(1.0, 2.0, 3.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0);"; U3, x:0, y:1, z:2; + "# use nalgebra::Translation4;\nlet t = Translation4::new(1.0, 2.0, 3.0, 4.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0);"; U4, x:0, y:1, z:2, w:3; + "# use nalgebra::Translation5;\nlet t = Translation5::new(1.0, 2.0, 3.0, 4.0, 5.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0);"; U5, x:0, y:1, z:2, w:3, a:4; + "# use nalgebra::Translation6;\nlet t = Translation6::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0 && t.vector.b == 6.0);"; U6, x:0, y:1, z:2, w:3, a:4, b:5; ); diff --git a/src/geometry/translation_conversion.rs b/src/geometry/translation_conversion.rs index 0d0b20ea..d4fd9df2 100644 --- a/src/geometry/translation_conversion.rs +++ b/src/geometry/translation_conversion.rs @@ -28,7 +28,7 @@ where { #[inline] fn to_superset(&self) -> Translation { - Translation::from_vector(self.vector.to_superset()) + Translation::from(self.vector.to_superset()) } #[inline] @@ -38,7 +38,9 @@ where #[inline] unsafe fn from_superset_unchecked(rot: &Translation) -> Self { - Translation::from_vector(rot.vector.to_subset_unchecked()) + Translation { + vector: rot.vector.to_subset_unchecked(), + } } } @@ -145,7 +147,9 @@ where #[inline] unsafe fn from_superset_unchecked(m: &MatrixN>) -> Self { let t = m.fixed_slice::(0, D::dim()); - Self::from_vector(::convert_unchecked(t.into_owned())) + Self { + vector: ::convert_unchecked(t.into_owned()), + } } } @@ -159,3 +163,12 @@ where t.to_homogeneous() } } + +impl From> for Translation +where DefaultAllocator: Allocator +{ + #[inline] + fn from(vector: VectorN) -> Self { + Translation { vector } + } +} diff --git a/src/geometry/translation_ops.rs b/src/geometry/translation_ops.rs index d124c763..57b8d6d1 100644 --- a/src/geometry/translation_ops.rs +++ b/src/geometry/translation_ops.rs @@ -13,44 +13,44 @@ use geometry::{Point, Translation}; add_sub_impl!(Mul, mul, ClosedAdd; (D, U1), (D, U1) -> (D) for D: DimName; self: &'a Translation, right: &'b Translation, Output = Translation; - Translation::from_vector(&self.vector + &right.vector); 'a, 'b); + Translation::from(&self.vector + &right.vector); 'a, 'b); add_sub_impl!(Mul, mul, ClosedAdd; (D, U1), (D, U1) -> (D) for D: DimName; self: &'a Translation, right: Translation, Output = Translation; - Translation::from_vector(&self.vector + right.vector); 'a); + Translation::from(&self.vector + right.vector); 'a); add_sub_impl!(Mul, mul, ClosedAdd; (D, U1), (D, U1) -> (D) for D: DimName; self: Translation, right: &'b Translation, Output = Translation; - Translation::from_vector(self.vector + &right.vector); 'b); + Translation::from(self.vector + &right.vector); 'b); add_sub_impl!(Mul, mul, ClosedAdd; (D, U1), (D, U1) -> (D) for D: DimName; self: Translation, right: Translation, Output = Translation; - Translation::from_vector(self.vector + right.vector); ); + Translation::from(self.vector + right.vector); ); // Translation ÷ Translation // FIXME: instead of calling inverse explicitly, could we just add a `mul_tr` or `mul_inv` method? add_sub_impl!(Div, div, ClosedSub; (D, U1), (D, U1) -> (D) for D: DimName; self: &'a Translation, right: &'b Translation, Output = Translation; - Translation::from_vector(&self.vector - &right.vector); 'a, 'b); + Translation::from(&self.vector - &right.vector); 'a, 'b); add_sub_impl!(Div, div, ClosedSub; (D, U1), (D, U1) -> (D) for D: DimName; self: &'a Translation, right: Translation, Output = Translation; - Translation::from_vector(&self.vector - right.vector); 'a); + Translation::from(&self.vector - right.vector); 'a); add_sub_impl!(Div, div, ClosedSub; (D, U1), (D, U1) -> (D) for D: DimName; self: Translation, right: &'b Translation, Output = Translation; - Translation::from_vector(self.vector - &right.vector); 'b); + Translation::from(self.vector - &right.vector); 'b); add_sub_impl!(Div, div, ClosedSub; (D, U1), (D, U1) -> (D) for D: DimName; self: Translation, right: Translation, Output = Translation; - Translation::from_vector(self.vector - right.vector); ); + Translation::from(self.vector - right.vector); ); // Translation × Point // FIXME: we don't handle properly non-zero origins here. Do we want this to be the intended diff --git a/src/geometry/unit_complex.rs b/src/geometry/unit_complex.rs index 63ddf80f..f8d94dc8 100644 --- a/src/geometry/unit_complex.rs +++ b/src/geometry/unit_complex.rs @@ -11,24 +11,50 @@ pub type UnitComplex = Unit>; impl UnitComplex { /// The rotation angle in `]-pi; pi]` of this unit complex number. + /// + /// # Example + /// ``` + /// # use nalgebra::UnitComplex; + /// let rot = UnitComplex::new(1.78); + /// assert_eq!(rot.angle(), 1.78); + /// ``` #[inline] pub fn angle(&self) -> N { self.im.atan2(self.re) } /// The sine of the rotation angle. + /// + /// # Example + /// ``` + /// # use nalgebra::UnitComplex; + /// let angle = 1.78f32; + /// let rot = UnitComplex::new(angle); + /// assert_eq!(rot.sin_angle(), angle.sin()); + /// ``` #[inline] pub fn sin_angle(&self) -> N { self.im } /// The cosine of the rotation angle. + /// + /// # Example + /// ``` + /// # use nalgebra::UnitComplex; + /// let angle = 1.78f32; + /// let rot = UnitComplex::new(angle); + /// assert_eq!(rot.cos_angle(),angle.cos()); + /// ``` #[inline] pub fn cos_angle(&self) -> N { self.re } /// The rotation angle returned as a 1-dimensional vector. + /// + /// This is generally used in the context of generic programming. Using + /// the `.angle()` method instead is more common. #[inline] pub fn scaled_axis(&self) -> Vector1 { Vector1::new(self.angle()) @@ -36,6 +62,8 @@ impl UnitComplex { /// The rotation axis and angle in ]0, pi] of this complex number. /// + /// This is generally used in the context of generic programming. Using + /// the `.angle()` method instead is more common. /// Returns `None` if the angle is zero. #[inline] pub fn axis_angle(&self) -> Option<(Unit>, N)> { @@ -53,24 +81,62 @@ impl UnitComplex { /// The underlying complex number. /// /// Same as `self.as_ref()`. + /// + /// # Example + /// ``` + /// # extern crate num_complex; + /// # use num_complex::Complex; + /// # use nalgebra::UnitComplex; + /// let angle = 1.78f32; + /// let rot = UnitComplex::new(angle); + /// assert_eq!(*rot.complex(), Complex::new(angle.cos(), angle.sin())); + /// ``` #[inline] pub fn complex(&self) -> &Complex { self.as_ref() } /// Compute the conjugate of this unit complex number. + /// + /// # Example + /// ``` + /// # use nalgebra::UnitComplex; + /// let rot = UnitComplex::new(1.78); + /// let conj = rot.conjugate(); + /// assert_eq!(rot.complex().im, -conj.complex().im); + /// assert_eq!(rot.complex().re, conj.complex().re); + /// ``` #[inline] pub fn conjugate(&self) -> Self { UnitComplex::new_unchecked(self.conj()) } /// Inverts this complex number if it is not zero. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::UnitComplex; + /// let rot = UnitComplex::new(1.2); + /// let inv = rot.inverse(); + /// assert_relative_eq!(rot * inv, UnitComplex::identity(), epsilon = 1.0e-6); + /// assert_relative_eq!(inv * rot, UnitComplex::identity(), epsilon = 1.0e-6); + /// ``` #[inline] pub fn inverse(&self) -> Self { self.conjugate() } /// The rotation angle needed to make `self` and `other` coincide. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::UnitComplex; + /// let rot1 = UnitComplex::new(0.1); + /// let rot2 = UnitComplex::new(1.7); + /// assert_relative_eq!(rot1.angle_to(&rot2), 1.6); + /// ``` #[inline] pub fn angle_to(&self, other: &Self) -> N { let delta = self.rotation_to(other); @@ -80,12 +146,36 @@ impl UnitComplex { /// The unit complex number needed to make `self` and `other` coincide. /// /// The result is such that: `self.rotation_to(other) * self == other`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::UnitComplex; + /// let rot1 = UnitComplex::new(0.1); + /// let rot2 = UnitComplex::new(1.7); + /// let rot_to = rot1.rotation_to(&rot2); + /// + /// assert_relative_eq!(rot_to * rot1, rot2); + /// assert_relative_eq!(rot_to.inverse() * rot2, rot1); + /// ``` #[inline] pub fn rotation_to(&self, other: &Self) -> Self { other / self } /// Compute in-place the conjugate of this unit complex number. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::UnitComplex; + /// let angle = 1.7; + /// let rot = UnitComplex::new(angle); + /// let mut conj = UnitComplex::new(angle); + /// conj.conjugate_mut(); + /// assert_eq!(rot.complex().im, -conj.complex().im); + /// assert_eq!(rot.complex().re, conj.complex().re); + /// ``` #[inline] pub fn conjugate_mut(&mut self) { let me = self.as_mut_unchecked(); @@ -93,6 +183,17 @@ impl UnitComplex { } /// Inverts in-place this unit complex number. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::UnitComplex; + /// let angle = 1.7; + /// let mut rot = UnitComplex::new(angle); + /// rot.inverse_mut(); + /// assert_relative_eq!(rot * UnitComplex::new(angle), UnitComplex::identity()); + /// assert_relative_eq!(UnitComplex::new(angle) * rot, UnitComplex::identity()); + /// ``` #[inline] pub fn inverse_mut(&mut self) { self.conjugate_mut() @@ -102,12 +203,30 @@ impl UnitComplex { /// /// This returns the unit complex number that identifies a rotation angle equal to /// `self.angle() × n`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::UnitComplex; + /// let rot = UnitComplex::new(0.78); + /// let pow = rot.powf(2.0); + /// assert_relative_eq!(pow.angle(), 2.0 * 0.78); + /// ``` #[inline] pub fn powf(&self, n: N) -> Self { Self::from_angle(self.angle() * n) } /// Builds the rotation matrix corresponding to this unit complex number. + /// + /// # Example + /// ``` + /// # use nalgebra::{UnitComplex, Rotation2}; + /// # use std::f32; + /// let rot = UnitComplex::new(f32::consts::FRAC_PI_6); + /// let expected = Rotation2::new(f32::consts::FRAC_PI_6); + /// assert_eq!(rot.to_rotation_matrix(), expected); + /// ``` #[inline] pub fn to_rotation_matrix(&self) -> Rotation2 { let r = self.re; @@ -117,6 +236,17 @@ impl UnitComplex { } /// Converts this unit complex number into its equivalent homogeneous transformation matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::{UnitComplex, Matrix3}; + /// # use std::f32; + /// let rot = UnitComplex::new(f32::consts::FRAC_PI_6); + /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, + /// 0.5, 0.8660254, 0.0, + /// 0.0, 0.0, 1.0); + /// assert_eq!(rot.to_homogeneous(), expected); + /// ``` #[inline] pub fn to_homogeneous(&self) -> Matrix3 { self.to_rotation_matrix().to_homogeneous() @@ -185,6 +315,6 @@ impl From> for Matrix3 { impl From> for Matrix2 { #[inline] fn from(q: UnitComplex) -> Matrix2 { - q.to_rotation_matrix().unwrap() + q.to_rotation_matrix().into_inner() } } diff --git a/src/geometry/unit_complex_construction.rs b/src/geometry/unit_complex_construction.rs index 8e4f45fe..c4c9cc0d 100644 --- a/src/geometry/unit_complex_construction.rs +++ b/src/geometry/unit_complex_construction.rs @@ -7,20 +7,40 @@ use rand::distributions::{Distribution, OpenClosed01, Standard}; use rand::Rng; use alga::general::Real; -use base::allocator::Allocator; use base::dimension::{U1, U2}; use base::storage::Storage; -use base::{DefaultAllocator, Unit, Vector}; -use geometry::{Rotation, UnitComplex}; +use base::{Unit, Vector}; +use geometry::{Rotation2, UnitComplex}; impl UnitComplex { /// The unit complex number multiplicative identity. + /// + /// # Example + /// ``` + /// # use nalgebra::UnitComplex; + /// let rot1 = UnitComplex::identity(); + /// let rot2 = UnitComplex::new(1.7); + /// + /// assert_eq!(rot1 * rot2, rot2); + /// assert_eq!(rot2 * rot1, rot2); + /// ``` #[inline] pub fn identity() -> Self { Self::new_unchecked(Complex::new(N::one(), N::zero())) } - /// Builds the unit complex number corresponding to the rotation with the angle. + /// Builds the unit complex number corresponding to the rotation with the given angle. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitComplex, Point2}; + /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); + /// + /// assert_relative_eq!(rot * Point2::new(3.0, 4.0), Point2::new(-4.0, 3.0)); + /// ``` #[inline] pub fn new(angle: N) -> Self { let (sin, cos) = angle.sin_cos(); @@ -30,6 +50,18 @@ impl UnitComplex { /// Builds the unit complex number corresponding to the rotation with the angle. /// /// Same as `Self::new(angle)`. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitComplex, Point2}; + /// let rot = UnitComplex::from_angle(f32::consts::FRAC_PI_2); + /// + /// assert_relative_eq!(rot * Point2::new(3.0, 4.0), Point2::new(-4.0, 3.0)); + /// ``` + // FIXME: deprecate this. #[inline] pub fn from_angle(angle: N) -> Self { Self::new(angle) @@ -37,7 +69,20 @@ impl UnitComplex { /// Builds the unit complex number from the sinus and cosinus of the rotation angle. /// - /// The input values are not checked. + /// The input values are not checked to actually be cosines and sine of the same value. + /// Is is generally preferable to use the `::new(angle)` constructor instead. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use std::f32; + /// # use nalgebra::{UnitComplex, Vector2, Point2}; + /// let angle = f32::consts::FRAC_PI_2; + /// let rot = UnitComplex::from_cos_sin_unchecked(angle.cos(), angle.sin()); + /// + /// assert_relative_eq!(rot * Point2::new(3.0, 4.0), Point2::new(-4.0, 3.0)); + /// ``` #[inline] pub fn from_cos_sin_unchecked(cos: N, sin: N) -> Self { UnitComplex::new_unchecked(Complex::new(cos, sin)) @@ -45,9 +90,10 @@ impl UnitComplex { /// Builds a unit complex rotation from an angle in radian wrapped in a 1-dimensional vector. /// - /// Equivalent to `Self::new(axisangle[0])`. + /// This is generally used in the context of generic programming. Using + /// the `::new(angle)` method instead is more common. #[inline] - pub fn from_scaled_axis>(axisangle: Vector) -> Self { + pub fn from_scaled_axis>(axisangle: Vector) -> Self { Self::from_angle(axisangle[0]) } @@ -61,7 +107,7 @@ impl UnitComplex { /// Creates a new unit complex number from a complex number. /// - /// The input complex number will be normalized. Returns the complex number norm as well. + /// The input complex number will be normalized. Returns the norm of the complex number as well. #[inline] pub fn from_complex_and_get(q: Complex) -> (Self, N) { let norm = (q.im * q.im + q.re * q.re).sqrt(); @@ -69,25 +115,56 @@ impl UnitComplex { } /// Builds the unit complex number from the corresponding 2D rotation matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::{Rotation2, UnitComplex}; + /// let rot = Rotation2::new(1.7); + /// let complex = UnitComplex::from_rotation_matrix(&rot); + /// assert_eq!(complex, UnitComplex::new(1.7)); + /// ``` + // FIXME: add UnitComplex::from(...) instead? #[inline] - pub fn from_rotation_matrix(rotmat: &Rotation) -> Self - where DefaultAllocator: Allocator { + pub fn from_rotation_matrix(rotmat: &Rotation2) -> Self { Self::new_unchecked(Complex::new(rotmat[(0, 0)], rotmat[(1, 0)])) } /// The unit complex needed to make `a` and `b` be collinear and point toward the same /// direction. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Vector2, UnitComplex}; + /// let a = Vector2::new(1.0, 2.0); + /// let b = Vector2::new(2.0, 1.0); + /// let rot = UnitComplex::rotation_between(&a, &b); + /// assert_relative_eq!(rot * a, b); + /// assert_relative_eq!(rot.inverse() * b, a); + /// ``` #[inline] pub fn rotation_between(a: &Vector, b: &Vector) -> Self where - SB: Storage, - SC: Storage, + SB: Storage, + SC: Storage, { Self::scaled_rotation_between(a, b, N::one()) } /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Vector2, UnitComplex}; + /// let a = Vector2::new(1.0, 2.0); + /// let b = Vector2::new(2.0, 1.0); + /// let rot2 = UnitComplex::scaled_rotation_between(&a, &b, 0.2); + /// let rot5 = UnitComplex::scaled_rotation_between(&a, &b, 0.5); + /// assert_relative_eq!(rot2 * rot2 * rot2 * rot2 * rot2 * a, b, epsilon = 1.0e-6); + /// assert_relative_eq!(rot5 * rot5 * a, b, epsilon = 1.0e-6); + /// ``` #[inline] pub fn scaled_rotation_between( a: &Vector, @@ -95,8 +172,8 @@ impl UnitComplex { s: N, ) -> Self where - SB: Storage, - SC: Storage, + SB: Storage, + SC: Storage, { // FIXME: code duplication with Rotation. if let (Some(na), Some(nb)) = ( @@ -111,6 +188,17 @@ impl UnitComplex { /// The unit complex needed to make `a` and `b` be collinear and point toward the same /// direction. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Unit, Vector2, UnitComplex}; + /// let a = Unit::new_normalize(Vector2::new(1.0, 2.0)); + /// let b = Unit::new_normalize(Vector2::new(2.0, 1.0)); + /// let rot = UnitComplex::rotation_between_axis(&a, &b); + /// assert_relative_eq!(rot * a, b); + /// assert_relative_eq!(rot.inverse() * b, a); + /// ``` #[inline] pub fn rotation_between_axis( a: &Unit>, @@ -125,6 +213,18 @@ impl UnitComplex { /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. + /// + /// # Example + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Unit, Vector2, UnitComplex}; + /// let a = Unit::new_normalize(Vector2::new(1.0, 2.0)); + /// let b = Unit::new_normalize(Vector2::new(2.0, 1.0)); + /// let rot2 = UnitComplex::scaled_rotation_between_axis(&a, &b, 0.2); + /// let rot5 = UnitComplex::scaled_rotation_between_axis(&a, &b, 0.5); + /// assert_relative_eq!(rot2 * rot2 * rot2 * rot2 * rot2 * a, b, epsilon = 1.0e-6); + /// assert_relative_eq!(rot5 * rot5 * a, b, epsilon = 1.0e-6); + /// ``` #[inline] pub fn scaled_rotation_between_axis( na: &Unit>, diff --git a/src/geometry/unit_complex_ops.rs b/src/geometry/unit_complex_ops.rs index 6456733c..c9d31ad4 100644 --- a/src/geometry/unit_complex_ops.rs +++ b/src/geometry/unit_complex_ops.rs @@ -50,7 +50,7 @@ impl Mul> for UnitComplex { #[inline] fn mul(self, rhs: UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.unwrap() * rhs.unwrap()) + Unit::new_unchecked(self.into_inner() * rhs.into_inner()) } } @@ -59,7 +59,7 @@ impl<'a, N: Real> Mul> for &'a UnitComplex { #[inline] fn mul(self, rhs: UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.complex() * rhs.unwrap()) + Unit::new_unchecked(self.complex() * rhs.into_inner()) } } @@ -68,7 +68,7 @@ impl<'b, N: Real> Mul<&'b UnitComplex> for UnitComplex { #[inline] fn mul(self, rhs: &'b UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.unwrap() * rhs.complex()) + Unit::new_unchecked(self.into_inner() * rhs.complex()) } } @@ -87,7 +87,7 @@ impl Div> for UnitComplex { #[inline] fn div(self, rhs: UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.unwrap() * rhs.conjugate().unwrap()) + Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner()) } } @@ -96,7 +96,7 @@ impl<'a, N: Real> Div> for &'a UnitComplex { #[inline] fn div(self, rhs: UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.complex() * rhs.conjugate().unwrap()) + Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner()) } } @@ -105,7 +105,7 @@ impl<'b, N: Real> Div<&'b UnitComplex> for UnitComplex { #[inline] fn div(self, rhs: &'b UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.unwrap() * rhs.conjugate().unwrap()) + Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner()) } } @@ -114,7 +114,7 @@ impl<'a, 'b, N: Real> Div<&'b UnitComplex> for &'a UnitComplex { #[inline] fn div(self, rhs: &'b UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.complex() * rhs.conjugate().unwrap()) + Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner()) } } @@ -220,7 +220,7 @@ complex_op_impl_all!( [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; - [ref ref] => Point2::from_coordinates(self * &rhs.coords); + [ref ref] => Point2::from(self * &rhs.coords); ); // UnitComplex × Vector @@ -260,7 +260,7 @@ complex_op_impl_all!( [val ref] => &self * rhs; [ref ref] => { let shift = self * &rhs.translation.vector; - Isometry::from_parts(Translation::from_vector(shift), self * &rhs.rotation) + Isometry::from_parts(Translation::from(shift), self * &rhs.rotation) }; ); @@ -282,10 +282,10 @@ complex_op_impl_all!( (U2, U1); self: UnitComplex, rhs: Translation, Output = Isometry>; - [val val] => Isometry::from_parts(Translation::from_vector(&self * rhs.vector), self); - [ref val] => Isometry::from_parts(Translation::from_vector( self * rhs.vector), self.clone()); - [val ref] => Isometry::from_parts(Translation::from_vector(&self * &rhs.vector), self); - [ref ref] => Isometry::from_parts(Translation::from_vector( self * &rhs.vector), self.clone()); + [val val] => Isometry::from_parts(Translation::from(&self * rhs.vector), self); + [ref val] => Isometry::from_parts(Translation::from( self * rhs.vector), self.clone()); + [val ref] => Isometry::from_parts(Translation::from(&self * &rhs.vector), self); + [ref ref] => Isometry::from_parts(Translation::from( self * &rhs.vector), self.clone()); ); // Translation × UnitComplex @@ -425,11 +425,11 @@ impl UnitComplex { for j in 0..rhs.ncols() { unsafe { - let a = *rhs.get_unchecked(0, j); - let b = *rhs.get_unchecked(1, j); + let a = *rhs.get_unchecked((0, j)); + let b = *rhs.get_unchecked((1, j)); - *rhs.get_unchecked_mut(0, j) = r * a - i * b; - *rhs.get_unchecked_mut(1, j) = i * a + r * b; + *rhs.get_unchecked_mut((0, j)) = r * a - i * b; + *rhs.get_unchecked_mut((1, j)) = i * a + r * b; } } } @@ -452,11 +452,11 @@ impl UnitComplex { // FIXME: can we optimize that to iterate on one column at a time ? for j in 0..lhs.nrows() { unsafe { - let a = *lhs.get_unchecked(j, 0); - let b = *lhs.get_unchecked(j, 1); + let a = *lhs.get_unchecked((j, 0)); + let b = *lhs.get_unchecked((j, 1)); - *lhs.get_unchecked_mut(j, 0) = r * a + i * b; - *lhs.get_unchecked_mut(j, 1) = -i * a + r * b; + *lhs.get_unchecked_mut((j, 0)) = r * a + i * b; + *lhs.get_unchecked_mut((j, 1)) = -i * a + r * b; } } } diff --git a/src/lib.rs b/src/lib.rs index 05665575..15776459 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -203,6 +203,12 @@ pub fn zero>() -> T { /// /// * [`one`](fn.one.html) /// * [`zero`](fn.zero.html) +/// +/// # Deprecated +/// Use [Point::origin] instead. +/// +/// Or, use [EuclideanSpace::origin](https://docs.rs/alga/0.7.2/alga/linear/trait.EuclideanSpace.html#tymethod.origin). +#[deprecated(note = "use `Point::origin` instead")] #[inline] pub fn origin() -> P { P::origin() @@ -289,6 +295,9 @@ pub fn min(a: T, b: T) -> T { } /// The absolute value of `a`. +/// +/// Deprecated: Use [Matrix::abs] or [Real::abs] instead. +#[deprecated(note = "use `Matrix::abs` or `Real::abs` instead")] #[inline] pub fn abs(a: &T) -> T { a.abs() @@ -427,12 +436,26 @@ pub fn inverse>(m: &M) -> M { */ /// Computes the dot product of two vectors. +/// +/// ## Deprecated +/// Use these methods instead: +/// - [Matrix::dot] +/// - [Quaternion::dot] +/// +/// Or, use [FiniteDimVectorSpace::dot](https://docs.rs/alga/0.7.2/alga/linear/trait.FiniteDimVectorSpace.html#tymethod.dot). +#[deprecated(note = "use `Matrix::dot` or `Quaternion::dot` instead")] #[inline] pub fn dot(a: &V, b: &V) -> V::Field { a.dot(b) } /// Computes the smallest angle between two vectors. +/// +/// ## Deprecated +/// Use [Matrix::angle] instead. +/// +/// Or, use [InnerSpace::angle](https://docs.rs/alga/0.7.2/alga/linear/trait.InnerSpace.html#method.angle). +#[deprecated(note = "use `Matrix::angle` instead")] #[inline] pub fn angle(a: &V, b: &V) -> V::Real { a.angle(b) @@ -449,6 +472,14 @@ pub fn angle(a: &V, b: &V) -> V::Real { /// * [`magnitude`](fn.magnitude.html) /// * [`magnitude_squared`](fn.magnitude_squared.html) /// * [`norm_squared`](fn.norm_squared.html) +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::norm] +/// * [Quaternion::norm] +/// +/// Or, use [NormedSpace::norm](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.norm). +#[deprecated(note = "use `Matrix::norm` or `Quaternion::norm` instead")] #[inline] pub fn norm(v: &V) -> V::Field { v.norm() @@ -461,6 +492,14 @@ pub fn norm(v: &V) -> V::Field { /// * [`magnitude`](fn.magnitude.html) /// * [`magnitude_squared`](fn.magnitude_squared.html) /// * [`norm`](fn.norm.html) +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::norm_squared] +/// * [Quaternion::norm_squared] +/// +/// Or, use [NormedSpace::norm_squared](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.norm_squared). +#[deprecated(note = "use `Matrix::norm_squared` or `Quaternion::norm_squared` instead")] #[inline] pub fn norm_squared(v: &V) -> V::Field { v.norm_squared() @@ -473,6 +512,14 @@ pub fn norm_squared(v: &V) -> V::Field { /// * [`magnitude_squared`](fn.magnitude_squared.html) /// * [`norm`](fn.norm.html) /// * [`norm_squared`](fn.norm_squared.html) +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::magnitude] +/// * [Quaternion::magnitude] +/// +/// Or, use [NormedSpace::norm](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.norm). +#[deprecated(note = "use `Matrix::magnitude` or `Quaternion::magnitude` instead")] #[inline] pub fn magnitude(v: &V) -> V::Field { v.norm() @@ -486,18 +533,42 @@ pub fn magnitude(v: &V) -> V::Field { /// * [`magnitude`](fn.magnitude.html) /// * [`norm`](fn.norm.html) /// * [`norm_squared`](fn.norm_squared.html) +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::magnitude_squared] +/// * [Quaternion::magnitude_squared] +/// +/// Or, use [NormedSpace::norm_squared](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.norm_squared). +#[deprecated(note = "use `Matrix::magnitude_squared` or `Quaternion::magnitude_squared` instead")] #[inline] pub fn magnitude_squared(v: &V) -> V::Field { v.norm_squared() } /// Computes the normalized version of the vector `v`. +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::normalize] +/// * [Quaternion::normalize] +/// +/// Or, use [NormedSpace::normalize](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.normalize). +#[deprecated(note = "use `Matrix::normalize` or `Quaternion::normalize` instead")] #[inline] pub fn normalize(v: &V) -> V { v.normalize() } /// Computes the normalized version of the vector `v` or returns `None` if its norm is smaller than `min_norm`. +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::try_normalize] +/// * [Quaternion::try_normalize] +/// +/// Or, use [NormedSpace::try_normalize](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.try_normalize). +#[deprecated(note = "use `Matrix::try_normalize` or `Quaternion::try_normalize` instead")] #[inline] pub fn try_normalize(v: &V, min_norm: V::Field) -> Option { v.try_normalize(min_norm) diff --git a/src/linalg/bidiagonal.rs b/src/linalg/bidiagonal.rs index 5e2f6609..3e73c40f 100644 --- a/src/linalg/bidiagonal.rs +++ b/src/linalg/bidiagonal.rs @@ -15,23 +15,27 @@ use linalg::householder; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DimMinimum: DimSub, + serde(bound( + serialize = "DimMinimum: DimSub, DefaultAllocator: Allocator + Allocator> + Allocator, U1>>, MatrixMN: Serialize, VectorN>: Serialize, - VectorN, U1>>: Serialize")) + VectorN, U1>>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DimMinimum: DimSub, + serde(bound( + deserialize = "DimMinimum: DimSub, DefaultAllocator: Allocator + Allocator> + Allocator, U1>>, MatrixMN: Deserialize<'de>, VectorN>: Deserialize<'de>, - VectorN, U1>>: Deserialize<'de>")) + VectorN, U1>>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct Bidiagonal, C: Dim> @@ -60,8 +64,7 @@ where MatrixMN: Copy, VectorN>: Copy, VectorN, U1>>: Copy, -{ -} +{} impl, C: Dim> Bidiagonal where diff --git a/src/linalg/cholesky.rs b/src/linalg/cholesky.rs index b75ba33a..c1fd7b85 100644 --- a/src/linalg/cholesky.rs +++ b/src/linalg/cholesky.rs @@ -13,13 +13,17 @@ use storage::{Storage, StorageMut}; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator, - MatrixN: Serialize")) + serde(bound( + serialize = "DefaultAllocator: Allocator, + MatrixN: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator, - MatrixN: Deserialize<'de>")) + serde(bound( + deserialize = "DefaultAllocator: Allocator, + MatrixN: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct Cholesky @@ -32,8 +36,7 @@ impl Copy for Cholesky where DefaultAllocator: Allocator, MatrixN: Copy, -{ -} +{} impl> Cholesky where DefaultAllocator: Allocator @@ -49,7 +52,7 @@ where DefaultAllocator: Allocator for j in 0..n { for k in 0..j { - let factor = unsafe { -*matrix.get_unchecked(j, k) }; + let factor = unsafe { -*matrix.get_unchecked((j, k)) }; let (mut col_j, col_k) = matrix.columns_range_pair_mut(j, k); let mut col_j = col_j.rows_range_mut(j..); @@ -58,11 +61,11 @@ where DefaultAllocator: Allocator col_j.axpy(factor, &col_k, N::one()); } - let diag = unsafe { *matrix.get_unchecked(j, j) }; + let diag = unsafe { *matrix.get_unchecked((j, j)) }; if diag > N::zero() { let denom = diag.sqrt(); unsafe { - *matrix.get_unchecked_mut(j, j) = denom; + *matrix.get_unchecked_mut((j, j)) = denom; } let mut col = matrix.slice_range_mut(j + 1.., j); diff --git a/src/linalg/determinant.rs b/src/linalg/determinant.rs index 100a21e7..6229da0e 100644 --- a/src/linalg/determinant.rs +++ b/src/linalg/determinant.rs @@ -23,27 +23,27 @@ impl, S: Storage> SquareMatrix N::one(), - 1 => *self.get_unchecked(0, 0), + 1 => *self.get_unchecked((0, 0)), 2 => { - let m11 = *self.get_unchecked(0, 0); - let m12 = *self.get_unchecked(0, 1); - let m21 = *self.get_unchecked(1, 0); - let m22 = *self.get_unchecked(1, 1); + let m11 = *self.get_unchecked((0, 0)); + let m12 = *self.get_unchecked((0, 1)); + let m21 = *self.get_unchecked((1, 0)); + let m22 = *self.get_unchecked((1, 1)); m11 * m22 - m21 * m12 } 3 => { - let m11 = *self.get_unchecked(0, 0); - let m12 = *self.get_unchecked(0, 1); - let m13 = *self.get_unchecked(0, 2); + let m11 = *self.get_unchecked((0, 0)); + let m12 = *self.get_unchecked((0, 1)); + let m13 = *self.get_unchecked((0, 2)); - let m21 = *self.get_unchecked(1, 0); - let m22 = *self.get_unchecked(1, 1); - let m23 = *self.get_unchecked(1, 2); + let m21 = *self.get_unchecked((1, 0)); + let m22 = *self.get_unchecked((1, 1)); + let m23 = *self.get_unchecked((1, 2)); - let m31 = *self.get_unchecked(2, 0); - let m32 = *self.get_unchecked(2, 1); - let m33 = *self.get_unchecked(2, 2); + let m31 = *self.get_unchecked((2, 0)); + let m32 = *self.get_unchecked((2, 1)); + let m33 = *self.get_unchecked((2, 2)); let minor_m12_m23 = m22 * m33 - m32 * m23; let minor_m11_m23 = m21 * m33 - m31 * m23; diff --git a/src/linalg/full_piv_lu.rs b/src/linalg/full_piv_lu.rs index 43cc20ba..022152f4 100644 --- a/src/linalg/full_piv_lu.rs +++ b/src/linalg/full_piv_lu.rs @@ -15,17 +15,21 @@ use linalg::PermutationSequence; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimum>, MatrixMN: Serialize, - PermutationSequence>: Serialize")) + PermutationSequence>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimum>, MatrixMN: Deserialize<'de>, - PermutationSequence>: Deserialize<'de>")) + PermutationSequence>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct FullPivLU, C: Dim> @@ -41,8 +45,7 @@ where DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimum>, MatrixMN: Copy, PermutationSequence>: Copy, -{ -} +{} impl, C: Dim> FullPivLU where DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimum> @@ -248,7 +251,7 @@ where DefaultAllocator: Allocator + Allocator<(usize, usize), D> let mut res = self.lu[(dim - 1, dim - 1)]; if !res.is_zero() { for i in 0..dim - 1 { - res *= unsafe { *self.lu.get_unchecked(i, i) }; + res *= unsafe { *self.lu.get_unchecked((i, i)) }; } res * self.p.determinant() * self.q.determinant() diff --git a/src/linalg/hessenberg.rs b/src/linalg/hessenberg.rs index 9a525edb..783055a3 100644 --- a/src/linalg/hessenberg.rs +++ b/src/linalg/hessenberg.rs @@ -14,17 +14,21 @@ use linalg::householder; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator>, MatrixN: Serialize, - VectorN>: Serialize")) + VectorN>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator>, MatrixN: Deserialize<'de>, - VectorN>: Deserialize<'de>")) + VectorN>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct Hessenberg> @@ -39,8 +43,7 @@ where DefaultAllocator: Allocator + Allocator>, MatrixN: Copy, VectorN>: Copy, -{ -} +{} impl> Hessenberg where DefaultAllocator: Allocator + Allocator + Allocator> diff --git a/src/linalg/inverse.rs b/src/linalg/inverse.rs index 08cebd2a..5748900f 100644 --- a/src/linalg/inverse.rs +++ b/src/linalg/inverse.rs @@ -35,46 +35,46 @@ impl> SquareMatrix { match dim { 0 => true, 1 => { - let determinant = self.get_unchecked(0, 0).clone(); + let determinant = self.get_unchecked((0, 0)).clone(); if determinant == N::zero() { false } else { - *self.get_unchecked_mut(0, 0) = N::one() / determinant; + *self.get_unchecked_mut((0, 0)) = N::one() / determinant; true } } 2 => { - let m11 = *self.get_unchecked(0, 0); - let m12 = *self.get_unchecked(0, 1); - let m21 = *self.get_unchecked(1, 0); - let m22 = *self.get_unchecked(1, 1); + let m11 = *self.get_unchecked((0, 0)); + let m12 = *self.get_unchecked((0, 1)); + let m21 = *self.get_unchecked((1, 0)); + let m22 = *self.get_unchecked((1, 1)); let determinant = m11 * m22 - m21 * m12; if determinant == N::zero() { false } else { - *self.get_unchecked_mut(0, 0) = m22 / determinant; - *self.get_unchecked_mut(0, 1) = -m12 / determinant; + *self.get_unchecked_mut((0, 0)) = m22 / determinant; + *self.get_unchecked_mut((0, 1)) = -m12 / determinant; - *self.get_unchecked_mut(1, 0) = -m21 / determinant; - *self.get_unchecked_mut(1, 1) = m11 / determinant; + *self.get_unchecked_mut((1, 0)) = -m21 / determinant; + *self.get_unchecked_mut((1, 1)) = m11 / determinant; true } } 3 => { - let m11 = *self.get_unchecked(0, 0); - let m12 = *self.get_unchecked(0, 1); - let m13 = *self.get_unchecked(0, 2); + let m11 = *self.get_unchecked((0, 0)); + let m12 = *self.get_unchecked((0, 1)); + let m13 = *self.get_unchecked((0, 2)); - let m21 = *self.get_unchecked(1, 0); - let m22 = *self.get_unchecked(1, 1); - let m23 = *self.get_unchecked(1, 2); + let m21 = *self.get_unchecked((1, 0)); + let m22 = *self.get_unchecked((1, 1)); + let m23 = *self.get_unchecked((1, 2)); - let m31 = *self.get_unchecked(2, 0); - let m32 = *self.get_unchecked(2, 1); - let m33 = *self.get_unchecked(2, 2); + let m31 = *self.get_unchecked((2, 0)); + let m32 = *self.get_unchecked((2, 1)); + let m33 = *self.get_unchecked((2, 2)); let minor_m12_m23 = m22 * m33 - m32 * m23; let minor_m11_m23 = m21 * m33 - m31 * m23; @@ -86,17 +86,17 @@ impl> SquareMatrix { if determinant == N::zero() { false } else { - *self.get_unchecked_mut(0, 0) = minor_m12_m23 / determinant; - *self.get_unchecked_mut(0, 1) = (m13 * m32 - m33 * m12) / determinant; - *self.get_unchecked_mut(0, 2) = (m12 * m23 - m22 * m13) / determinant; + *self.get_unchecked_mut((0, 0)) = minor_m12_m23 / determinant; + *self.get_unchecked_mut((0, 1)) = (m13 * m32 - m33 * m12) / determinant; + *self.get_unchecked_mut((0, 2)) = (m12 * m23 - m22 * m13) / determinant; - *self.get_unchecked_mut(1, 0) = -minor_m11_m23 / determinant; - *self.get_unchecked_mut(1, 1) = (m11 * m33 - m31 * m13) / determinant; - *self.get_unchecked_mut(1, 2) = (m13 * m21 - m23 * m11) / determinant; + *self.get_unchecked_mut((1, 0)) = -minor_m11_m23 / determinant; + *self.get_unchecked_mut((1, 1)) = (m11 * m33 - m31 * m13) / determinant; + *self.get_unchecked_mut((1, 2)) = (m13 * m21 - m23 * m11) / determinant; - *self.get_unchecked_mut(2, 0) = minor_m11_m22 / determinant; - *self.get_unchecked_mut(2, 1) = (m12 * m31 - m32 * m11) / determinant; - *self.get_unchecked_mut(2, 2) = (m11 * m22 - m21 * m12) / determinant; + *self.get_unchecked_mut((2, 0)) = minor_m11_m22 / determinant; + *self.get_unchecked_mut((2, 1)) = (m12 * m31 - m32 * m11) / determinant; + *self.get_unchecked_mut((2, 2)) = (m11 * m22 - m21 * m12) / determinant; true } diff --git a/src/linalg/lu.rs b/src/linalg/lu.rs index d150852a..67fae23f 100644 --- a/src/linalg/lu.rs +++ b/src/linalg/lu.rs @@ -15,17 +15,21 @@ use linalg::PermutationSequence; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimum>, MatrixMN: Serialize, - PermutationSequence>: Serialize")) + PermutationSequence>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimum>, MatrixMN: Deserialize<'de>, - PermutationSequence>: Deserialize<'de>")) + PermutationSequence>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct LU, C: Dim> @@ -40,8 +44,7 @@ where DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimum>, MatrixMN: Copy, PermutationSequence>: Copy, -{ -} +{} /// Performs a LU decomposition to overwrite `out` with the inverse of `matrix`. /// @@ -287,7 +290,7 @@ where DefaultAllocator: Allocator + Allocator<(usize, usize), D> let mut res = N::one(); for i in 0..dim { - res *= unsafe { *self.lu.get_unchecked(i, i) }; + res *= unsafe { *self.lu.get_unchecked((i, i)) }; } res * self.p.determinant() diff --git a/src/linalg/permutation_sequence.rs b/src/linalg/permutation_sequence.rs index bba40b8f..ac58d857 100644 --- a/src/linalg/permutation_sequence.rs +++ b/src/linalg/permutation_sequence.rs @@ -15,13 +15,17 @@ use storage::StorageMut; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator<(usize, usize), D>, - VectorN<(usize, usize), D>: Serialize")) + serde(bound( + serialize = "DefaultAllocator: Allocator<(usize, usize), D>, + VectorN<(usize, usize), D>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator<(usize, usize), D>, - VectorN<(usize, usize), D>: Deserialize<'de>")) + serde(bound( + deserialize = "DefaultAllocator: Allocator<(usize, usize), D>, + VectorN<(usize, usize), D>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct PermutationSequence @@ -35,8 +39,7 @@ impl Copy for PermutationSequence where DefaultAllocator: Allocator<(usize, usize), D>, VectorN<(usize, usize), D>: Copy, -{ -} +{} impl PermutationSequence where DefaultAllocator: Allocator<(usize, usize), D> diff --git a/src/linalg/qr.rs b/src/linalg/qr.rs index 3ec53c1d..81f72269 100644 --- a/src/linalg/qr.rs +++ b/src/linalg/qr.rs @@ -15,17 +15,21 @@ use linalg::householder; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator>, MatrixMN: Serialize, - VectorN>: Serialize")) + VectorN>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator>, MatrixMN: Deserialize<'de>, - VectorN>: Deserialize<'de>")) + VectorN>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct QR, C: Dim> @@ -40,8 +44,7 @@ where DefaultAllocator: Allocator + Allocator>, MatrixMN: Copy, VectorN>: Copy, -{ -} +{} impl, C: Dim> QR where DefaultAllocator: Allocator + Allocator + Allocator> diff --git a/src/linalg/schur.rs b/src/linalg/schur.rs index cb6c637f..66299c07 100644 --- a/src/linalg/schur.rs +++ b/src/linalg/schur.rs @@ -19,13 +19,17 @@ use linalg::Hessenberg; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator, - MatrixN: Serialize")) + serde(bound( + serialize = "DefaultAllocator: Allocator, + MatrixN: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator, - MatrixN: Deserialize<'de>")) + serde(bound( + deserialize = "DefaultAllocator: Allocator, + MatrixN: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct RealSchur @@ -39,8 +43,7 @@ impl Copy for RealSchur where DefaultAllocator: Allocator, MatrixN: Copy, -{ -} +{} impl RealSchur where @@ -409,7 +412,7 @@ where rot.rotate_rows(&mut m); if compute_q { - let c = rot.unwrap(); + let c = rot.into_inner(); // XXX: we have to build the matrix manually because // rot.to_rotation_matrix().unwrap() causes an ICE. q = Some(MatrixN::from_column_slice_generic( diff --git a/src/linalg/solve.rs b/src/linalg/solve.rs index 904a1b29..4d2d103f 100644 --- a/src/linalg/solve.rs +++ b/src/linalg/solve.rs @@ -79,7 +79,7 @@ impl> SquareMatrix { let coeff; unsafe { - let diag = *self.get_unchecked(i, i); + let diag = *self.get_unchecked((i, i)); if diag.is_zero() { return false; @@ -161,7 +161,7 @@ impl> SquareMatrix { let coeff; unsafe { - let diag = *self.get_unchecked(i, i); + let diag = *self.get_unchecked((i, i)); if diag.is_zero() { return false; @@ -258,7 +258,7 @@ impl> SquareMatrix { unsafe { let b_i = b.vget_unchecked_mut(i); - let diag = *self.get_unchecked(i, i); + let diag = *self.get_unchecked((i, i)); if diag.is_zero() { return false; @@ -304,7 +304,7 @@ impl> SquareMatrix { unsafe { let b_i = b.vget_unchecked_mut(i); - let diag = *self.get_unchecked(i, i); + let diag = *self.get_unchecked((i, i)); if diag.is_zero() { return false; diff --git a/src/linalg/svd.rs b/src/linalg/svd.rs index e3e42d5b..5c1d9a0f 100644 --- a/src/linalg/svd.rs +++ b/src/linalg/svd.rs @@ -20,27 +20,27 @@ use linalg::Bidiagonal; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde( - bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator> + Allocator, C> + Allocator>, MatrixMN>: Serialize, MatrixMN, C>: Serialize, - VectorN>: Serialize") - ) + VectorN>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde( - bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator> + Allocator, C> + Allocator>, MatrixMN>: Deserialize<'de>, MatrixMN, C>: Deserialize<'de>, - VectorN>: Deserialize<'de>") - ) + VectorN>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct SVD, C: Dim> @@ -64,8 +64,7 @@ where MatrixMN>: Copy, MatrixMN, C>: Copy, VectorN>: Copy, -{ -} +{} impl, C: Dim> SVD where @@ -485,90 +484,94 @@ where /// Rebuild the original matrix. /// - /// This is useful if some of the singular values have been manually modified. Panics if the - /// right- and left- singular vectors have not been computed at construction-time. - pub fn recompose(self) -> MatrixMN { - let mut u = self.u.expect("SVD recomposition: U has not been computed."); - let v_t = self - .v_t - .expect("SVD recomposition: V^t has not been computed."); - - for i in 0..self.singular_values.len() { - let val = self.singular_values[i]; - u.column_mut(i).mul_assign(val); + /// This is useful if some of the singular values have been manually modified. + /// Returns `Err` if the right- and left- singular vectors have not been + /// computed at construction-time. + pub fn recompose(self) -> Result, &'static str> { + match (self.u, self.v_t) { + (Some(mut u), Some(v_t)) => { + for i in 0..self.singular_values.len() { + let val = self.singular_values[i]; + u.column_mut(i).mul_assign(val); + } + Ok(u * v_t) + } + (None, None) => Err("SVD recomposition: U and V^t have not been computed."), + (None, _) => Err("SVD recomposition: U has not been computed."), + (_, None) => Err("SVD recomposition: V^t has not been computed.") } - - u * v_t } /// Computes the pseudo-inverse of the decomposed matrix. /// /// Any singular value smaller than `eps` is assumed to be zero. - /// Panics if the right- and left- singular vectors have not been computed at - /// construction-time. - pub fn pseudo_inverse(mut self, eps: N) -> MatrixMN - where DefaultAllocator: Allocator { - assert!( - eps >= N::zero(), - "SVD pseudo inverse: the epsilon must be non-negative." - ); - for i in 0..self.singular_values.len() { - let val = self.singular_values[i]; - - if val > eps { - self.singular_values[i] = N::one() / val; - } else { - self.singular_values[i] = N::zero(); - } + /// Returns `Err` if the right- and left- singular vectors have not + /// been computed at construction-time. + pub fn pseudo_inverse(mut self, eps: N) -> Result, &'static str> + where + DefaultAllocator: Allocator, + { + if eps < N::zero() { + Err("SVD pseudo inverse: the epsilon must be non-negative.") } + else { + for i in 0..self.singular_values.len() { + let val = self.singular_values[i]; - self.recompose().transpose() + if val > eps { + self.singular_values[i] = N::one() / val; + } else { + self.singular_values[i] = N::zero(); + } + } + + self.recompose().map(|m| m.transpose()) + } } /// Solves the system `self * x = b` where `self` is the decomposed matrix and `x` the unknown. /// /// Any singular value smaller than `eps` is assumed to be zero. - /// Returns `None` if the singular vectors `U` and `V` have not been computed. + /// Returns `Err` if the singular vectors `U` and `V` have not been computed. // FIXME: make this more generic wrt the storage types and the dimensions for `b`. pub fn solve( &self, b: &Matrix, eps: N, - ) -> MatrixMN + ) -> Result, &'static str> where S2: Storage, DefaultAllocator: Allocator + Allocator, C2>, ShapeConstraint: SameNumberOfRows, { - assert!( - eps >= N::zero(), - "SVD solve: the epsilon must be non-negative." - ); - let u = self - .u - .as_ref() - .expect("SVD solve: U has not been computed."); - let v_t = self - .v_t - .as_ref() - .expect("SVD solve: V^t has not been computed."); + if eps < N::zero() { + Err("SVD solve: the epsilon must be non-negative.") + } + else { + match (&self.u, &self.v_t) { + (Some(u), Some(v_t)) => { + let mut ut_b = u.tr_mul(b); - let mut ut_b = u.tr_mul(b); + for j in 0..ut_b.ncols() { + let mut col = ut_b.column_mut(j); - for j in 0..ut_b.ncols() { - let mut col = ut_b.column_mut(j); + for i in 0..self.singular_values.len() { + let val = self.singular_values[i]; + if val > eps { + col[i] /= val; + } else { + col[i] = N::zero(); + } + } + } - for i in 0..self.singular_values.len() { - let val = self.singular_values[i]; - if val > eps { - col[i] /= val; - } else { - col[i] = N::zero(); + Ok(v_t.tr_mul(&ut_b)) } + (None, None) => Err("SVD solve: U and V^t have not been computed."), + (None, _) => Err("SVD solve: U has not been computed."), + (_, None) => Err("SVD solve: V^t has not been computed.") } } - - v_t.tr_mul(&ut_b) } } @@ -625,8 +628,10 @@ where /// Computes the pseudo-inverse of this matrix. /// /// All singular values below `eps` are considered equal to 0. - pub fn pseudo_inverse(self, eps: N) -> MatrixMN - where DefaultAllocator: Allocator { + pub fn pseudo_inverse(self, eps: N) -> Result, &'static str> + where + DefaultAllocator: Allocator, + { SVD::new(self.clone_owned(), true, true).pseudo_inverse(eps) } } diff --git a/src/linalg/symmetric_eigen.rs b/src/linalg/symmetric_eigen.rs index bf4ac8c2..cd455b7c 100644 --- a/src/linalg/symmetric_eigen.rs +++ b/src/linalg/symmetric_eigen.rs @@ -18,17 +18,21 @@ use linalg::SymmetricTridiagonal; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator, VectorN: Serialize, - MatrixN: Serialize")) + MatrixN: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator, VectorN: Deserialize<'de>, - MatrixN: Deserialize<'de>")) + MatrixN: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct SymmetricEigen @@ -46,8 +50,7 @@ where DefaultAllocator: Allocator + Allocator, MatrixN: Copy, VectorN: Copy, -{ -} +{} impl SymmetricEigen where DefaultAllocator: Allocator + Allocator diff --git a/src/linalg/symmetric_tridiagonal.rs b/src/linalg/symmetric_tridiagonal.rs index e1ed5ba5..5ac75b47 100644 --- a/src/linalg/symmetric_tridiagonal.rs +++ b/src/linalg/symmetric_tridiagonal.rs @@ -13,17 +13,21 @@ use linalg::householder; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", - serde(bound(serialize = "DefaultAllocator: Allocator + + serde(bound( + serialize = "DefaultAllocator: Allocator + Allocator>, MatrixN: Serialize, - VectorN>: Serialize")) + VectorN>: Serialize" + )) )] #[cfg_attr( feature = "serde-serialize", - serde(bound(deserialize = "DefaultAllocator: Allocator + + serde(bound( + deserialize = "DefaultAllocator: Allocator + Allocator>, MatrixN: Deserialize<'de>, - VectorN>: Deserialize<'de>")) + VectorN>: Deserialize<'de>" + )) )] #[derive(Clone, Debug)] pub struct SymmetricTridiagonal> @@ -38,8 +42,7 @@ where DefaultAllocator: Allocator + Allocator>, MatrixN: Copy, VectorN>: Copy, -{ -} +{} impl> SymmetricTridiagonal where DefaultAllocator: Allocator + Allocator> diff --git a/tests/core/edition.rs b/tests/core/edition.rs index edaf2f08..37452a2d 100644 --- a/tests/core/edition.rs +++ b/tests/core/edition.rs @@ -565,4 +565,4 @@ fn resize_empty_matrix() { assert_eq!(m1, m5.resize(0, 0, 42)); assert_eq!(m1, m6.resize(0, 0, 42)); assert_eq!(m1, m7.resize(0, 0, 42)); -} \ No newline at end of file +} diff --git a/tests/core/matrix.rs b/tests/core/matrix.rs index 9cf2a66b..9c6d468a 100644 --- a/tests/core/matrix.rs +++ b/tests/core/matrix.rs @@ -195,10 +195,10 @@ fn from_columns() { #[test] fn from_columns_dynamic() { let columns = &[ - DVector::from_row_slice(3, &[11, 21, 31]), - DVector::from_row_slice(3, &[12, 22, 32]), - DVector::from_row_slice(3, &[13, 23, 33]), - DVector::from_row_slice(3, &[14, 24, 34]), + DVector::from_row_slice(&[11, 21, 31]), + DVector::from_row_slice(&[12, 22, 32]), + DVector::from_row_slice(&[13, 23, 33]), + DVector::from_row_slice(&[14, 24, 34]), ]; let expected = DMatrix::from_row_slice(3, 4, &[11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34]); @@ -233,8 +233,8 @@ fn from_not_enough_columns() { #[should_panic] fn from_rows_with_different_dimensions() { let columns = &[ - DVector::from_row_slice(3, &[11, 21, 31]), - DVector::from_row_slice(3, &[12, 22, 32, 33]), + DVector::from_row_slice(&[11, 21, 31]), + DVector::from_row_slice(&[12, 22, 32, 33]), ]; let _ = DMatrix::from_columns(columns); @@ -272,11 +272,31 @@ fn to_homogeneous() { let a = Vector3::new(1.0, 2.0, 3.0); let expected_a = Vector4::new(1.0, 2.0, 3.0, 0.0); - let b = DVector::from_row_slice(3, &[1.0, 2.0, 3.0]); - let expected_b = DVector::from_row_slice(4, &[1.0, 2.0, 3.0, 0.0]); + let b = DVector::from_row_slice(&[1.0, 2.0, 3.0]); + let expected_b = DVector::from_row_slice(&[1.0, 2.0, 3.0, 0.0]); + + let c = Matrix2::new(1.0, 2.0, 3.0, 4.0); + let expected_c = Matrix3::new(1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 1.0); + + let d = DMatrix::from_row_slice(2, 2, &[1.0, 2.0, 3.0, 4.0]); + let expected_d = DMatrix::from_row_slice(3, 3, &[1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 1.0]); assert_eq!(a.to_homogeneous(), expected_a); assert_eq!(b.to_homogeneous(), expected_b); + assert_eq!(c.to_homogeneous(), expected_c); + assert_eq!(d.to_homogeneous(), expected_d); +} + +#[test] +fn push() { + let a = Vector3::new(1.0, 2.0, 3.0); + let expected_a = Vector4::new(1.0, 2.0, 3.0, 4.0); + + let b = DVector::from_row_slice(&[1.0, 2.0, 3.0]); + let expected_b = DVector::from_row_slice(&[1.0, 2.0, 3.0, 4.0]); + + assert_eq!(a.push(4.0), expected_a); + assert_eq!(b.push(4.0), expected_b); } #[test] diff --git a/tests/geometry/isometry.rs b/tests/geometry/isometry.rs index 936f4a09..a0e00272 100644 --- a/tests/geometry/isometry.rs +++ b/tests/geometry/isometry.rs @@ -34,7 +34,7 @@ quickcheck!( } fn observer_frame_3(eye: Point3, target: Point3, up: Vector3) -> bool { - let observer = Isometry3::new_observer_frame(&eye, &target, &up); + let observer = Isometry3::face_towards(&eye, &target, &up); let origin = Point3::origin(); relative_eq!(observer * origin, eye, epsilon = 1.0e-7) diff --git a/tests/geometry/point.rs b/tests/geometry/point.rs index ad990048..896a09d6 100644 --- a/tests/geometry/point.rs +++ b/tests/geometry/point.rs @@ -1,7 +1,13 @@ -#![cfg(feature = "arbitrary")] use na::{Point3, Vector3, Vector4}; use num::Zero; +#[test] +fn point_clone() { + let p = Point3::new(1.0, 2.0, 3.0); + let p2 = p.clone(); + assert_eq!(p, p2); +} + #[test] fn point_ops() { let a = Point3::new(1.0, 2.0, 3.0); @@ -87,6 +93,7 @@ fn to_homogeneous() { assert_eq!(a.to_homogeneous(), expected); } +#[cfg(feature = "arbitrary")] quickcheck!( fn point_sub(pt1: Point3, pt2: Point3) -> bool { let dpt = &pt2 - &pt1; diff --git a/tests/geometry/projection.rs b/tests/geometry/projection.rs index 17d04a13..0f38bc37 100644 --- a/tests/geometry/projection.rs +++ b/tests/geometry/projection.rs @@ -5,7 +5,7 @@ fn perspective_inverse() { let proj = Perspective3::new(800.0 / 600.0, 3.14 / 2.0, 1.0, 1000.0); let inv = proj.inverse(); - let id = inv * proj.unwrap(); + let id = inv * proj.into_inner(); assert!(id.is_identity(1.0e-7)); } @@ -15,7 +15,7 @@ fn orthographic_inverse() { let proj = Orthographic3::new(1.0, 2.0, -3.0, -2.5, 10.0, 900.0); let inv = proj.inverse(); - let id = inv * proj.unwrap(); + let id = inv * proj.into_inner(); assert!(id.is_identity(1.0e-7)); } diff --git a/tests/geometry/quaternion.rs b/tests/geometry/quaternion.rs index 7d903450..4dd3da62 100644 --- a/tests/geometry/quaternion.rs +++ b/tests/geometry/quaternion.rs @@ -10,15 +10,15 @@ quickcheck!( * */ fn from_euler_angles(r: f64, p: f64, y: f64) -> bool { - let roll = UnitQuaternion::from_euler_angles(r, 0.0, 0.0); + let roll = UnitQuaternion::from_euler_angles(r, 0.0, 0.0); let pitch = UnitQuaternion::from_euler_angles(0.0, p, 0.0); - let yaw = UnitQuaternion::from_euler_angles(0.0, 0.0, y); + let yaw = UnitQuaternion::from_euler_angles(0.0, 0.0, y); let rpy = UnitQuaternion::from_euler_angles(r, p, y); - let rroll = roll.to_rotation_matrix(); + let rroll = roll.to_rotation_matrix(); let rpitch = pitch.to_rotation_matrix(); - let ryaw = yaw.to_rotation_matrix(); + let ryaw = yaw.to_rotation_matrix(); relative_eq!(rroll[(0, 0)], 1.0, epsilon = 1.0e-7) && // rotation wrt. x axis. relative_eq!(rpitch[(1, 1)], 1.0, epsilon = 1.0e-7) && // rotation wrt. y axis. @@ -26,27 +26,25 @@ quickcheck!( relative_eq!(yaw * pitch * roll, rpy, epsilon = 1.0e-7) } - fn to_euler_angles(r: f64, p: f64, y: f64) -> bool { + fn euler_angles(r: f64, p: f64, y: f64) -> bool { let rpy = UnitQuaternion::from_euler_angles(r, p, y); - let (roll, pitch, yaw) = rpy.to_euler_angles(); - relative_eq!( - UnitQuaternion::from_euler_angles(roll, pitch, yaw), - rpy, - epsilon = 1.0e-7 - ) + let (roll, pitch, yaw) = rpy.euler_angles(); + relative_eq!(UnitQuaternion::from_euler_angles(roll, pitch, yaw), rpy, epsilon = 1.0e-7) } + /* * * From/to rotation matrix. * */ fn unit_quaternion_rotation_conversion(q: UnitQuaternion) -> bool { - let r = q.to_rotation_matrix(); + let r = q.to_rotation_matrix(); let qq = UnitQuaternion::from_rotation_matrix(&r); let rr = qq.to_rotation_matrix(); - relative_eq!(q, qq, epsilon = 1.0e-7) && relative_eq!(r, rr, epsilon = 1.0e-7) + relative_eq!(q, qq, epsilon = 1.0e-7) && + relative_eq!(r, rr, epsilon = 1.0e-7) } /* @@ -54,26 +52,23 @@ quickcheck!( * Point/Vector transformation. * */ - fn unit_quaternion_transformation( - q: UnitQuaternion, - v: Vector3, - p: Point3, - ) -> bool - { + fn unit_quaternion_transformation(q: UnitQuaternion, v: Vector3, p: Point3) -> bool { let r = q.to_rotation_matrix(); let rv = r * v; let rp = r * p; - relative_eq!(q * v, rv, epsilon = 1.0e-7) - && relative_eq!(q * &v, rv, epsilon = 1.0e-7) - && relative_eq!(&q * v, rv, epsilon = 1.0e-7) - && relative_eq!(&q * &v, rv, epsilon = 1.0e-7) - && relative_eq!(q * p, rp, epsilon = 1.0e-7) - && relative_eq!(q * &p, rp, epsilon = 1.0e-7) - && relative_eq!(&q * p, rp, epsilon = 1.0e-7) - && relative_eq!(&q * &p, rp, epsilon = 1.0e-7) + relative_eq!( q * v, rv, epsilon = 1.0e-7) && + relative_eq!( q * &v, rv, epsilon = 1.0e-7) && + relative_eq!(&q * v, rv, epsilon = 1.0e-7) && + relative_eq!(&q * &v, rv, epsilon = 1.0e-7) && + + relative_eq!( q * p, rp, epsilon = 1.0e-7) && + relative_eq!( q * &p, rp, epsilon = 1.0e-7) && + relative_eq!(&q * p, rp, epsilon = 1.0e-7) && + relative_eq!(&q * &p, rp, epsilon = 1.0e-7) } + /* * * Inversion. @@ -81,14 +76,15 @@ quickcheck!( */ fn unit_quaternion_inv(q: UnitQuaternion) -> bool { let iq = q.inverse(); - relative_eq!(&iq * &q, UnitQuaternion::identity(), epsilon = 1.0e-7) - && relative_eq!(iq * &q, UnitQuaternion::identity(), epsilon = 1.0e-7) - && relative_eq!(&iq * q, UnitQuaternion::identity(), epsilon = 1.0e-7) - && relative_eq!(iq * q, UnitQuaternion::identity(), epsilon = 1.0e-7) - && relative_eq!(&q * &iq, UnitQuaternion::identity(), epsilon = 1.0e-7) - && relative_eq!(q * &iq, UnitQuaternion::identity(), epsilon = 1.0e-7) - && relative_eq!(&q * iq, UnitQuaternion::identity(), epsilon = 1.0e-7) - && relative_eq!(q * iq, UnitQuaternion::identity(), epsilon = 1.0e-7) + relative_eq!(&iq * &q, UnitQuaternion::identity(), epsilon = 1.0e-7) && + relative_eq!( iq * &q, UnitQuaternion::identity(), epsilon = 1.0e-7) && + relative_eq!(&iq * q, UnitQuaternion::identity(), epsilon = 1.0e-7) && + relative_eq!( iq * q, UnitQuaternion::identity(), epsilon = 1.0e-7) && + + relative_eq!(&q * &iq, UnitQuaternion::identity(), epsilon = 1.0e-7) && + relative_eq!( q * &iq, UnitQuaternion::identity(), epsilon = 1.0e-7) && + relative_eq!(&q * iq, UnitQuaternion::identity(), epsilon = 1.0e-7) && + relative_eq!( q * iq, UnitQuaternion::identity(), epsilon = 1.0e-7) } /* @@ -120,34 +116,28 @@ quickcheck!( // Test that all operators (incl. all combinations of references) work. // See the top comment on `geometry/quaternion_ops.rs` for details on which operations are // supported. - fn all_op_exist( - q: Quaternion, - uq: UnitQuaternion, - v: Vector3, - p: Point3, - r: Rotation3, - s: f64, - ) -> bool - { + fn all_op_exist(q: Quaternion, uq: UnitQuaternion, + v: Vector3, p: Point3, r: Rotation3, + s: f64) -> bool { let uv = Unit::new_normalize(v); let qpq = q + q; let qmq = q - q; let qMq = q * q; - let mq = -q; + let mq = -q; let qMs = q * s; let qDs = q / s; let sMq = s * q; let uqMuq = uq * uq; - let uqMr = uq * r; - let rMuq = r * uq; + let uqMr = uq * r; + let rMuq = r * uq; let uqDuq = uq / uq; - let uqDr = uq / r; - let rDuq = r / uq; + let uqDr = uq / r; + let rDuq = r / uq; - let uqMp = uq * p; - let uqMv = uq * v; + let uqMp = uq * p; + let uqMv = uq * v; let uqMuv = uq * uv; let mut qMs1 = q; @@ -196,60 +186,81 @@ quickcheck!( uqDr1 /= r; uqDr2 /= &r; - qMs1 == qMs - && qMq1 == qMq - && qMq1 == qMq2 - && qpq1 == qpq - && qpq1 == qpq2 - && qmq1 == qmq - && qmq1 == qmq2 - && uqMuq1 == uqMuq - && uqMuq1 == uqMuq2 - && uqMr1 == uqMr - && uqMr1 == uqMr2 - && uqDuq1 == uqDuq - && uqDuq1 == uqDuq2 - && uqDr1 == uqDr - && uqDr1 == uqDr2 - && qpq == &q + &q - && qpq == q + &q - && qpq == &q + q - && qmq == &q - &q - && qmq == q - &q - && qmq == &q - q - && qMq == &q * &q - && qMq == q * &q - && qMq == &q * q - && mq == -&q - && qMs == &q * s - && qDs == &q / s - && sMq == s * &q - && uqMuq == &uq * &uq - && uqMuq == uq * &uq - && uqMuq == &uq * uq - && uqMr == &uq * &r - && uqMr == uq * &r - && uqMr == &uq * r - && rMuq == &r * &uq - && rMuq == r * &uq - && rMuq == &r * uq - && uqDuq == &uq / &uq - && uqDuq == uq / &uq - && uqDuq == &uq / uq - && uqDr == &uq / &r - && uqDr == uq / &r - && uqDr == &uq / r - && rDuq == &r / &uq - && rDuq == r / &uq - && rDuq == &r / uq - && uqMp == &uq * &p - && uqMp == uq * &p - && uqMp == &uq * p - && uqMv == &uq * &v - && uqMv == uq * &v - && uqMv == &uq * v - && uqMuv == &uq * &uv - && uqMuv == uq * &uv - && uqMuv == &uq * uv + qMs1 == qMs && + + qMq1 == qMq && + qMq1 == qMq2 && + + qpq1 == qpq && + qpq1 == qpq2 && + + qmq1 == qmq && + qmq1 == qmq2 && + + uqMuq1 == uqMuq && + uqMuq1 == uqMuq2 && + + uqMr1 == uqMr && + uqMr1 == uqMr2 && + + uqDuq1 == uqDuq && + uqDuq1 == uqDuq2 && + + uqDr1 == uqDr && + uqDr1 == uqDr2 && + + qpq == &q + &q && + qpq == q + &q && + qpq == &q + q && + + qmq == &q - &q && + qmq == q - &q && + qmq == &q - q && + + qMq == &q * &q && + qMq == q * &q && + qMq == &q * q && + + mq == -&q && + + qMs == &q * s && + qDs == &q / s && + sMq == s * &q && + + uqMuq == &uq * &uq && + uqMuq == uq * &uq && + uqMuq == &uq * uq && + + uqMr == &uq * &r && + uqMr == uq * &r && + uqMr == &uq * r && + + rMuq == &r * &uq && + rMuq == r * &uq && + rMuq == &r * uq && + + uqDuq == &uq / &uq && + uqDuq == uq / &uq && + uqDuq == &uq / uq && + + uqDr == &uq / &r && + uqDr == uq / &r && + uqDr == &uq / r && + + rDuq == &r / &uq && + rDuq == r / &uq && + rDuq == &r / uq && + + uqMp == &uq * &p && + uqMp == uq * &p && + uqMp == &uq * p && + + uqMv == &uq * &v && + uqMv == uq * &v && + uqMv == &uq * v && + + uqMuv == &uq * &uv && + uqMuv == uq * &uv && + uqMuv == &uq * uv } ); diff --git a/tests/geometry/rotation.rs b/tests/geometry/rotation.rs index 9bd3e590..e4b1f9d7 100644 --- a/tests/geometry/rotation.rs +++ b/tests/geometry/rotation.rs @@ -1,4 +1,4 @@ -use na::{Vector2, Vector3}; +use na::{Quaternion, Real, UnitQuaternion, Vector2, Vector3}; #[test] fn angle_2() { @@ -16,6 +16,20 @@ fn angle_3() { assert_eq!(a.angle(&b), 0.0); } +#[test] +fn quaternion_euler_angles_issue_494() { + let quat = UnitQuaternion::from_quaternion(Quaternion::new( + -0.10405792, + -0.6993922f32, + -0.10406871, + 0.69942284, + )); + let angs = quat.euler_angles(); + assert_eq!(angs.0, 2.8461843); + assert_eq!(angs.1, f32::frac_pi_2()); + assert_eq!(angs.2, 0.0); +} + #[cfg(feature = "arbitrary")] mod quickcheck_tests { use alga::general::Real; @@ -41,17 +55,17 @@ mod quickcheck_tests { yaw * pitch * roll == rpy } - fn to_euler_angles(r: f64, p: f64, y: f64) -> bool { + fn euler_angles(r: f64, p: f64, y: f64) -> bool { let rpy = Rotation3::from_euler_angles(r, p, y); - let (roll, pitch, yaw) = rpy.to_euler_angles(); + let (roll, pitch, yaw) = rpy.euler_angles(); relative_eq!(Rotation3::from_euler_angles(roll, pitch, yaw), rpy, epsilon = 1.0e-7) } - fn to_euler_angles_gimble_lock(r: f64, y: f64) -> bool { + fn euler_angles_gimble_lock(r: f64, y: f64) -> bool { let pos = Rotation3::from_euler_angles(r, f64::frac_pi_2(), y); let neg = Rotation3::from_euler_angles(r, -f64::frac_pi_2(), y); - let (pos_r, pos_p, pos_y) = pos.to_euler_angles(); - let (neg_r, neg_p, neg_y) = neg.to_euler_angles(); + let (pos_r, pos_p, pos_y) = pos.euler_angles(); + let (neg_r, neg_p, neg_y) = neg.euler_angles(); relative_eq!(Rotation3::from_euler_angles(pos_r, pos_p, pos_y), pos, epsilon = 1.0e-7) && relative_eq!(Rotation3::from_euler_angles(neg_r, neg_p, neg_y), neg, epsilon = 1.0e-7) } diff --git a/tests/linalg/eigen.rs b/tests/linalg/eigen.rs index f609292a..36855acf 100644 --- a/tests/linalg/eigen.rs +++ b/tests/linalg/eigen.rs @@ -1,3 +1,5 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + use na::DMatrix; #[cfg(feature = "arbitrary")] @@ -66,41 +68,30 @@ fn symmetric_eigen_singular_24x24() { 24, 24, &[ - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, - 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, - -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, - 0.0, 1.0, 1.0, 1.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, - 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, - 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, - -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, - -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, + 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], ); diff --git a/tests/linalg/full_piv_lu.rs b/tests/linalg/full_piv_lu.rs index 06d87156..5a0ad75b 100644 --- a/tests/linalg/full_piv_lu.rs +++ b/tests/linalg/full_piv_lu.rs @@ -459,4 +459,4 @@ fn resize() { assert_eq!(add_del, m.resize(5, 2, 42)); assert_eq!(del_add, m.resize(1, 8, 42)); } -*/ \ No newline at end of file +*/ diff --git a/tests/linalg/svd.rs b/tests/linalg/svd.rs index 629b404d..e84108ed 100644 --- a/tests/linalg/svd.rs +++ b/tests/linalg/svd.rs @@ -1,3 +1,4 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] use na::{DMatrix, Matrix6}; #[cfg(feature = "arbitrary")] @@ -11,7 +12,7 @@ mod quickcheck_tests { fn svd(m: DMatrix) -> bool { if m.len() > 0 { let svd = m.clone().svd(true, true); - let recomp_m = svd.clone().recompose(); + let recomp_m = svd.clone().recompose().unwrap(); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = DMatrix::from_diagonal(&s); @@ -92,7 +93,7 @@ mod quickcheck_tests { fn svd_pseudo_inverse(m: DMatrix) -> bool { if m.len() > 0 { let svd = m.clone().svd(true, true); - let pinv = svd.pseudo_inverse(1.0e-10); + let pinv = svd.pseudo_inverse(1.0e-10).unwrap(); if m.nrows() > m.ncols() { println!("{}", &pinv * &m); @@ -119,10 +120,10 @@ mod quickcheck_tests { let b1 = DVector::new_random(n); let b2 = DMatrix::new_random(n, nb); - let sol1 = svd.solve(&b1, 1.0e-7); - let sol2 = svd.solve(&b2, 1.0e-7); + let sol1 = svd.solve(&b1, 1.0e-7).unwrap(); + let sol2 = svd.solve(&b2, 1.0e-7).unwrap(); - let recomp = svd.recompose(); + let recomp = svd.recompose().unwrap(); if !relative_eq!(m, recomp, epsilon = 1.0e-6) { println!("{}{}", m, recomp); } @@ -145,47 +146,31 @@ mod quickcheck_tests { // Test proposed on the issue #176 of rulinalg. #[test] fn svd_singular() { - let m = DMatrix::from_row_slice( - 24, - 24, - &[ - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, - 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, - -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, - 0.0, 1.0, 1.0, 1.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, - 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, - 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, - -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, - -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, - ], - ); + let m = DMatrix::from_row_slice(24, 24, &[ + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, + 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0]); let svd = m.clone().svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); @@ -202,48 +187,33 @@ fn svd_singular() { // Same as the previous test but with one additional row. #[test] fn svd_singular_vertical() { - let m = DMatrix::from_row_slice( - 25, - 24, - &[ - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, - 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, - -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, - 0.0, 1.0, 1.0, 1.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, - 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, - 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, - -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, - -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, - 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, - ], - ); + let m = DMatrix::from_row_slice(25, 24, &[ + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, + 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0]); + let svd = m.clone().svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); @@ -256,48 +226,31 @@ fn svd_singular_vertical() { // Same as the previous test but with one additional column. #[test] fn svd_singular_horizontal() { - let m = DMatrix::from_row_slice( - 24, - 25, - &[ - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, - 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, - -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, - 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, - 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, - 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, - -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, - 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, - 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - ], - ); + let m = DMatrix::from_row_slice(24, 25, &[ + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, + 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]); let svd = m.clone().svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); @@ -311,126 +264,81 @@ fn svd_singular_horizontal() { fn svd_zeros() { let m = DMatrix::from_element(10, 10, 0.0); let svd = m.clone().svd(true, true); - assert_eq!(m, svd.recompose()); + assert_eq!(Ok(m), svd.recompose()); } #[test] fn svd_identity() { let m = DMatrix::::identity(10, 10); let svd = m.clone().svd(true, true); - assert_eq!(m, svd.recompose()); + assert_eq!(Ok(m), svd.recompose()); let m = DMatrix::::identity(10, 15); let svd = m.clone().svd(true, true); - assert_eq!(m, svd.recompose()); + assert_eq!(Ok(m), svd.recompose()); let m = DMatrix::::identity(15, 10); let svd = m.clone().svd(true, true); - assert_eq!(m, svd.recompose()); + assert_eq!(Ok(m), svd.recompose()); } #[test] fn svd_with_delimited_subproblem() { let mut m = DMatrix::::from_element(10, 10, 0.0); - m[(0, 0)] = 1.0; - m[(0, 1)] = 2.0; - m[(1, 1)] = 0.0; - m[(1, 2)] = 3.0; - m[(2, 2)] = 4.0; - m[(2, 3)] = 5.0; - m[(3, 3)] = 6.0; - m[(3, 4)] = 0.0; - m[(4, 4)] = 8.0; - m[(3, 5)] = 9.0; - m[(5, 5)] = 10.0; - m[(3, 6)] = 11.0; - m[(6, 6)] = 12.0; - m[(3, 7)] = 12.0; - m[(7, 7)] = 14.0; - m[(3, 8)] = 13.0; - m[(8, 8)] = 16.0; - m[(3, 9)] = 17.0; - m[(9, 9)] = 18.0; + m[(0,0)] = 1.0; m[(0,1)] = 2.0; + m[(1,1)] = 0.0; m[(1,2)] = 3.0; + m[(2,2)] = 4.0; m[(2,3)] = 5.0; + m[(3,3)] = 6.0; m[(3,4)] = 0.0; + m[(4,4)] = 8.0; m[(3,5)] = 9.0; + m[(5,5)] = 10.0; m[(3,6)] = 11.0; + m[(6,6)] = 12.0; m[(3,7)] = 12.0; + m[(7,7)] = 14.0; m[(3,8)] = 13.0; + m[(8,8)] = 16.0; m[(3,9)] = 17.0; + m[(9,9)] = 18.0; let svd = m.clone().svd(true, true); - assert!(relative_eq!(m, svd.recompose(), epsilon = 1.0e-7)); + assert!(relative_eq!(m, svd.recompose().unwrap(), epsilon = 1.0e-7)); // Rectangular versions. let mut m = DMatrix::::from_element(15, 10, 0.0); - m[(0, 0)] = 1.0; - m[(0, 1)] = 2.0; - m[(1, 1)] = 0.0; - m[(1, 2)] = 3.0; - m[(2, 2)] = 4.0; - m[(2, 3)] = 5.0; - m[(3, 3)] = 6.0; - m[(3, 4)] = 0.0; - m[(4, 4)] = 8.0; - m[(3, 5)] = 9.0; - m[(5, 5)] = 10.0; - m[(3, 6)] = 11.0; - m[(6, 6)] = 12.0; - m[(3, 7)] = 12.0; - m[(7, 7)] = 14.0; - m[(3, 8)] = 13.0; - m[(8, 8)] = 16.0; - m[(3, 9)] = 17.0; - m[(9, 9)] = 18.0; + m[(0,0)] = 1.0; m[(0,1)] = 2.0; + m[(1,1)] = 0.0; m[(1,2)] = 3.0; + m[(2,2)] = 4.0; m[(2,3)] = 5.0; + m[(3,3)] = 6.0; m[(3,4)] = 0.0; + m[(4,4)] = 8.0; m[(3,5)] = 9.0; + m[(5,5)] = 10.0; m[(3,6)] = 11.0; + m[(6,6)] = 12.0; m[(3,7)] = 12.0; + m[(7,7)] = 14.0; m[(3,8)] = 13.0; + m[(8,8)] = 16.0; m[(3,9)] = 17.0; + m[(9,9)] = 18.0; let svd = m.clone().svd(true, true); - assert!(relative_eq!(m, svd.recompose(), epsilon = 1.0e-7)); + assert!(relative_eq!(m, svd.recompose().unwrap(), epsilon = 1.0e-7)); let svd = m.transpose().svd(true, true); - assert!(relative_eq!( - m.transpose(), - svd.recompose(), - epsilon = 1.0e-7 - )); + assert!(relative_eq!(m.transpose(), svd.recompose().unwrap(), epsilon = 1.0e-7)); } #[test] fn svd_fail() { let m = Matrix6::new( - 0.9299319121545955, - 0.9955870335651049, - 0.8824725266413644, - 0.28966880207132295, - 0.06102723649846409, - 0.9311880746048009, - 0.5938395242304351, - 0.8398522876024204, - 0.06672831951963198, - 0.9941213119963099, - 0.9431846038057834, - 0.8159885168706427, - 0.9121962883152357, - 0.6471119669367571, - 0.4823309702814407, - 0.6420516076705516, - 0.7731203925207113, - 0.7424069470756647, - 0.07311092531259344, - 0.5579247949052946, - 0.14518764691585773, - 0.03502980663114896, - 0.7991329455957719, - 0.4929930019965745, - 0.12293810556077789, - 0.6617084679545999, - 0.9002240700227326, - 0.027153062135304884, - 0.3630189466989524, - 0.18207502727558866, - 0.843196731466686, - 0.08951878746549924, - 0.7533450877576973, - 0.009558876499740077, - 0.9429679490873482, - 0.9355764454129878, - ); + 0.9299319121545955, 0.9955870335651049, 0.8824725266413644, 0.28966880207132295, 0.06102723649846409, 0.9311880746048009, + 0.5938395242304351, 0.8398522876024204, 0.06672831951963198, 0.9941213119963099, 0.9431846038057834, 0.8159885168706427, + 0.9121962883152357, 0.6471119669367571, 0.4823309702814407, 0.6420516076705516, 0.7731203925207113, 0.7424069470756647, + 0.07311092531259344, 0.5579247949052946, 0.14518764691585773, 0.03502980663114896, 0.7991329455957719, 0.4929930019965745, + 0.12293810556077789, 0.6617084679545999, 0.9002240700227326, 0.027153062135304884, 0.3630189466989524, 0.18207502727558866, + 0.843196731466686, 0.08951878746549924, 0.7533450877576973, 0.009558876499740077, 0.9429679490873482, 0.9355764454129878); let svd = m.clone().svd(true, true); println!("Singular values: {}", svd.singular_values); println!("u: {:.5}", svd.u.unwrap()); println!("v: {:.5}", svd.v_t.unwrap()); - let recomp = svd.recompose(); + let recomp = svd.recompose().unwrap(); println!("{:.5}{:.5}", m, recomp); assert!(relative_eq!(m, recomp, epsilon = 1.0e-5)); } + +#[test] +fn svd_err() { + let m = DMatrix::from_element(10, 10, 0.0); + let svd = m.clone().svd(false, false); + assert_eq!(Err("SVD recomposition: U and V^t have not been computed."), svd.clone().recompose()); + assert_eq!(Err("SVD pseudo inverse: the epsilon must be non-negative."), svd.clone().pseudo_inverse(-1.0)); +}