From 85a64fb517c9783663ba1c241092fa7b2060276f Mon Sep 17 00:00:00 2001
From: Jenan Wise
Date: Mon, 22 Jun 2020 15:29:13 -0700
Subject: [PATCH 01/55] More verbose DMatrix dim asserts where possible.
Previously, most dimension mismatch asserts used raw `assert!` and did
not include the mismatching dimensions in the panic message. When using
dynamic matrices, this led to somewhat-opaque panics such as:
```rust
let m1 = DMatrix::::zeros(2, 3);
let m2 = DMatrix::::zeros(5, 10);
m1 + m2 // panic: Matrix addition/subtraction dimensions mismatch.
```
This patch adds dimension information in the panic messages wherever
doing so did not add additional bounds checks, mostly by simply changing
`assert!(a == b, ...)` cases to `assert_eq!`. After:
```rust
// panic: assertion failed: `(left == right)`
// left: `(2, 3)`,
// right: `(5, 10)`: Matrix addition/subtraction dimensions mismatch.
```
Note that the `gemv` and `ger` were not updated, as they are called from
within other functions on subset matricies -- e.g., `gemv` is called
from `gemm` which is called from `mul_to` . Including dimension
information in the `gemv` panic messages would be confusing to
`mul` / `mul_to` users, because it would include dimensions of the column
vectors that `gemm` passes to `gemv` rather than of the original `mul`
arguments. A fix would be to add bounds checks to `mul_to`, but that may
have performance and redundancy implications, so is left to another
patch.
---
src/base/blas.rs | 9 ++++--
src/base/matrix.rs | 70 ++++++++++++++++++++++++++++------------------
src/base/ops.rs | 28 +++++++++++++------
3 files changed, 68 insertions(+), 39 deletions(-)
diff --git a/src/base/blas.rs b/src/base/blas.rs
index add17af2..1d61b9d2 100644
--- a/src/base/blas.rs
+++ b/src/base/blas.rs
@@ -284,7 +284,9 @@ where
{
assert!(
self.nrows() == rhs.nrows(),
- "Dot product dimensions mismatch."
+ "Dot product dimensions mismatch for shapes {:?} and {:?}: left rows != right rows.",
+ self.shape(),
+ rhs.shape(),
);
// So we do some special cases for common fixed-size vectors of dimension lower than 8
@@ -496,8 +498,9 @@ where
ShapeConstraint: DimEq + DimEq,
{
let (nrows, ncols) = self.shape();
- assert!(
- (ncols, nrows) == rhs.shape(),
+ assert_eq!(
+ (ncols, nrows),
+ rhs.shape(),
"Transposed dot product dimension mismatch."
);
diff --git a/src/base/matrix.rs b/src/base/matrix.rs
index e4821bf8..d30773a3 100644
--- a/src/base/matrix.rs
+++ b/src/base/matrix.rs
@@ -538,8 +538,9 @@ impl> Matrix {
let mut res = unsafe { MatrixMN::new_uninitialized_generic(nrows, ncols) };
- assert!(
- (nrows.value(), ncols.value()) == rhs.shape(),
+ assert_eq!(
+ (nrows.value(), ncols.value()),
+ rhs.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
@@ -578,9 +579,14 @@ impl> Matrix {
let mut res = unsafe { MatrixMN::new_uninitialized_generic(nrows, ncols) };
- assert!(
- (nrows.value(), ncols.value()) == b.shape()
- && (nrows.value(), ncols.value()) == c.shape(),
+ assert_eq!(
+ (nrows.value(), ncols.value()),
+ b.shape(),
+ "Matrix simultaneous traversal error: dimension mismatch."
+ );
+ assert_eq!(
+ (nrows.value(), ncols.value()),
+ c.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
@@ -636,8 +642,9 @@ impl> Matrix {
let mut res = init;
- assert!(
- (nrows.value(), ncols.value()) == rhs.shape(),
+ assert_eq!(
+ (nrows.value(), ncols.value()),
+ rhs.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
@@ -884,8 +891,9 @@ impl> Matrix {
{
let (nrows, ncols) = self.shape();
- assert!(
- (nrows, ncols) == rhs.shape(),
+ assert_eq!(
+ (nrows, ncols),
+ rhs.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
@@ -922,12 +930,14 @@ impl> Matrix {
{
let (nrows, ncols) = self.shape();
- assert!(
- (nrows, ncols) == b.shape(),
+ assert_eq!(
+ (nrows, ncols),
+ b.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
- assert!(
- (nrows, ncols) == c.shape(),
+ assert_eq!(
+ (nrows, ncols),
+ c.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
@@ -1427,8 +1437,9 @@ where
#[inline]
fn lt(&self, right: &Self) -> bool {
- assert!(
- self.shape() == right.shape(),
+ assert_eq!(
+ self.shape(),
+ right.shape(),
"Matrix comparison error: dimensions mismatch."
);
self.iter().zip(right.iter()).all(|(a, b)| a.lt(b))
@@ -1436,8 +1447,9 @@ where
#[inline]
fn le(&self, right: &Self) -> bool {
- assert!(
- self.shape() == right.shape(),
+ assert_eq!(
+ self.shape(),
+ right.shape(),
"Matrix comparison error: dimensions mismatch."
);
self.iter().zip(right.iter()).all(|(a, b)| a.le(b))
@@ -1445,8 +1457,9 @@ where
#[inline]
fn gt(&self, right: &Self) -> bool {
- assert!(
- self.shape() == right.shape(),
+ assert_eq!(
+ self.shape(),
+ right.shape(),
"Matrix comparison error: dimensions mismatch."
);
self.iter().zip(right.iter()).all(|(a, b)| a.gt(b))
@@ -1454,8 +1467,9 @@ where
#[inline]
fn ge(&self, right: &Self) -> bool {
- assert!(
- self.shape() == right.shape(),
+ assert_eq!(
+ self.shape(),
+ right.shape(),
"Matrix comparison error: dimensions mismatch."
);
self.iter().zip(right.iter()).all(|(a, b)| a.ge(b))
@@ -1602,7 +1616,11 @@ impl
+ SameNumberOfColumns,
{
- assert!(self.shape() == (2, 1), "2D perpendicular product ");
+ assert!(
+ self.shape() == (2, 1),
+ "2D perpendicular product requires (2, 1) vector but found {:?}",
+ self.shape()
+ );
unsafe {
self.get_unchecked((0, 0)).inlined_clone() * b.get_unchecked((1, 0)).inlined_clone()
@@ -1626,13 +1644,11 @@ impl + SameNumberOfColumns,
{
let shape = self.shape();
- assert!(
- shape == b.shape(),
- "Vector cross product dimension mismatch."
- );
+ assert_eq!(shape, b.shape(), "Vector cross product dimension mismatch.");
assert!(
(shape.0 == 3 && shape.1 == 1) || (shape.0 == 1 && shape.1 == 3),
- "Vector cross product dimension mismatch."
+ "Vector cross product dimension mismatch: must be (3, 1) or (1, 3) but found {:?}.",
+ shape
);
if shape.0 == 3 {
diff --git a/src/base/ops.rs b/src/base/ops.rs
index 12b26d1a..7f0c6e4e 100644
--- a/src/base/ops.rs
+++ b/src/base/ops.rs
@@ -154,8 +154,8 @@ macro_rules! componentwise_binop_impl(
out: &mut Matrix)
where SB: Storage,
SC: StorageMut {
- assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
- assert!(self.shape() == out.shape(), "Matrix addition/subtraction output dimensions mismatch.");
+ assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
+ assert_eq!(self.shape(), out.shape(), "Matrix addition/subtraction output dimensions mismatch.");
// This is the most common case and should be deduced at compile-time.
// FIXME: use specialization instead?
@@ -188,7 +188,7 @@ macro_rules! componentwise_binop_impl(
C2: Dim,
SA: StorageMut,
SB: Storage {
- assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
+ assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
// This is the most common case and should be deduced at compile-time.
// FIXME: use specialization instead?
@@ -218,7 +218,7 @@ macro_rules! componentwise_binop_impl(
where R2: Dim,
C2: Dim,
SB: StorageMut {
- assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
+ assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
// This is the most common case and should be deduced at compile-time.
// FIXME: use specialization instead?
@@ -277,7 +277,7 @@ macro_rules! componentwise_binop_impl(
#[inline]
fn $method(self, rhs: &'b Matrix) -> Self::Output {
- assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
+ assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
let mut res = self.into_owned_sum::();
res.$method_assign_statically_unchecked(rhs);
res
@@ -296,7 +296,7 @@ macro_rules! componentwise_binop_impl(
#[inline]
fn $method(self, rhs: Matrix) -> Self::Output {
let mut rhs = rhs.into_owned_sum::();
- assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
+ assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
self.$method_assign_statically_unchecked_rhs(&mut rhs);
rhs
}
@@ -728,11 +728,21 @@ where
assert!(
nrows1 == nrows2,
- "Matrix multiplication dimensions mismatch."
+ "Matrix multiplication dimensions mismatch {:?} and {:?}: left rows != right rows.",
+ self.shape(),
+ rhs.shape()
);
assert!(
- nrows3 == ncols1 && ncols3 == ncols2,
- "Matrix multiplication output dimensions mismatch."
+ ncols1 == nrows3,
+ "Matrix multiplication output dimensions mismatch {:?} and {:?}: left cols != right rows.",
+ self.shape(),
+ out.shape()
+ );
+ assert!(
+ ncols2 == ncols3,
+ "Matrix multiplication output dimensions mismatch {:?} and {:?}: left cols != right cols",
+ rhs.shape(),
+ out.shape()
);
for i in 0..ncols1 {
From eca17586a62304995fc9d774f6d25ae3bbf0c6c6 Mon Sep 17 00:00:00 2001
From: Jenan Wise
Date: Mon, 22 Jun 2020 19:13:43 -0700
Subject: [PATCH 02/55] Remove rustfmt.toml.
The CI rustfmt checker uses `stable` rust, not `nightly`, but the
config options in `rustfmt.toml` only work on nightly -- on stable,
there are ignored with warnings. This means that devs running stable
get a lot of warnings and devs running nightly will accidentally format
in a way that the CI checker forbids. (specifically, using
`where_single_line`). E.g.:
```
$ cargo +stable fmt -- --check
Warning: can't set `indent_style = Block`, unstable features are only available in nightly channel.
Warning: can't set `where_single_line = true`, unstable features are only available in nightly channel.
...etc
$ cargo +nightly fmt -- --check
-where
- N: RealField,
-{
+where N: RealField {
...etc
```
Just removing the toml fixes this problem.
---
rustfmt.toml | 3 ---
1 file changed, 3 deletions(-)
delete mode 100644 rustfmt.toml
diff --git a/rustfmt.toml b/rustfmt.toml
deleted file mode 100644
index 8dc8e61a..00000000
--- a/rustfmt.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-unstable_features = true
-indent_style = "Block"
-where_single_line = true
\ No newline at end of file
From 7a42513c1ae18317a5c3f971c5bbf6ef9d597eda Mon Sep 17 00:00:00 2001
From: Jenan Wise
Date: Tue, 23 Jun 2020 09:48:23 -0700
Subject: [PATCH 03/55] Add empty rustfmt.toml.
Previous commit removed it entirely, but we should have an empty config
file instead to indicate that we use the base config.
---
rustfmt.toml | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 rustfmt.toml
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 00000000..e69de29b
From f6730dac1fe2c911ba1022e0347fd520f77c2e18 Mon Sep 17 00:00:00 2001
From: Andreas Longva
Date: Sun, 11 Aug 2019 18:26:12 +0200
Subject: [PATCH 04/55] Basic matrixcompare functionality
---
Cargo.toml | 5 +++
examples/matrixcompare.rs | 79 ++++++++++++++++++++++++++++++++++++
src/base/matrix.rs | 24 +++++++++++
tests/core/matrixcompare.rs | 81 +++++++++++++++++++++++++++++++++++++
tests/core/mod.rs | 3 ++
5 files changed, 192 insertions(+)
create mode 100644 examples/matrixcompare.rs
create mode 100644 tests/core/matrixcompare.rs
diff --git a/Cargo.toml b/Cargo.toml
index 4adf2ab1..fe347ed0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,6 +30,7 @@ sparse = [ ]
debug = [ "approx/num-complex", "rand/std" ]
alloc = [ ]
io = [ "pest", "pest_derive" ]
+compare = [ "matrixcompare-core" ]
[dependencies]
typenum = "1.11"
@@ -50,6 +51,7 @@ mint = { version = "0.5", optional = true }
quickcheck = { version = "0.9", optional = true }
pest = { version = "2.0", optional = true }
pest_derive = { version = "2.0", optional = true }
+matrixcompare-core = { version = "0.1", optional = true }
[dev-dependencies]
serde_json = "1.0"
@@ -61,6 +63,9 @@ rand_isaac = "0.2"
### https://github.com/rust-lang/cargo/issues/4866
#criterion = "0.2.10"
+# For matrix comparison macro
+matrixcompare = "0.1"
+
[workspace]
members = [ "nalgebra-lapack", "nalgebra-glm" ]
diff --git a/examples/matrixcompare.rs b/examples/matrixcompare.rs
new file mode 100644
index 00000000..60c632d3
--- /dev/null
+++ b/examples/matrixcompare.rs
@@ -0,0 +1,79 @@
+extern crate nalgebra as na;
+
+#[macro_use]
+extern crate quickcheck;
+
+use na::{U3, U4, MatrixMN};
+use matrixcompare::compare_matrices;
+use matrixcompare::comparators::{ExactElementwiseComparator, AbsoluteElementwiseComparator};
+
+fn compare_integers_fail() {
+ println!("Comparing two integer matrices.");
+
+ let a = MatrixMN::<_, U3, U4>::from_row_slice(&[
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, -2, 11
+ ]);
+
+ let b = MatrixMN::<_, U3, U4>::from_row_slice(&[
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11
+ ]);
+
+ if let Some(msg) = compare_matrices(a, b, &ExactElementwiseComparator).panic_message() {
+ println!("{}", msg);
+ }
+}
+
+fn compare_different_size() {
+ println!("Comparing matrices of different size.");
+ let a = MatrixMN::<_, U3, U3>::from_row_slice(&[
+ 0, 1, 2,
+ 4, 5, 6,
+ 8, 9, 10,
+ ]);
+
+ let b = MatrixMN::<_, U3, U4>::from_row_slice(&[
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11
+ ]);
+
+ if let Some(msg) = compare_matrices(a, b, &ExactElementwiseComparator).panic_message() {
+ println!("{}", msg);
+ }
+}
+
+fn compare_f64_abs_tol_fail() {
+ println!("Comparing two f64 matrices.");
+
+ let a = MatrixMN::::from_row_slice(&[
+ 0.0, 1.0, 2.0 + 1e-10,
+ 4.0, 5.0, 6.0,
+ 8.0, 9.0, 10.0,
+ ]);
+
+ let b = MatrixMN::<_, U3, U3>::from_row_slice(&[
+ 0.0, 1.0, 2.0,
+ 4.0, 5.0, 6.0,
+ 8.0, 9.0, 10.0
+ ]);
+
+ let cmp = AbsoluteElementwiseComparator { tol: 1e-12 };
+ if let Some(msg) = compare_matrices(a, b, &cmp).panic_message() {
+ println!("{}", msg);
+ }
+}
+
+fn main() {
+ // This example mostly serves the purpose of demonstrating the kind of error messages
+ // that are given upon comparison failure.
+ // The more typical use case is using `assert_matrix_eq!` in tests.
+ compare_integers_fail();
+ println!("======================================================");
+ compare_f64_abs_tol_fail();
+ println!("======================================================");
+ compare_different_size();
+}
\ No newline at end of file
diff --git a/src/base/matrix.rs b/src/base/matrix.rs
index d30773a3..8107a1dc 100644
--- a/src/base/matrix.rs
+++ b/src/base/matrix.rs
@@ -159,6 +159,30 @@ impl Abomonation for Matrix>
+ matrixcompare_core::Matrix for Matrix {
+ fn rows(&self) -> usize {
+ self.nrows()
+ }
+
+ fn cols(&self) -> usize {
+ self.ncols()
+ }
+
+ fn access(&self) -> matrixcompare_core::Access {
+ matrixcompare_core::Access::Dense(self)
+ }
+}
+
+#[cfg(feature = "compare")]
+impl>
+ matrixcompare_core::DenseAccess for Matrix {
+ fn fetch_single(&self, row: usize, col: usize) -> N {
+ self.index((row, col)).clone()
+ }
+}
+
impl Matrix {
/// Creates a new matrix with the given data without statically checking that the matrix
/// dimension matches the storage dimension.
diff --git a/tests/core/matrixcompare.rs b/tests/core/matrixcompare.rs
new file mode 100644
index 00000000..d4b6f142
--- /dev/null
+++ b/tests/core/matrixcompare.rs
@@ -0,0 +1,81 @@
+//! Tests for `matrixcompare` integration.
+//!
+//! The `matrixcompare` crate itself is responsible for testing the actual comparison.
+//! The tests here only check that the necessary trait implementations are correctly implemented,
+//! in addition to some sanity checks with example input.
+
+use nalgebra::{U4, U5, MatrixMN, DMatrix};
+
+use matrixcompare::{assert_matrix_eq, DenseAccess};
+
+#[cfg(feature = "arbitrary")]
+quickcheck! {
+ fn fetch_single_is_equivalent_to_index_f64(matrix: DMatrix) -> bool {
+ for i in 0 .. matrix.nrows() {
+ for j in 0 .. matrix.ncols() {
+ if matrix.fetch_single(i, j) != *matrix.index((i, j)) {
+ return false;
+ }
+ }
+ }
+
+ true
+ }
+
+ fn matrixcompare_shape_agrees_with_matrix(matrix: DMatrix) -> bool {
+ matrix.nrows() == as matrixcompare::Matrix>::rows(&matrix)
+ &&
+ matrix.ncols() == as matrixcompare::Matrix>::cols(&matrix)
+ }
+}
+
+#[test]
+fn assert_matrix_eq_dense_positive_comparison() {
+ let a = MatrixMN::<_, U4, U5>::from_row_slice(&[
+ 1210, 1320, 1430, 1540, 1650,
+ 2310, 2420, 2530, 2640, 2750,
+ 3410, 3520, 3630, 3740, 3850,
+ 4510, 4620, 4730, 4840, 4950,
+ ]);
+
+ let b = MatrixMN::<_, U4, U5>::from_row_slice(&[
+ 1210, 1320, 1430, 1540, 1650,
+ 2310, 2420, 2530, 2640, 2750,
+ 3410, 3520, 3630, 3740, 3850,
+ 4510, 4620, 4730, 4840, 4950,
+ ]);
+
+ // Test matrices of static size
+ assert_matrix_eq!(a, b);
+ assert_matrix_eq!(&a, b);
+ assert_matrix_eq!(a, &b);
+ assert_matrix_eq!(&a, &b);
+
+ // Test matrices of dynamic size
+ let a_dyn = a.index((0..4, 0..5));
+ let b_dyn = b.index((0..4, 0..5));
+ assert_matrix_eq!(a_dyn, b_dyn);
+ assert_matrix_eq!(a_dyn, &b_dyn);
+ assert_matrix_eq!(&a_dyn, b_dyn);
+ assert_matrix_eq!(&a_dyn, &b_dyn);
+}
+
+#[test]
+#[should_panic]
+fn assert_matrix_eq_dense_negative_comparison() {
+ let a = MatrixMN::<_, U4, U5>::from_row_slice(&[
+ 1210, 1320, 1430, 1540, 1650,
+ 2310, 2420, 2530, 2640, 2750,
+ 3410, 3520, 3630, 3740, 3850,
+ 4510, 4620, -4730, 4840, 4950,
+ ]);
+
+ let b = MatrixMN::<_, U4, U5>::from_row_slice(&[
+ 1210, 1320, 1430, 1540, 1650,
+ 2310, 2420, 2530, 2640, 2750,
+ 3410, 3520, 3630, 3740, 3850,
+ 4510, 4620, 4730, 4840, 4950,
+ ]);
+
+ assert_matrix_eq!(a, b);
+}
\ No newline at end of file
diff --git a/tests/core/mod.rs b/tests/core/mod.rs
index ec1c4e3e..1b3c7861 100644
--- a/tests/core/mod.rs
+++ b/tests/core/mod.rs
@@ -10,5 +10,8 @@ mod matrix_slice;
mod mint;
mod serde;
+#[cfg(feature = "compare")]
+mod matrixcompare;
+
#[cfg(feature = "arbitrary")]
pub mod helper;
From 9196759bc2f0d04d2ba0ce76d9f8acede2f1c82d Mon Sep 17 00:00:00 2001
From: Andreas Longva
Date: Mon, 29 Jun 2020 19:03:20 +0200
Subject: [PATCH 05/55] Improve matrixcompare example
---
examples/matrixcompare.rs | 27 +++++++++++++++------------
src/base/matrix.rs | 10 ++++++----
tests/core/matrixcompare.rs | 8 ++++++--
3 files changed, 27 insertions(+), 18 deletions(-)
diff --git a/examples/matrixcompare.rs b/examples/matrixcompare.rs
index 60c632d3..713be25f 100644
--- a/examples/matrixcompare.rs
+++ b/examples/matrixcompare.rs
@@ -1,60 +1,63 @@
extern crate nalgebra as na;
-#[macro_use]
-extern crate quickcheck;
-
-use na::{U3, U4, MatrixMN};
+use matrixcompare::comparators::{AbsoluteElementwiseComparator, ExactElementwiseComparator};
use matrixcompare::compare_matrices;
-use matrixcompare::comparators::{ExactElementwiseComparator, AbsoluteElementwiseComparator};
+use na::{MatrixMN, U3, U4};
fn compare_integers_fail() {
println!("Comparing two integer matrices.");
+ #[rustfmt::skip]
let a = MatrixMN::<_, U3, U4>::from_row_slice(&[
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, -2, 11
]);
+ #[rustfmt::skip]
let b = MatrixMN::<_, U3, U4>::from_row_slice(&[
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11
]);
- if let Some(msg) = compare_matrices(a, b, &ExactElementwiseComparator).panic_message() {
- println!("{}", msg);
+ if let Err(err) = compare_matrices(a, b, &ExactElementwiseComparator) {
+ println!("{}", err);
}
}
fn compare_different_size() {
println!("Comparing matrices of different size.");
+ #[rustfmt::skip]
let a = MatrixMN::<_, U3, U3>::from_row_slice(&[
0, 1, 2,
4, 5, 6,
8, 9, 10,
]);
+ #[rustfmt::skip]
let b = MatrixMN::<_, U3, U4>::from_row_slice(&[
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11
]);
- if let Some(msg) = compare_matrices(a, b, &ExactElementwiseComparator).panic_message() {
- println!("{}", msg);
+ if let Err(err) = compare_matrices(a, b, &ExactElementwiseComparator) {
+ println!("{}", err);
}
}
fn compare_f64_abs_tol_fail() {
println!("Comparing two f64 matrices.");
+ #[rustfmt::skip]
let a = MatrixMN::::from_row_slice(&[
0.0, 1.0, 2.0 + 1e-10,
4.0, 5.0, 6.0,
8.0, 9.0, 10.0,
]);
+ #[rustfmt::skip]
let b = MatrixMN::<_, U3, U3>::from_row_slice(&[
0.0, 1.0, 2.0,
4.0, 5.0, 6.0,
@@ -62,8 +65,8 @@ fn compare_f64_abs_tol_fail() {
]);
let cmp = AbsoluteElementwiseComparator { tol: 1e-12 };
- if let Some(msg) = compare_matrices(a, b, &cmp).panic_message() {
- println!("{}", msg);
+ if let Err(err) = compare_matrices(a, b, &cmp) {
+ println!("{}", err);
}
}
@@ -76,4 +79,4 @@ fn main() {
compare_f64_abs_tol_fail();
println!("======================================================");
compare_different_size();
-}
\ No newline at end of file
+}
diff --git a/src/base/matrix.rs b/src/base/matrix.rs
index 8107a1dc..6e6aaba5 100644
--- a/src/base/matrix.rs
+++ b/src/base/matrix.rs
@@ -160,8 +160,9 @@ impl Abomonation for Matrix>
- matrixcompare_core::Matrix for Matrix {
+impl> matrixcompare_core::Matrix
+ for Matrix
+{
fn rows(&self) -> usize {
self.nrows()
}
@@ -176,8 +177,9 @@ impl>
}
#[cfg(feature = "compare")]
-impl>
- matrixcompare_core::DenseAccess for Matrix {
+impl> matrixcompare_core::DenseAccess
+ for Matrix
+{
fn fetch_single(&self, row: usize, col: usize) -> N {
self.index((row, col)).clone()
}
diff --git a/tests/core/matrixcompare.rs b/tests/core/matrixcompare.rs
index d4b6f142..df112173 100644
--- a/tests/core/matrixcompare.rs
+++ b/tests/core/matrixcompare.rs
@@ -4,7 +4,7 @@
//! The tests here only check that the necessary trait implementations are correctly implemented,
//! in addition to some sanity checks with example input.
-use nalgebra::{U4, U5, MatrixMN, DMatrix};
+use nalgebra::{DMatrix, MatrixMN, U4, U5};
use matrixcompare::{assert_matrix_eq, DenseAccess};
@@ -31,6 +31,7 @@ quickcheck! {
#[test]
fn assert_matrix_eq_dense_positive_comparison() {
+ #[rustfmt::skip]
let a = MatrixMN::<_, U4, U5>::from_row_slice(&[
1210, 1320, 1430, 1540, 1650,
2310, 2420, 2530, 2640, 2750,
@@ -38,6 +39,7 @@ fn assert_matrix_eq_dense_positive_comparison() {
4510, 4620, 4730, 4840, 4950,
]);
+ #[rustfmt::skip]
let b = MatrixMN::<_, U4, U5>::from_row_slice(&[
1210, 1320, 1430, 1540, 1650,
2310, 2420, 2530, 2640, 2750,
@@ -63,6 +65,7 @@ fn assert_matrix_eq_dense_positive_comparison() {
#[test]
#[should_panic]
fn assert_matrix_eq_dense_negative_comparison() {
+ #[rustfmt::skip]
let a = MatrixMN::<_, U4, U5>::from_row_slice(&[
1210, 1320, 1430, 1540, 1650,
2310, 2420, 2530, 2640, 2750,
@@ -70,6 +73,7 @@ fn assert_matrix_eq_dense_negative_comparison() {
4510, 4620, -4730, 4840, 4950,
]);
+ #[rustfmt::skip]
let b = MatrixMN::<_, U4, U5>::from_row_slice(&[
1210, 1320, 1430, 1540, 1650,
2310, 2420, 2530, 2640, 2750,
@@ -78,4 +82,4 @@ fn assert_matrix_eq_dense_negative_comparison() {
]);
assert_matrix_eq!(a, b);
-}
\ No newline at end of file
+}
From 217a2bf312e2661824ac50d3af5ea51cccb7e99d Mon Sep 17 00:00:00 2001
From: Andreas Longva
Date: Mon, 29 Jun 2020 19:07:44 +0200
Subject: [PATCH 06/55] Add compare feature to tests run by Makefile
---
Makefile | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index 01707964..0af9f3f2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
all:
- cargo test --features "debug arbitrary serde-serialize abomonation-serialize"
+ cargo test --features "debug arbitrary serde-serialize abomonation-serialize compare"
# cargo check --features "debug arbitrary serde-serialize"
doc:
@@ -9,4 +9,4 @@ bench:
cargo bench
test:
- cargo test --features "debug arbitrary serde-serialize abomonation-serialize"
+ cargo test --features "debug arbitrary serde-serialize abomonation-serialize compare"
From 4653f772bdfee342e5bd94bdd1f957316785bc6c Mon Sep 17 00:00:00 2001
From: Adam Nemecek
Date: Thu, 2 Jul 2020 10:16:18 -0700
Subject: [PATCH 07/55] added new_nonuniform_scaling_wrt_point to Matrix3 &
Matrix4
---
src/base/cg.rs | 26 +++++++++++++++++++--
tests/core/cg.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++
tests/core/mod.rs | 1 +
3 files changed, 84 insertions(+), 2 deletions(-)
create mode 100644 tests/core/cg.rs
diff --git a/src/base/cg.rs b/src/base/cg.rs
index 6a7db4bd..02aeade8 100644
--- a/src/base/cg.rs
+++ b/src/base/cg.rs
@@ -11,11 +11,11 @@ use crate::base::allocator::Allocator;
use crate::base::dimension::{DimName, DimNameDiff, DimNameSub, U1};
use crate::base::storage::{Storage, StorageMut};
use crate::base::{
- DefaultAllocator, Matrix3, Matrix4, MatrixN, Scalar, SquareMatrix, Unit, Vector, Vector3,
+ DefaultAllocator, Matrix3, Matrix4, MatrixN, Scalar, SquareMatrix, Unit, Vector, Vector2, Vector3,
VectorN,
};
use crate::geometry::{
- Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point3, Rotation2, Rotation3,
+ Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point2, Point3, Rotation2, Rotation3, Translation2, Translation3,
};
use simba::scalar::{ClosedAdd, ClosedMul, RealField};
@@ -70,6 +70,17 @@ impl Matrix3 {
pub fn new_rotation(angle: N) -> Self {
Rotation2::new(angle).to_homogeneous()
}
+
+ /// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point.
+ ///
+ /// Can be used to implement "zoom_to" functionality.
+ #[inline]
+ pub fn new_nonuniform_scaling_wrt_point(scaling: Vector2, pt: Point2) -> Self {
+ let translate = Translation2::new(pt.x, pt.y).to_homogeneous();
+ let scale = Matrix3::new_nonuniform_scaling(&scaling);
+ let translate_inv = Translation2::new(-pt.x, -pt.y).to_homogeneous();
+ translate * scale * translate_inv
+ }
}
impl Matrix4 {
@@ -90,6 +101,17 @@ impl Matrix4 {
Isometry::rotation_wrt_point(rot, pt).to_homogeneous()
}
+ /// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point.
+ ///
+ /// Can be used to implement "zoom_to" functionality.
+ #[inline]
+ pub fn new_nonuniform_scaling_wrt_point(scaling: Vector3, pt: Point3) -> Self {
+ let translate = Translation3::new(pt.x, pt.y, pt.z).to_homogeneous();
+ let scale = Matrix4::new_nonuniform_scaling(&scaling);
+ let translate_inv = Translation3::new(-pt.x, -pt.y, -pt.z).to_homogeneous();
+ translate * scale * translate_inv
+ }
+
/// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
///
/// Returns the identity matrix if the given argument is zero.
diff --git a/tests/core/cg.rs b/tests/core/cg.rs
new file mode 100644
index 00000000..17df4eb1
--- /dev/null
+++ b/tests/core/cg.rs
@@ -0,0 +1,59 @@
+use na::{Vector2, Vector3, Matrix3, Matrix4, Point2, Point3};
+
+/// See Example 3.4 of "Graphics and Visualization: Principles & Algorithms"
+/// by Theoharis, Papaioannou, Platis, Patrikalakis.
+#[test]
+fn test_scaling_wrt_point_1() {
+ let a = Point2::new(0.0, 0.0);
+ let b = Point2::new(1.0, 1.0);
+ let c = Point2::new(5.0, 2.0);
+
+ let scaling = Vector2::new(2.0, 2.0);
+ let scale_about = Matrix3::new_nonuniform_scaling_wrt_point(scaling, c);
+
+ let expected_a = Point2::new(-5.0, -2.0);
+ let expected_b = Point2::new(-3.0, 0.0);
+ let result_a = scale_about.transform_point(&a);
+ let result_b = scale_about.transform_point(&b);
+ let result_c = scale_about.transform_point(&c);
+
+ assert!(expected_a == result_a);
+ assert!(expected_b == result_b);
+ assert!(c == result_c);
+}
+
+/// Based on the same example as the test above.
+#[test]
+fn test_scaling_wrt_point_2() {
+ let a = Point3::new(0.0, 0.0, 1.0);
+ let b = Point3::new(1.0, 1.0, 1.0);
+ let c = Point3::new(5.0, 2.0, 1.0);
+
+ let scaling = Vector3::new(2.0, 2.0, 1.0);
+ let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(scaling, c);
+
+ let expected_a = Point3::new(-5.0, -2.0, 1.0);
+ let expected_b = Point3::new(-3.0, 0.0, 1.0);
+
+ let result_a = scale_about.transform_point(&a);
+ let result_b = scale_about.transform_point(&b);
+ let result_c = scale_about.transform_point(&c);
+
+ assert!(expected_a == result_a);
+ assert!(expected_b == result_b);
+ assert!(c == result_c);
+}
+
+/// Based on https://github.com/emlowry/AiE/blob/50bae4068edb686cf8ffacdf6fab8e7cb22e7eb1/Year%201%20Classwork/MathTest/Matrix4x4TestGroup.cpp#L145
+#[test]
+fn test_scaling_wrt_point_3() {
+ let about = Point3::new(2.0, 1.0, -2.0);
+ let scale = Vector3::new(2.0, 0.5, -1.0);
+ let pt = Point3::new(1.0, 2.0, 3.0);
+ let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(scale, about);
+
+ let expected = Point3::new(0.0, 1.5, -7.0);
+ let result = scale_about.transform_point(&pt);
+
+ assert!(result == expected);
+}
\ No newline at end of file
diff --git a/tests/core/mod.rs b/tests/core/mod.rs
index ec1c4e3e..7c738368 100644
--- a/tests/core/mod.rs
+++ b/tests/core/mod.rs
@@ -1,6 +1,7 @@
#[cfg(feature = "abomonation-serialize")]
mod abomonation;
mod blas;
+mod cg;
mod conversion;
mod edition;
mod empty;
From 6a1c4f84af61a4208c2137afc31f2b3726e1e741 Mon Sep 17 00:00:00 2001
From: Adam Nemecek
Date: Thu, 2 Jul 2020 10:31:30 -0700
Subject: [PATCH 08/55] cargo fmt
---
src/base/cg.rs | 7 ++++---
tests/core/cg.rs | 4 ++--
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/base/cg.rs b/src/base/cg.rs
index 02aeade8..6a324bd2 100644
--- a/src/base/cg.rs
+++ b/src/base/cg.rs
@@ -11,11 +11,12 @@ use crate::base::allocator::Allocator;
use crate::base::dimension::{DimName, DimNameDiff, DimNameSub, U1};
use crate::base::storage::{Storage, StorageMut};
use crate::base::{
- DefaultAllocator, Matrix3, Matrix4, MatrixN, Scalar, SquareMatrix, Unit, Vector, Vector2, Vector3,
- VectorN,
+ DefaultAllocator, Matrix3, Matrix4, MatrixN, Scalar, SquareMatrix, Unit, Vector, Vector2,
+ Vector3, VectorN,
};
use crate::geometry::{
- Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point2, Point3, Rotation2, Rotation3, Translation2, Translation3,
+ Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point2, Point3, Rotation2,
+ Rotation3, Translation2, Translation3,
};
use simba::scalar::{ClosedAdd, ClosedMul, RealField};
diff --git a/tests/core/cg.rs b/tests/core/cg.rs
index 17df4eb1..f1969658 100644
--- a/tests/core/cg.rs
+++ b/tests/core/cg.rs
@@ -1,4 +1,4 @@
-use na::{Vector2, Vector3, Matrix3, Matrix4, Point2, Point3};
+use na::{Matrix3, Matrix4, Point2, Point3, Vector2, Vector3};
/// See Example 3.4 of "Graphics and Visualization: Principles & Algorithms"
/// by Theoharis, Papaioannou, Platis, Patrikalakis.
@@ -56,4 +56,4 @@ fn test_scaling_wrt_point_3() {
let result = scale_about.transform_point(&pt);
assert!(result == expected);
-}
\ No newline at end of file
+}
From 1cf7d12695dacd2b55777612a4e4234b6600c3da Mon Sep 17 00:00:00 2001
From: Adam Nemecek
Date: Sun, 5 Jul 2020 13:29:08 -0700
Subject: [PATCH 09/55] unrolled new_nonuniform_scaling_wrt_point
---
src/base/cg.rs | 47 ++++++++++++++++++++++++++++++++++++-----------
tests/core/cg.rs | 6 +++---
2 files changed, 39 insertions(+), 14 deletions(-)
diff --git a/src/base/cg.rs b/src/base/cg.rs
index 6a324bd2..935388af 100644
--- a/src/base/cg.rs
+++ b/src/base/cg.rs
@@ -16,7 +16,7 @@ use crate::base::{
};
use crate::geometry::{
Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point2, Point3, Rotation2,
- Rotation3, Translation2, Translation3,
+ Rotation3,
};
use simba::scalar::{ClosedAdd, ClosedMul, RealField};
@@ -76,11 +76,20 @@ impl Matrix3 {
///
/// Can be used to implement "zoom_to" functionality.
#[inline]
- pub fn new_nonuniform_scaling_wrt_point(scaling: Vector2, pt: Point2) -> Self {
- let translate = Translation2::new(pt.x, pt.y).to_homogeneous();
- let scale = Matrix3::new_nonuniform_scaling(&scaling);
- let translate_inv = Translation2::new(-pt.x, -pt.y).to_homogeneous();
- translate * scale * translate_inv
+ pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector2, pt: &Point2) -> Self {
+ let _0 = N::zero();
+ let _1 = N::one();
+ Matrix3::new(
+ scaling.x,
+ _0,
+ pt.x - pt.x * scaling.x,
+ _0,
+ scaling.y,
+ pt.y - pt.y * scaling.y,
+ _0,
+ _0,
+ _1,
+ )
}
}
@@ -106,11 +115,27 @@ impl Matrix4 {
///
/// Can be used to implement "zoom_to" functionality.
#[inline]
- pub fn new_nonuniform_scaling_wrt_point(scaling: Vector3, pt: Point3) -> Self {
- let translate = Translation3::new(pt.x, pt.y, pt.z).to_homogeneous();
- let scale = Matrix4::new_nonuniform_scaling(&scaling);
- let translate_inv = Translation3::new(-pt.x, -pt.y, -pt.z).to_homogeneous();
- translate * scale * translate_inv
+ pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector3, pt: &Point3) -> Self {
+ let _0 = N::zero();
+ let _1 = N::one();
+ Matrix4::new(
+ scaling.x,
+ _0,
+ _0,
+ pt.x - pt.x * scaling.x,
+ _0,
+ scaling.y,
+ _0,
+ pt.y - pt.y * scaling.y,
+ _0,
+ _0,
+ scaling.z,
+ pt.z - pt.z * scaling.z,
+ _0,
+ _0,
+ _0,
+ _1,
+ )
}
/// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
diff --git a/tests/core/cg.rs b/tests/core/cg.rs
index f1969658..b061efbb 100644
--- a/tests/core/cg.rs
+++ b/tests/core/cg.rs
@@ -9,7 +9,7 @@ fn test_scaling_wrt_point_1() {
let c = Point2::new(5.0, 2.0);
let scaling = Vector2::new(2.0, 2.0);
- let scale_about = Matrix3::new_nonuniform_scaling_wrt_point(scaling, c);
+ let scale_about = Matrix3::new_nonuniform_scaling_wrt_point(&scaling, &c);
let expected_a = Point2::new(-5.0, -2.0);
let expected_b = Point2::new(-3.0, 0.0);
@@ -30,7 +30,7 @@ fn test_scaling_wrt_point_2() {
let c = Point3::new(5.0, 2.0, 1.0);
let scaling = Vector3::new(2.0, 2.0, 1.0);
- let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(scaling, c);
+ let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(&scaling, &c);
let expected_a = Point3::new(-5.0, -2.0, 1.0);
let expected_b = Point3::new(-3.0, 0.0, 1.0);
@@ -50,7 +50,7 @@ fn test_scaling_wrt_point_3() {
let about = Point3::new(2.0, 1.0, -2.0);
let scale = Vector3::new(2.0, 0.5, -1.0);
let pt = Point3::new(1.0, 2.0, 3.0);
- let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(scale, about);
+ let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(&scale, &about);
let expected = Point3::new(0.0, 1.5, -7.0);
let result = scale_about.transform_point(&pt);
From bc70258e5c6c0057efdc659ea533119c028e7e96 Mon Sep 17 00:00:00 2001
From: CGMossa
Date: Thu, 16 Jul 2020 09:27:06 +0200
Subject: [PATCH 10/55] Why Option<_> (#746)
Add a comment about why `UnitQuaternion::rotation_between` returns an Option.
---
src/geometry/quaternion_construction.rs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/geometry/quaternion_construction.rs b/src/geometry/quaternion_construction.rs
index 175275d4..a59d3030 100644
--- a/src/geometry/quaternion_construction.rs
+++ b/src/geometry/quaternion_construction.rs
@@ -374,7 +374,8 @@ where
}
/// The unit quaternion needed to make `a` and `b` be collinear and point toward the same
- /// direction.
+ /// direction. Returns `None` if both `a` and `b` are collinear and point to opposite directions, as then the
+ /// rotation desired is not unique.
///
/// # Example
/// ```
From f9f7169558fb7701d23c7ee19182a5304ecd2e42 Mon Sep 17 00:00:00 2001
From: danielschlaugies <46607696+danielschlaugies@users.noreply.github.com>
Date: Thu, 16 Jul 2020 10:29:52 +0200
Subject: [PATCH 11/55] Add matrix exponential for complex matrices (#744)
Added matrix exponential for complex matrices.
---
src/linalg/exp.rs | 83 ++++++++++++++++++++++++++-------------------
tests/linalg/exp.rs | 47 +++++++++++++++++++++++++
2 files changed, 95 insertions(+), 35 deletions(-)
diff --git a/src/linalg/exp.rs b/src/linalg/exp.rs
index c11fd757..6c43568d 100644
--- a/src/linalg/exp.rs
+++ b/src/linalg/exp.rs
@@ -10,10 +10,12 @@ use crate::{
convert, try_convert, ComplexField, MatrixN, RealField,
};
+use crate::num::Zero;
+
// https://github.com/scipy/scipy/blob/c1372d8aa90a73d8a52f135529293ff4edb98fc8/scipy/sparse/linalg/matfuncs.py
struct ExpmPadeHelper
where
- N: RealField,
+ N: ComplexField,
D: DimMin,
DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimum>,
{
@@ -27,20 +29,20 @@ where
a8: Option>,
a10: Option>,
- d4_exact: Option,
- d6_exact: Option,
- d8_exact: Option,
- d10_exact: Option,
+ d4_exact: Option,
+ d6_exact: Option,
+ d8_exact: Option,
+ d10_exact: Option,
- d4_approx: Option,
- d6_approx: Option,
- d8_approx: Option,
- d10_approx: Option,
+ d4_approx: Option,
+ d6_approx: Option,
+ d8_approx: Option,
+ d10_approx: Option,
}
impl ExpmPadeHelper
where
- N: RealField,
+ N: ComplexField,
D: DimMin,
DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimum>,
{
@@ -110,7 +112,7 @@ where
}
}
- fn d4_tight(&mut self) -> N {
+ fn d4_tight(&mut self) -> N::RealField {
if self.d4_exact.is_none() {
self.calc_a4();
self.d4_exact = Some(one_norm(self.a4.as_ref().unwrap()).powf(convert(0.25)));
@@ -118,7 +120,7 @@ where
self.d4_exact.unwrap()
}
- fn d6_tight(&mut self) -> N {
+ fn d6_tight(&mut self) -> N::RealField {
if self.d6_exact.is_none() {
self.calc_a6();
self.d6_exact = Some(one_norm(self.a6.as_ref().unwrap()).powf(convert(1.0 / 6.0)));
@@ -126,7 +128,7 @@ where
self.d6_exact.unwrap()
}
- fn d8_tight(&mut self) -> N {
+ fn d8_tight(&mut self) -> N::RealField {
if self.d8_exact.is_none() {
self.calc_a8();
self.d8_exact = Some(one_norm(self.a8.as_ref().unwrap()).powf(convert(1.0 / 8.0)));
@@ -134,7 +136,7 @@ where
self.d8_exact.unwrap()
}
- fn d10_tight(&mut self) -> N {
+ fn d10_tight(&mut self) -> N::RealField {
if self.d10_exact.is_none() {
self.calc_a10();
self.d10_exact = Some(one_norm(self.a10.as_ref().unwrap()).powf(convert(1.0 / 10.0)));
@@ -142,7 +144,7 @@ where
self.d10_exact.unwrap()
}
- fn d4_loose(&mut self) -> N {
+ fn d4_loose(&mut self) -> N::RealField {
if self.use_exact_norm {
return self.d4_tight();
}
@@ -159,7 +161,7 @@ where
self.d4_approx.unwrap()
}
- fn d6_loose(&mut self) -> N {
+ fn d6_loose(&mut self) -> N::RealField {
if self.use_exact_norm {
return self.d6_tight();
}
@@ -176,7 +178,7 @@ where
self.d6_approx.unwrap()
}
- fn d8_loose(&mut self) -> N {
+ fn d8_loose(&mut self) -> N::RealField {
if self.use_exact_norm {
return self.d8_tight();
}
@@ -193,7 +195,7 @@ where
self.d8_approx.unwrap()
}
- fn d10_loose(&mut self) -> N {
+ fn d10_loose(&mut self) -> N::RealField {
if self.use_exact_norm {
return self.d10_tight();
}
@@ -359,15 +361,20 @@ where
fn ell(a: &MatrixN, m: u64) -> u64
where
- N: RealField,
+ N: ComplexField,
D: Dim,
- DefaultAllocator: Allocator + Allocator,
+ DefaultAllocator: Allocator
+ + Allocator
+ + Allocator
+ + Allocator,
{
// 2m choose m = (2m)!/(m! * (2m-m)!)
- let a_abs_onenorm = onenorm_matrix_power_nonm(&a.abs(), 2 * m + 1);
+ let a_abs = a.map(|x| x.abs());
- if a_abs_onenorm == N::zero() {
+ let a_abs_onenorm = onenorm_matrix_power_nonm(&a_abs, 2 * m + 1);
+
+ if a_abs_onenorm == ::RealField::zero() {
return 0;
}
@@ -399,27 +406,33 @@ where
q.lu().solve(&p).unwrap()
}
-fn one_norm(m: &MatrixN) -> N
+fn one_norm(m: &MatrixN) -> N::RealField
where
- N: RealField,
+ N: ComplexField,
D: Dim,
DefaultAllocator: Allocator,
{
- let mut max = N::zero();
+ let mut max = ::RealField::zero();
for i in 0..m.ncols() {
let col = m.column(i);
- max = max.max(col.iter().fold(N::zero(), |a, b| a + b.abs()));
+ max = max.max(
+ col.iter()
+ .fold(::RealField::zero(), |a, b| a + b.abs()),
+ );
}
max
}
-impl MatrixN
+impl MatrixN
where
D: DimMin,
- DefaultAllocator:
- Allocator + Allocator<(usize, usize), DimMinimum> + Allocator,
+ DefaultAllocator: Allocator
+ + Allocator<(usize, usize), DimMinimum>
+ + Allocator
+ + Allocator
+ + Allocator,
{
/// Computes exponential of this matrix
pub fn exp(&self) -> Self {
@@ -430,19 +443,19 @@ where
let mut h = ExpmPadeHelper::new(self.clone(), true);
- let eta_1 = N::max(h.d4_loose(), h.d6_loose());
+ let eta_1 = N::RealField::max(h.d4_loose(), h.d6_loose());
if eta_1 < convert(1.495585217958292e-002) && ell(&h.a, 3) == 0 {
let (u, v) = h.pade3();
return solve_p_q(u, v);
}
- let eta_2 = N::max(h.d4_tight(), h.d6_loose());
+ let eta_2 = N::RealField::max(h.d4_tight(), h.d6_loose());
if eta_2 < convert(2.539398330063230e-001) && ell(&h.a, 5) == 0 {
let (u, v) = h.pade5();
return solve_p_q(u, v);
}
- let eta_3 = N::max(h.d6_tight(), h.d8_loose());
+ let eta_3 = N::RealField::max(h.d6_tight(), h.d8_loose());
if eta_3 < convert(9.504178996162932e-001) && ell(&h.a, 7) == 0 {
let (u, v) = h.pade7();
return solve_p_q(u, v);
@@ -452,11 +465,11 @@ where
return solve_p_q(u, v);
}
- let eta_4 = N::max(h.d8_loose(), h.d10_loose());
- let eta_5 = N::min(eta_3, eta_4);
+ let eta_4 = N::RealField::max(h.d8_loose(), h.d10_loose());
+ let eta_5 = N::RealField::min(eta_3, eta_4);
let theta_13 = convert(4.25);
- let mut s = if eta_5 == N::zero() {
+ let mut s = if eta_5 == N::RealField::zero() {
0
} else {
let l2 = try_convert((eta_5 / theta_13).log2().ceil()).unwrap();
diff --git a/tests/linalg/exp.rs b/tests/linalg/exp.rs
index 75122107..93859934 100644
--- a/tests/linalg/exp.rs
+++ b/tests/linalg/exp.rs
@@ -126,4 +126,51 @@ mod tests {
assert!(relative_eq!(f, m.exp(), epsilon = 1.0e-7));
}
+
+ #[test]
+ fn exp_complex() {
+ use nalgebra::{Complex, ComplexField, DMatrix, DVector, Matrix2, RealField};
+
+ {
+ let z = Matrix2::>::zeros();
+
+ let identity = Matrix2::>::identity();
+
+ assert!((z.exp() - identity).norm() < 1e-7);
+ }
+
+ {
+ let a = Matrix2::>::new(
+ Complex::::new(0.0, 1.0),
+ Complex::::new(0.0, 2.0),
+ Complex::::new(0.0, -1.0),
+ Complex::::new(0.0, 3.0),
+ );
+
+ let b = Matrix2::>::new(
+ Complex::::new(0.42645929666726, 1.89217550966333),
+ Complex::::new(-2.13721484276556, -0.97811251808259),
+ Complex::::new(1.06860742138278, 0.48905625904129),
+ Complex::::new(-1.7107555460983, 0.91406299158075),
+ );
+
+ assert!((a.exp() - b).norm() < 1.0e-07);
+ }
+
+ {
+ let d1 = Complex::::new(0.0, ::pi());
+ let d2 = Complex::::new(0.0, ::frac_pi_2());
+ let d3 = Complex::::new(0.0, ::frac_pi_4());
+
+ let m = DMatrix::>::from_diagonal(&DVector::from_row_slice(&[d1, d2, d3]));
+
+ let res = DMatrix::>::from_diagonal(&DVector::from_row_slice(&[
+ d1.exp(),
+ d2.exp(),
+ d3.exp(),
+ ]));
+
+ assert!((m.exp() - res).norm() < 1e-07);
+ }
+ }
}
From d13b3de4e441dcf71173e00c30cd23bcb73c5bb7 Mon Sep 17 00:00:00 2001
From: Andreas Longva
Date: Fri, 17 Jul 2020 09:54:30 +0200
Subject: [PATCH 12/55] Use matrixcompare 0.1.3 for tests (fixes no-std test
issues)
---
Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Cargo.toml b/Cargo.toml
index fe347ed0..36047af0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -64,7 +64,7 @@ rand_isaac = "0.2"
#criterion = "0.2.10"
# For matrix comparison macro
-matrixcompare = "0.1"
+matrixcompare = "0.1.3"
[workspace]
members = [ "nalgebra-lapack", "nalgebra-glm" ]
From 0f6c0e52339bb040f37e8e58a3c8500b7a609600 Mon Sep 17 00:00:00 2001
From: Frans Skarman
Date: Fri, 31 Jul 2020 14:14:39 +0200
Subject: [PATCH 13/55] Update version in docs header
---
src/lib.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib.rs b/src/lib.rs
index 9db176bf..05c01e2b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,7 +15,7 @@ Simply add the following to your `Cargo.toml` file:
```.ignore
[dependencies]
-nalgebra = "0.18"
+nalgebra = "0.21"
```
From 0a0799f76a8cfd1cc46561807677c612ef8731ea Mon Sep 17 00:00:00 2001
From: Michael Stevens
Date: Mon, 3 Aug 2020 18:03:01 +0200
Subject: [PATCH 14/55] Remove unnecessary Trait bound DimSub in fn
cholesky(self)
---
src/linalg/cholesky.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/linalg/cholesky.rs b/src/linalg/cholesky.rs
index d9f33f27..17187fdf 100644
--- a/src/linalg/cholesky.rs
+++ b/src/linalg/cholesky.rs
@@ -8,7 +8,7 @@ use simba::simd::SimdComplexField;
use crate::allocator::Allocator;
use crate::base::{DefaultAllocator, Matrix, MatrixMN, MatrixN, SquareMatrix, Vector};
use crate::constraint::{SameNumberOfRows, ShapeConstraint};
-use crate::dimension::{Dim, DimAdd, DimDiff, DimSub, DimSum, Dynamic, U1};
+use crate::dimension::{Dim, DimAdd, DimDiff, DimSub, DimSum, U1};
use crate::storage::{Storage, StorageMut};
/// The Cholesky decomposition of a symmetric-definite-positive matrix.
@@ -364,7 +364,7 @@ where
}
}
-impl, S: Storage> SquareMatrix
+impl> SquareMatrix
where
DefaultAllocator: Allocator,
{
From d81a895a87e2e5a94a847c8033a7c2cce2fbed9a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Crozet?=
Date: Wed, 19 Aug 2020 19:48:16 +0200
Subject: [PATCH 15/55] Switch license to Apache v2.0 + update sponsor link.
---
.github/FUNDING.yml | 4 +-
Cargo.toml | 2 +-
LICENSE | 218 +++++++++++++++++++++++++++++++++++++++-----
README.md | 16 +---
4 files changed, 203 insertions(+), 37 deletions(-)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 356ddf23..5ee5fec9 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,7 +1,7 @@
# These are supported funding model platforms
-github: [ "sebcrozet" ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: sebcrozet # Replace with a single Patreon username
+github: [ "dimforge" ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
diff --git a/Cargo.toml b/Cargo.toml
index 36047af0..7ea6f8ba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,7 +10,7 @@ repository = "https://github.com/rustsim/nalgebra"
readme = "README.md"
categories = [ "science" ]
keywords = [ "linear", "algebra", "matrix", "vector", "math" ]
-license = "BSD-3-Clause"
+license = "Apache-2.0"
edition = "2018"
exclude = ["/ci/*", "/.travis.yml", "/Makefile"]
diff --git a/LICENSE b/LICENSE
index f7f80044..97f4383a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,27 +1,201 @@
-Copyright (c) 2013, Sébastien Crozet
-All rights reserved.
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
+ 1. Definitions.
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
-3. Neither the name of the author nor the names of its contributors may be used
- to endorse or promote products derived from this software without specific
- prior written permission.
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2020 Sébastien Crozet
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
index df3a82f3..72d058b5 100644
--- a/README.md
+++ b/README.md
@@ -5,14 +5,14 @@
-
-
+
+
-
-
+
+
@@ -29,11 +29,3 @@
-----
-
-
- Click this button if you wish to donate to support the development of nalgebra:
-
-
-
-
-
From 6300d343566390120defa1190e84e92a457d31c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Crozet?=
Date: Sat, 13 Jun 2020 10:06:48 +0200
Subject: [PATCH 16/55] Add the ::ith constructor for vectors.
This initializes a vectors to zero except the i-th element set to a given value.
---
src/base/construction.rs | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/base/construction.rs b/src/base/construction.rs
index ae8c10d4..f810acdf 100644
--- a/src/base/construction.rs
+++ b/src/base/construction.rs
@@ -1011,6 +1011,14 @@ where
N: Scalar + Zero + One,
DefaultAllocator: Allocator,
{
+ /// The column vector with `val` as its i-th component.
+ #[inline]
+ pub fn ith(i: usize, val: N) -> Self {
+ let mut res = Self::zeros();
+ res[i] = val;
+ res
+ }
+
/// The column vector with a 1 as its first component, and zero elsewhere.
#[inline]
pub fn x() -> Self
From 7c69cbf3261bb6e30cd3bf8aaf4f44bf7edbc47f Mon Sep 17 00:00:00 2001
From: sebcrozet
Date: Sun, 12 Jul 2020 10:47:16 +0200
Subject: [PATCH 17/55] Don't depend on serde_derive explicitly.
---
Cargo.toml | 5 ++---
src/lib.rs | 14 ++++----------
2 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 7ea6f8ba..d7576d62 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,7 +24,7 @@ default = [ "std" ]
std = [ "matrixmultiply", "rand/std", "rand_distr", "simba/std" ]
stdweb = [ "rand/stdweb" ]
arbitrary = [ "quickcheck" ]
-serde-serialize = [ "serde", "serde_derive", "num-complex/serde" ]
+serde-serialize = [ "serde", "num-complex/serde" ]
abomonation-serialize = [ "abomonation" ]
sparse = [ ]
debug = [ "approx/num-complex", "rand/std" ]
@@ -44,8 +44,7 @@ simba = { version = "0.1", default-features = false }
alga = { version = "0.9", default-features = false, optional = true }
rand_distr = { version = "0.2", optional = true }
matrixmultiply = { version = "0.2", optional = true }
-serde = { version = "1.0", optional = true }
-serde_derive = { version = "1.0", optional = true }
+serde = { version = "1.0", features = [ "derive" ], optional = true }
abomonation = { version = "0.7", optional = true }
mint = { version = "0.5", optional = true }
quickcheck = { version = "0.9", optional = true }
diff --git a/src/lib.rs b/src/lib.rs
index 9db176bf..fbc0a27d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -90,11 +90,9 @@ an optimized set of tools for computer graphics and physics. Those features incl
#[cfg(feature = "arbitrary")]
extern crate quickcheck;
-#[cfg(feature = "serde")]
-extern crate serde;
-#[cfg(feature = "serde")]
+#[cfg(feature = "serde-serialize")]
#[macro_use]
-extern crate serde_derive;
+extern crate serde;
#[cfg(feature = "abomonation-serialize")]
extern crate abomonation;
@@ -190,9 +188,7 @@ pub fn zero() -> T {
/// The range must not be empty.
#[inline]
pub fn wrap(mut val: T, min: T, max: T) -> T
-where
- T: Copy + PartialOrd + ClosedAdd + ClosedSub,
-{
+where T: Copy + PartialOrd + ClosedAdd + ClosedSub {
assert!(min < max, "Invalid wrapping bounds.");
let width = max - min;
@@ -392,9 +388,7 @@ pub fn partial_sort2<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> Option<(&'a T, &'
/// * [distance_squared](fn.distance_squared.html)
#[inline]
pub fn center(p1: &Point, p2: &Point) -> Point
-where
- DefaultAllocator: Allocator,
-{
+where DefaultAllocator: Allocator {
((&p1.coords + &p2.coords) * convert::<_, N>(0.5)).into()
}
From 46d1cf22314f85cc4f8ad83481b61c090a8ab3c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Crozet?=
Date: Tue, 25 Aug 2020 20:44:03 +0200
Subject: [PATCH 18/55] Add a libm and libm-force feature to transitively
enable the corresponding simba feature.
---
Cargo.toml | 6 +++---
src/linalg/mod.rs | 5 +++++
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index d7576d62..9ed76264 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,6 +31,9 @@ debug = [ "approx/num-complex", "rand/std" ]
alloc = [ ]
io = [ "pest", "pest_derive" ]
compare = [ "matrixcompare-core" ]
+libm = [ "simba/libm" ]
+libm-force = [ "simba/libm_force" ]
+
[dependencies]
typenum = "1.11"
@@ -75,6 +78,3 @@ path = "benches/lib.rs"
[profile.bench]
lto = true
-
-#[patch.crates-io]
-#simba = { path = "../simba" }
\ No newline at end of file
diff --git a/src/linalg/mod.rs b/src/linalg/mod.rs
index f96cef0c..4a1edbcf 100644
--- a/src/linalg/mod.rs
+++ b/src/linalg/mod.rs
@@ -5,6 +5,10 @@ mod bidiagonal;
mod cholesky;
mod convolution;
mod determinant;
+// FIXME: this should not be needed. However, the exp uses
+// explicit float operations on `f32` and `f64`. We need to
+// get rid of these to allow exp to be used on a no-std context.
+#[cfg(feature = "std")]
mod exp;
mod full_piv_lu;
pub mod givens;
@@ -27,6 +31,7 @@ mod symmetric_tridiagonal;
pub use self::bidiagonal::*;
pub use self::cholesky::*;
pub use self::convolution::*;
+#[cfg(feature = "std")]
pub use self::exp::*;
pub use self::full_piv_lu::*;
pub use self::hessenberg::*;
From a6962dfadcefb4b45279760c5e2b3e5ce783347c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Crozet?=
Date: Tue, 25 Aug 2020 20:45:25 +0200
Subject: [PATCH 19/55] Bump the simba dependency version.
---
Cargo.toml | 2 +-
nalgebra-glm/Cargo.toml | 2 +-
nalgebra-lapack/Cargo.toml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 9ed76264..6705c222 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -43,7 +43,7 @@ num-traits = { version = "0.2", default-features = false }
num-complex = { version = "0.2", default-features = false }
num-rational = { version = "0.2", default-features = false }
approx = { version = "0.3", default-features = false }
-simba = { version = "0.1", default-features = false }
+simba = { version = "0.2", default-features = false }
alga = { version = "0.9", default-features = false, optional = true }
rand_distr = { version = "0.2", optional = true }
matrixmultiply = { version = "0.2", optional = true }
diff --git a/nalgebra-glm/Cargo.toml b/nalgebra-glm/Cargo.toml
index b72e2988..2bc29613 100644
--- a/nalgebra-glm/Cargo.toml
+++ b/nalgebra-glm/Cargo.toml
@@ -24,5 +24,5 @@ abomonation-serialize = [ "nalgebra/abomonation-serialize" ]
[dependencies]
num-traits = { version = "0.2", default-features = false }
approx = { version = "0.3", default-features = false }
-simba = { version = "0.1", default-features = false }
+simba = { version = "0.2", default-features = false }
nalgebra = { path = "..", version = "0.21", default-features = false }
diff --git a/nalgebra-lapack/Cargo.toml b/nalgebra-lapack/Cargo.toml
index b262e8b9..712c19e7 100644
--- a/nalgebra-lapack/Cargo.toml
+++ b/nalgebra-lapack/Cargo.toml
@@ -26,7 +26,7 @@ intel-mkl = ["lapack-src/intel-mkl"]
nalgebra = { version = "0.21", path = ".." }
num-traits = "0.2"
num-complex = { version = "0.2", default-features = false }
-simba = "0.1"
+simba = "0.2"
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
lapack = { version = "0.16", default-features = false }
From a8f73cb7b20fd67cd6caccf439b32211e98e762b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Crozet?=
Date: Tue, 25 Aug 2020 20:47:07 +0200
Subject: [PATCH 20/55] Run cargo fmt.
---
src/lib.rs | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/lib.rs b/src/lib.rs
index fbc0a27d..68116334 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -188,7 +188,9 @@ pub fn zero() -> T {
/// The range must not be empty.
#[inline]
pub fn wrap(mut val: T, min: T, max: T) -> T
-where T: Copy + PartialOrd + ClosedAdd + ClosedSub {
+where
+ T: Copy + PartialOrd + ClosedAdd + ClosedSub,
+{
assert!(min < max, "Invalid wrapping bounds.");
let width = max - min;
@@ -388,7 +390,9 @@ pub fn partial_sort2<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> Option<(&'a T, &'
/// * [distance_squared](fn.distance_squared.html)
#[inline]
pub fn center(p1: &Point, p2: &Point) -> Point
-where DefaultAllocator: Allocator {
+where
+ DefaultAllocator: Allocator,
+{
((&p1.coords + &p2.coords) * convert::<_, N>(0.5)).into()
}
From b96181f6c40ed0a4b95f5c54b0824ef81796157b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Crozet?=
Date: Tue, 25 Aug 2020 21:04:37 +0200
Subject: [PATCH 21/55] CI: don't eanble --all-features when running the tests.
This would enable the libm_force feature which results in less accurate results causing some tests to fail.
---
.circleci/config.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index ee64d771..b21798d9 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -47,10 +47,10 @@ jobs:
- checkout
- run:
name: test
- command: cargo test --all-features
+ command: cargo test --features arbitrary --features serde-serialize --features abomonation-serialize --features sparse --features debug --features io --features compare --features libm
- run:
name: test nalgebra-glm
- command: cargo test -p nalgebra-glm --all-features
+ command: cargo test -p nalgebra-glm --features arbitrary --features serde-serialize --features abomonation-serialize --features sparse --features debug --features io --features compare --features libm
build-wasm:
executor: rust-executor
steps:
From d63500189869a39cc600f49c583f9cb359c48f4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Crozet?=
Date: Tue, 25 Aug 2020 21:15:56 +0200
Subject: [PATCH 22/55] Release v0.22.0
---
CHANGELOG.md | 10 ++++++++--
Cargo.toml | 2 +-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 57f18f25..db0cdc6b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,15 +4,21 @@ documented here.
This project adheres to [Semantic Versioning](https://semver.org/).
-## [0.22.0] - WIP
+## [0.22.0]
+In this release, we are using the new version 0.2 of simba. One major change of that version is that the
+use of `libm` is now opt-in when building targetting `no-std` environment. If you are using floating-point
+operations with nalgebra in a `no-std` environment, you will need to enable the new `libm` feature
+of nalgebra for your code to compile again.
### Added
+ * The `libm` feature that enables `libm` when building for `no-std` environment.
+ * The `libm-force` feature that enables `libm` even when building for a not `no-std` environment.
* `Cholesky::new_unchecked` which build a Cholesky decomposition without checking that its input is
positive-definite. It can be use with SIMD types.
* The `Default` trait is now implemented for matrices, and quaternions. They are all filled with zeros,
except for `UnitQuaternion` which is initialized with the identity.
* Matrix exponential `matrix.exp()`.
-
+ * The `Vector::ith(i, x)` that builds a vector filled with zeros except for the `i`-th component set to `x`.
## [0.21.0]
In this release, we are no longer relying on traits from the __alga__ crate for our generic code.
diff --git a/Cargo.toml b/Cargo.toml
index 6705c222..e4be2462 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "nalgebra"
-version = "0.21.1"
+version = "0.22.0"
authors = [ "Sébastien Crozet " ]
description = "Linear algebra library with transformations and statically-sized or dynamically-sized matrices."
From 1e65fb948d7eb74fd11327cad2a982c38bf78cf6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Crozet?=
Date: Tue, 25 Aug 2020 21:16:06 +0200
Subject: [PATCH 23/55] Release nalgebra-glm v0.8.0
---
nalgebra-glm/Cargo.toml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nalgebra-glm/Cargo.toml b/nalgebra-glm/Cargo.toml
index 2bc29613..50f45eae 100644
--- a/nalgebra-glm/Cargo.toml
+++ b/nalgebra-glm/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "nalgebra-glm"
-version = "0.7.0"
+version = "0.8.0"
authors = ["sebcrozet "]
description = "A computer-graphics oriented API for nalgebra, inspired by the C++ GLM library."
@@ -25,4 +25,4 @@ abomonation-serialize = [ "nalgebra/abomonation-serialize" ]
num-traits = { version = "0.2", default-features = false }
approx = { version = "0.3", default-features = false }
simba = { version = "0.2", default-features = false }
-nalgebra = { path = "..", version = "0.21", default-features = false }
+nalgebra = { path = "..", version = "0.22", default-features = false }
From c1eaac2fae30d7269e693b1dacab93af4d5a6248 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Crozet?=
Date: Tue, 25 Aug 2020 21:16:18 +0200
Subject: [PATCH 24/55] Release nalgebra-lapack 0.14.0
---
nalgebra-lapack/Cargo.toml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nalgebra-lapack/Cargo.toml b/nalgebra-lapack/Cargo.toml
index 712c19e7..c5e397fb 100644
--- a/nalgebra-lapack/Cargo.toml
+++ b/nalgebra-lapack/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "nalgebra-lapack"
-version = "0.13.0"
+version = "0.14.0"
authors = [ "Sébastien Crozet ", "Andrew Straw " ]
description = "Linear algebra library with transformations and satically-sized or dynamically-sized matrices."
@@ -23,7 +23,7 @@ accelerate = ["lapack-src/accelerate"]
intel-mkl = ["lapack-src/intel-mkl"]
[dependencies]
-nalgebra = { version = "0.21", path = ".." }
+nalgebra = { version = "0.22", path = ".." }
num-traits = "0.2"
num-complex = { version = "0.2", default-features = false }
simba = "0.2"
@@ -34,7 +34,7 @@ lapack-src = { version = "0.5", default-features = false }
# clippy = "*"
[dev-dependencies]
-nalgebra = { version = "0.21", path = "..", features = [ "arbitrary" ] }
+nalgebra = { version = "0.22", path = "..", features = [ "arbitrary" ] }
quickcheck = "0.9"
approx = "0.3"
rand = "0.7"
From c6d5d8a1a67d9eff236088e7a5284e0970fdf6ed Mon Sep 17 00:00:00 2001
From: Michael Morgan
Date: Fri, 9 Oct 2020 14:14:14 -0400
Subject: [PATCH 25/55] Remove unsafe statement in Point::deref by forwarding
to Vector.
Since both impls are #[inline], this should have no performance impact.
---
src/geometry/point_coordinates.rs | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/geometry/point_coordinates.rs b/src/geometry/point_coordinates.rs
index b56e120e..268c9b0d 100644
--- a/src/geometry/point_coordinates.rs
+++ b/src/geometry/point_coordinates.rs
@@ -1,4 +1,3 @@
-use std::mem;
use std::ops::{Deref, DerefMut};
use crate::base::allocator::Allocator;
@@ -22,7 +21,7 @@ macro_rules! deref_impl(
#[inline]
fn deref(&self) -> &Self::Target {
- unsafe { mem::transmute(self) }
+ &*self.coords
}
}
@@ -30,7 +29,7 @@ macro_rules! deref_impl(
where DefaultAllocator: Allocator {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
- unsafe { mem::transmute(self) }
+ &mut *self.coords
}
}
}
From fb15658cc977d992db2ebd6c1549292611375e87 Mon Sep 17 00:00:00 2001
From: Philippe Renon
Date: Sun, 11 Oct 2020 10:41:25 +0200
Subject: [PATCH 26/55] fix typo: apsect should be aspect
---
nalgebra-glm/src/ext/matrix_clip_space.rs | 8 ++++----
src/geometry/orthographic.rs | 2 +-
src/geometry/perspective.rs | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/nalgebra-glm/src/ext/matrix_clip_space.rs b/nalgebra-glm/src/ext/matrix_clip_space.rs
index 260c5196..5ee725ae 100644
--- a/nalgebra-glm/src/ext/matrix_clip_space.rs
+++ b/nalgebra-glm/src/ext/matrix_clip_space.rs
@@ -530,7 +530,7 @@ pub fn perspective_lh_no(aspect: N, fovy: N, near: N, far: N) -> T
);
assert!(
!relative_eq!(aspect, N::zero()),
- "The apsect ratio must not be zero."
+ "The aspect ratio must not be zero."
);
let one = N::one();
@@ -566,7 +566,7 @@ pub fn perspective_lh_zo(aspect: N, fovy: N, near: N, far: N) -> T
);
assert!(
!relative_eq!(aspect, N::zero()),
- "The apsect ratio must not be zero."
+ "The aspect ratio must not be zero."
);
let one = N::one();
@@ -632,7 +632,7 @@ pub fn perspective_rh_no(aspect: N, fovy: N, near: N, far: N) -> T
);
assert!(
!relative_eq!(aspect, N::zero()),
- "The apsect ratio must not be zero."
+ "The aspect ratio must not be zero."
);
let negone = -N::one();
@@ -669,7 +669,7 @@ pub fn perspective_rh_zo(aspect: N, fovy: N, near: N, far: N) -> T
);
assert!(
!relative_eq!(aspect, N::zero()),
- "The apsect ratio must not be zero."
+ "The aspect ratio must not be zero."
);
let negone = -N::one();
diff --git a/src/geometry/orthographic.rs b/src/geometry/orthographic.rs
index a7d5372f..c9bc76e6 100644
--- a/src/geometry/orthographic.rs
+++ b/src/geometry/orthographic.rs
@@ -151,7 +151,7 @@ impl Orthographic3 {
);
assert!(
!relative_eq!(aspect, N::zero()),
- "The apsect ratio must not be zero."
+ "The aspect ratio must not be zero."
);
let half: N = crate::convert(0.5);
diff --git a/src/geometry/perspective.rs b/src/geometry/perspective.rs
index 325a450c..ed7649fd 100644
--- a/src/geometry/perspective.rs
+++ b/src/geometry/perspective.rs
@@ -75,7 +75,7 @@ impl Perspective3 {
);
assert!(
!relative_eq!(aspect, N::zero()),
- "The apsect ratio must not be zero."
+ "The aspect ratio must not be zero."
);
let matrix = Matrix4::identity();
From 6293d3375bb0095c0ba7fb89fa78c340f378ccfd Mon Sep 17 00:00:00 2001
From: Philippe Renon
Date: Sun, 11 Oct 2020 10:57:26 +0200
Subject: [PATCH 27/55] clippy: fix #redundant_field_names
---
src/base/dimension.rs | 2 +-
src/base/matrix.rs | 2 +-
src/base/vec_storage.rs | 6 +-----
src/geometry/orthographic.rs | 2 +-
src/geometry/perspective.rs | 2 +-
src/geometry/point.rs | 2 +-
src/geometry/rotation.rs | 2 +-
src/geometry/transform.rs | 2 +-
src/geometry/translation.rs | 2 +-
src/linalg/full_piv_lu.rs | 12 ++----------
src/linalg/lu.rs | 4 ++--
src/linalg/qr.rs | 10 ++--------
src/linalg/schur.rs | 6 ++----
13 files changed, 17 insertions(+), 37 deletions(-)
diff --git a/src/base/dimension.rs b/src/base/dimension.rs
index c171ff23..3f77998e 100644
--- a/src/base/dimension.rs
+++ b/src/base/dimension.rs
@@ -23,7 +23,7 @@ impl Dynamic {
/// A dynamic size equal to `value`.
#[inline]
pub fn new(value: usize) -> Self {
- Self { value: value }
+ Self { value }
}
}
diff --git a/src/base/matrix.rs b/src/base/matrix.rs
index 6e6aaba5..ca7fa77b 100644
--- a/src/base/matrix.rs
+++ b/src/base/matrix.rs
@@ -191,7 +191,7 @@ impl Matrix {
#[inline]
pub unsafe fn from_data_statically_unchecked(data: S) -> Matrix {
Matrix {
- data: data,
+ data,
_phantoms: PhantomData,
}
}
diff --git a/src/base/vec_storage.rs b/src/base/vec_storage.rs
index 26948693..40909c32 100644
--- a/src/base/vec_storage.rs
+++ b/src/base/vec_storage.rs
@@ -41,11 +41,7 @@ impl VecStorage {
nrows.value() * ncols.value() == data.len(),
"Data storage buffer dimension mismatch."
);
- Self {
- data: data,
- nrows: nrows,
- ncols: ncols,
- }
+ Self { data, nrows, ncols }
}
/// The underlying data storage.
diff --git a/src/geometry/orthographic.rs b/src/geometry/orthographic.rs
index c9bc76e6..9f0eadaf 100644
--- a/src/geometry/orthographic.rs
+++ b/src/geometry/orthographic.rs
@@ -139,7 +139,7 @@ impl Orthographic3 {
/// ```
#[inline]
pub fn from_matrix_unchecked(matrix: Matrix4) -> Self {
- Self { matrix: matrix }
+ Self { matrix }
}
/// Creates a new orthographic projection matrix from an aspect ratio and the vertical field of view.
diff --git a/src/geometry/perspective.rs b/src/geometry/perspective.rs
index ed7649fd..db283609 100644
--- a/src/geometry/perspective.rs
+++ b/src/geometry/perspective.rs
@@ -97,7 +97,7 @@ impl Perspective3 {
/// projection.
#[inline]
pub fn from_matrix_unchecked(matrix: Matrix4) -> Self {
- Self { matrix: matrix }
+ Self { matrix }
}
/// Retrieves the inverse of the underlying homogeneous matrix.
diff --git a/src/geometry/point.rs b/src/geometry/point.rs
index ab26589b..d12b9212 100644
--- a/src/geometry/point.rs
+++ b/src/geometry/point.rs
@@ -135,7 +135,7 @@ where
#[deprecated(note = "Use Point::from(vector) instead.")]
#[inline]
pub fn from_coordinates(coords: VectorN) -> Self {
- Self { coords: coords }
+ Self { coords }
}
/// The dimension of this point.
diff --git a/src/geometry/rotation.rs b/src/geometry/rotation.rs
index c6cd3d86..bb3fc235 100755
--- a/src/geometry/rotation.rs
+++ b/src/geometry/rotation.rs
@@ -255,7 +255,7 @@ where
"Unable to create a rotation from a non-square matrix."
);
- Self { matrix: matrix }
+ Self { matrix }
}
/// Transposes `self`.
diff --git a/src/geometry/transform.rs b/src/geometry/transform.rs
index 42586509..80b72771 100755
--- a/src/geometry/transform.rs
+++ b/src/geometry/transform.rs
@@ -245,7 +245,7 @@ where
#[inline]
pub fn from_matrix_unchecked(matrix: MatrixN>) -> Self {
Transform {
- matrix: matrix,
+ matrix,
_phantom: PhantomData,
}
}
diff --git a/src/geometry/translation.rs b/src/geometry/translation.rs
index 88d52ce4..26bbfd12 100755
--- a/src/geometry/translation.rs
+++ b/src/geometry/translation.rs
@@ -119,7 +119,7 @@ where
#[inline]
#[deprecated(note = "Use `::from` instead.")]
pub fn from_vector(vector: VectorN) -> Translation {
- Translation { vector: vector }
+ Translation { vector }
}
/// Inverts `self`.
diff --git a/src/linalg/full_piv_lu.rs b/src/linalg/full_piv_lu.rs
index bfa6854f..146b2cb7 100644
--- a/src/linalg/full_piv_lu.rs
+++ b/src/linalg/full_piv_lu.rs
@@ -60,11 +60,7 @@ where
let mut q = PermutationSequence::identity_generic(min_nrows_ncols);
if min_nrows_ncols.value() == 0 {
- return Self {
- lu: matrix,
- p: p,
- q: q,
- };
+ return Self { lu: matrix, p, q };
}
for i in 0..min_nrows_ncols.value() {
@@ -90,11 +86,7 @@ where
}
}
- Self {
- lu: matrix,
- p: p,
- q: q,
- }
+ Self { lu: matrix, p, q }
}
#[doc(hidden)]
diff --git a/src/linalg/lu.rs b/src/linalg/lu.rs
index b3acaaa0..6aa84d62 100644
--- a/src/linalg/lu.rs
+++ b/src/linalg/lu.rs
@@ -96,7 +96,7 @@ where
let mut p = PermutationSequence::identity_generic(min_nrows_ncols);
if min_nrows_ncols.value() == 0 {
- return LU { lu: matrix, p: p };
+ return LU { lu: matrix, p };
}
for i in 0..min_nrows_ncols.value() {
@@ -117,7 +117,7 @@ where
}
}
- LU { lu: matrix, p: p }
+ LU { lu: matrix, p }
}
#[doc(hidden)]
diff --git a/src/linalg/qr.rs b/src/linalg/qr.rs
index f218d47b..48d7a896 100644
--- a/src/linalg/qr.rs
+++ b/src/linalg/qr.rs
@@ -57,20 +57,14 @@ where
let mut diag = unsafe { MatrixMN::new_uninitialized_generic(min_nrows_ncols, U1) };
if min_nrows_ncols.value() == 0 {
- return QR {
- qr: matrix,
- diag: diag,
- };
+ return QR { qr: matrix, diag };
}
for ite in 0..min_nrows_ncols.value() {
householder::clear_column_unchecked(&mut matrix, &mut diag[ite], ite, 0, None);
}
- QR {
- qr: matrix,
- diag: diag,
- }
+ QR { qr: matrix, diag }
}
/// Retrieves the upper trapezoidal submatrix `R` of this decomposition.
diff --git a/src/linalg/schur.rs b/src/linalg/schur.rs
index 99e73ea1..acdcd3dd 100644
--- a/src/linalg/schur.rs
+++ b/src/linalg/schur.rs
@@ -73,10 +73,8 @@ where
pub fn try_new(m: MatrixN, eps: N::RealField, max_niter: usize) -> Option {
let mut work = unsafe { VectorN::new_uninitialized_generic(m.data.shape().0, U1) };
- Self::do_decompose(m, &mut work, eps, max_niter, true).map(|(q, t)| Schur {
- q: q.unwrap(),
- t: t,
- })
+ Self::do_decompose(m, &mut work, eps, max_niter, true)
+ .map(|(q, t)| Schur { q: q.unwrap(), t })
}
fn do_decompose(
From 1624a87efa9eab6d6a996ffde0da507074b7e805 Mon Sep 17 00:00:00 2001
From: Philippe Renon
Date: Sun, 11 Oct 2020 11:01:20 +0200
Subject: [PATCH 28/55] clippy: fix #needless_return
---
src/geometry/quaternion_construction.rs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/geometry/quaternion_construction.rs b/src/geometry/quaternion_construction.rs
index a59d3030..33e05867 100644
--- a/src/geometry/quaternion_construction.rs
+++ b/src/geometry/quaternion_construction.rs
@@ -492,18 +492,18 @@ where
// The cosinus may be out of [-1, 1] because of inaccuracies.
if cos <= -N::one() {
- return None;
+ None
} else if cos >= N::one() {
- return Some(Self::identity());
+ Some(Self::identity())
} else {
- return Some(Self::from_axis_angle(&axis, cos.acos() * s));
+ Some(Self::from_axis_angle(&axis, cos.acos() * s))
}
} else if na.dot(&nb) < N::zero() {
// PI
//
// The rotation axis is undefined but the angle not zero. This is not a
// simple rotation.
- return None;
+ None
} else {
// Zero
Some(Self::identity())
From 0b0f248267e3822fe40878e783e49de636c0eac7 Mon Sep 17 00:00:00 2001
From: Philippe Renon
Date: Sun, 11 Oct 2020 11:23:05 +0200
Subject: [PATCH 29/55] clippy: fix #identity_op
---
src/base/blas.rs | 4 ++--
src/linalg/schur.rs | 8 ++++----
src/linalg/svd.rs | 6 +++---
src/linalg/symmetric_eigen.rs | 2 +-
4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/base/blas.rs b/src/base/blas.rs
index 1d61b9d2..c0615066 100644
--- a/src/base/blas.rs
+++ b/src/base/blas.rs
@@ -363,8 +363,8 @@ where
while self.nrows() - i >= 8 {
acc0 += unsafe {
- conjugate(self.get_unchecked((i + 0, j)).inlined_clone())
- * rhs.get_unchecked((i + 0, j)).inlined_clone()
+ conjugate(self.get_unchecked((i, j)).inlined_clone())
+ * rhs.get_unchecked((i, j)).inlined_clone()
};
acc1 += unsafe {
conjugate(self.get_unchecked((i + 1, j)).inlined_clone())
diff --git a/src/linalg/schur.rs b/src/linalg/schur.rs
index acdcd3dd..8d8be72d 100644
--- a/src/linalg/schur.rs
+++ b/src/linalg/schur.rs
@@ -136,9 +136,9 @@ where
let m = end - 1;
let n = end;
- let h11 = t[(start + 0, start + 0)];
- let h12 = t[(start + 0, start + 1)];
- let h21 = t[(start + 1, start + 0)];
+ let h11 = t[(start, start)];
+ let h12 = t[(start, start + 1)];
+ let h21 = t[(start + 1, start)];
let h22 = t[(start + 1, start + 1)];
let h32 = t[(start + 2, start + 1)];
@@ -161,7 +161,7 @@ where
if not_zero {
if k > start {
- t[(k + 0, k - 1)] = norm;
+ t[(k, k - 1)] = norm;
t[(k + 1, k - 1)] = N::zero();
t[(k + 2, k - 1)] = N::zero();
}
diff --git a/src/linalg/svd.rs b/src/linalg/svd.rs
index 4be0ecdb..94ec4d96 100644
--- a/src/linalg/svd.rs
+++ b/src/linalg/svd.rs
@@ -218,9 +218,9 @@ where
}
}
- diagonal[k + 0] = subm[(0, 0)];
+ diagonal[k] = subm[(0, 0)];
diagonal[k + 1] = subm[(1, 1)];
- off_diagonal[k + 0] = subm[(0, 1)];
+ off_diagonal[k] = subm[(0, 1)];
if k != n - 1 {
off_diagonal[k + 1] = subm[(1, 2)];
@@ -244,7 +244,7 @@ where
let u2 = u2.map(|u2| GivensRotation::new_unchecked(u2.c(), N::from_real(u2.s())));
let v2 = v2.map(|v2| GivensRotation::new_unchecked(v2.c(), N::from_real(v2.s())));
- diagonal[start + 0] = s[0];
+ diagonal[start] = s[0];
diagonal[start + 1] = s[1];
off_diagonal[start] = N::RealField::zero();
diff --git a/src/linalg/symmetric_eigen.rs b/src/linalg/symmetric_eigen.rs
index 70f57cdf..2d62816e 100644
--- a/src/linalg/symmetric_eigen.rs
+++ b/src/linalg/symmetric_eigen.rs
@@ -192,7 +192,7 @@ where
let eigvals = m.eigenvalues().unwrap();
let basis = Vector2::new(eigvals.x - diag[start + 1], off_diag[start]);
- diag[start + 0] = eigvals[0];
+ diag[start] = eigvals[0];
diag[start + 1] = eigvals[1];
if let Some(ref mut q) = q {
From 822f114254652c6744d6404f9cd194c80095db65 Mon Sep 17 00:00:00 2001
From: Philippe Renon
Date: Sun, 11 Oct 2020 11:57:43 +0200
Subject: [PATCH 30/55] perspective: fix copy/paste error
---
src/geometry/perspective.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/geometry/perspective.rs b/src/geometry/perspective.rs
index db283609..f550bcc6 100644
--- a/src/geometry/perspective.rs
+++ b/src/geometry/perspective.rs
@@ -294,7 +294,7 @@ impl Arbitrary for Perspective3 {
impl From> for Matrix4 {
#[inline]
- fn from(orth: Perspective3) -> Self {
- orth.into_inner()
+ fn from(pers: Perspective3) -> Self {
+ pers.into_inner()
}
}
From 343fb2f24f1486ec01fb70c1b415de1d9544e470 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Tue, 13 Oct 2020 10:12:14 +0200
Subject: [PATCH 31/55] Ensure Isometry implements Copy when targeting no-std.
Fix #774.
---
src/geometry/isometry.rs | 10 ++++++----
src/geometry/isometry_alias.rs | 12 ++++++++++++
2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs
index 2e4b7cd5..a4e16abe 100755
--- a/src/geometry/isometry.rs
+++ b/src/geometry/isometry.rs
@@ -82,21 +82,23 @@ where
}
}
-impl + Copy> Copy
- for Isometry
+impl Copy for Isometry
where
DefaultAllocator: Allocator,
Owned: Copy,
{
}
-impl + Clone> Clone for Isometry
+impl Clone for Isometry
where
DefaultAllocator: Allocator,
{
#[inline]
fn clone(&self) -> Self {
- Self::from_parts(self.translation.clone(), self.rotation.clone())
+ Self {
+ rotation: self.rotation.clone(),
+ translation: self.translation.clone(),
+ }
}
}
diff --git a/src/geometry/isometry_alias.rs b/src/geometry/isometry_alias.rs
index ba9a69e7..69d33cb4 100644
--- a/src/geometry/isometry_alias.rs
+++ b/src/geometry/isometry_alias.rs
@@ -13,3 +13,15 @@ pub type IsometryMatrix2 = Isometry>;
/// A 3-dimensional direct isometry using a rotation matrix for its rotational part. Also known as a rigid-body motion, or as an element of SE(3).
pub type IsometryMatrix3 = Isometry>;
+
+// This tests that the types correctly implement `Copy`, without having to run tests
+// (when targeting no-std for example).
+#[allow(dead_code)]
+fn ensure_copy() {
+ fn is_copy() {}
+
+ is_copy::>();
+ is_copy::>();
+ is_copy::>();
+ is_copy::>();
+}
From 07dc799bbd840c114e352be9dcb52795263fdad3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Tue, 13 Oct 2020 10:28:30 +0200
Subject: [PATCH 32/55] Release v0.22.1
---
Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Cargo.toml b/Cargo.toml
index e4be2462..c98c13dc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "nalgebra"
-version = "0.22.0"
+version = "0.22.1"
authors = [ "Sébastien Crozet " ]
description = "Linear algebra library with transformations and statically-sized or dynamically-sized matrices."
From 4da2bfcb717764fb6e97df3ea0e9c168776f2c84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Tue, 13 Oct 2020 17:23:08 +0200
Subject: [PATCH 33/55] Add missing dot-product dimension check.
Fix #776
---
src/base/blas.rs | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/base/blas.rs b/src/base/blas.rs
index c0615066..f245f24a 100644
--- a/src/base/blas.rs
+++ b/src/base/blas.rs
@@ -289,6 +289,13 @@ where
rhs.shape(),
);
+ assert!(
+ self.ncols() == rhs.ncols(),
+ "Dot product dimensions mismatch for shapes {:?} and {:?}: left cols != right cols.",
+ self.shape(),
+ rhs.shape(),
+ );
+
// So we do some special cases for common fixed-size vectors of dimension lower than 8
// because the `for` loop below won't be very efficient on those.
if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) {
From 3d82c4335e1802290ce2b845dbc2ce3147f22aaf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Sun, 25 Oct 2020 11:23:34 +0100
Subject: [PATCH 34/55] Add inverse_transform_unit_vector to rotations and
isometries.
---
src/geometry/abstract_rotation.rs | 17 ++++++++++++++++-
src/geometry/isometry.rs | 25 ++++++++++++++++++++++++-
src/geometry/quaternion.rs | 20 ++++++++++++++++++++
src/geometry/rotation.rs | 21 ++++++++++++++++++++-
src/geometry/unit_complex.rs | 16 ++++++++++++++++
5 files changed, 96 insertions(+), 3 deletions(-)
diff --git a/src/geometry/abstract_rotation.rs b/src/geometry/abstract_rotation.rs
index ff057a09..52471851 100644
--- a/src/geometry/abstract_rotation.rs
+++ b/src/geometry/abstract_rotation.rs
@@ -1,6 +1,6 @@
use crate::allocator::Allocator;
use crate::geometry::{Rotation, UnitComplex, UnitQuaternion};
-use crate::{DefaultAllocator, DimName, Point, Scalar, SimdRealField, VectorN, U2, U3};
+use crate::{DefaultAllocator, DimName, Point, Scalar, SimdRealField, Unit, VectorN, U2, U3};
use simba::scalar::ClosedMul;
@@ -24,6 +24,13 @@ pub trait AbstractRotation: PartialEq + ClosedMul + Clone
fn inverse_transform_vector(&self, v: &VectorN) -> VectorN
where
DefaultAllocator: Allocator;
+ /// Apply the inverse rotation to the given unit vector.
+ fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit>
+ where
+ DefaultAllocator: Allocator,
+ {
+ Unit::new_unchecked(self.inverse_transform_vector(&**v))
+ }
/// Apply the inverse rotation to the given point.
fn inverse_transform_point(&self, p: &Point) -> Point
where
@@ -74,6 +81,14 @@ where
self.inverse_transform_vector(v)
}
+ #[inline]
+ fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit>
+ where
+ DefaultAllocator: Allocator,
+ {
+ self.inverse_transform_unit_vector(v)
+ }
+
#[inline]
fn inverse_transform_point(&self, p: &Point) -> Point
where
diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs
index a4e16abe..b59c8b60 100755
--- a/src/geometry/isometry.rs
+++ b/src/geometry/isometry.rs
@@ -16,7 +16,7 @@ use simba::simd::SimdRealField;
use crate::base::allocator::Allocator;
use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1};
use crate::base::storage::Owned;
-use crate::base::{DefaultAllocator, MatrixN, Scalar, VectorN};
+use crate::base::{DefaultAllocator, MatrixN, Scalar, Unit, VectorN};
use crate::geometry::{AbstractRotation, Point, Translation};
/// A direct isometry, i.e., a rotation followed by a translation, aka. a rigid-body motion, aka. an element of a Special Euclidean (SE) group.
@@ -350,6 +350,29 @@ where
pub fn inverse_transform_vector(&self, v: &VectorN) -> VectorN {
self.rotation.inverse_transform_vector(v)
}
+
+ /// Transform the given unit vector by the inverse of this isometry, ignoring the
+ /// translation component of the isometry. This may be
+ /// less expensive than computing the entire isometry inverse and then
+ /// transforming the point.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # #[macro_use] extern crate approx;
+ /// # use std::f32;
+ /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3};
+ /// let tra = Translation3::new(0.0, 0.0, 3.0);
+ /// let rot = UnitQuaternion::from_scaled_axis(Vector3::z() * f32::consts::FRAC_PI_2);
+ /// let iso = Isometry3::from_parts(tra, rot);
+ ///
+ /// let transformed_point = iso.inverse_transform_unit_vector(&Vector3::x_axis());
+ /// assert_relative_eq!(transformed_point, -Vector3::y_axis(), epsilon = 1.0e-6);
+ /// ```
+ #[inline]
+ pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> {
+ self.rotation.inverse_transform_unit_vector(v)
+ }
}
// NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation
diff --git a/src/geometry/quaternion.rs b/src/geometry/quaternion.rs
index 0c996ede..22ee5309 100755
--- a/src/geometry/quaternion.rs
+++ b/src/geometry/quaternion.rs
@@ -1542,6 +1542,26 @@ where
pub fn inverse_transform_vector(&self, v: &Vector3) -> Vector3 {
self.inverse() * v
}
+
+ /// Rotate a vector by the inverse of this unit quaternion. This may be
+ /// cheaper than inverting the unit quaternion and transforming the
+ /// vector.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # #[macro_use] extern crate approx;
+ /// # use std::f32;
+ /// # use nalgebra::{UnitQuaternion, Vector3};
+ /// let rot = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_2);
+ /// let transformed_vector = rot.inverse_transform_unit_vector(&Vector3::x_axis());
+ ///
+ /// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6);
+ /// ```
+ #[inline]
+ pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> {
+ self.inverse() * v
+ }
}
impl Default for UnitQuaternion {
diff --git a/src/geometry/rotation.rs b/src/geometry/rotation.rs
index bb3fc235..c30a3a97 100755
--- a/src/geometry/rotation.rs
+++ b/src/geometry/rotation.rs
@@ -19,7 +19,7 @@ use simba::simd::SimdRealField;
use crate::base::allocator::Allocator;
use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1};
-use crate::base::{DefaultAllocator, MatrixN, Scalar, VectorN};
+use crate::base::{DefaultAllocator, MatrixN, Scalar, Unit, VectorN};
use crate::geometry::Point;
/// A rotation matrix.
@@ -441,6 +441,25 @@ where
pub fn inverse_transform_vector(&self, v: &VectorN) -> VectorN {
self.matrix().tr_mul(v)
}
+
+ /// Rotate the given vector by the inverse of this rotation. This may be
+ /// cheaper than inverting the rotation and then transforming the given
+ /// vector.
+ ///
+ /// # Example
+ /// ```
+ /// # #[macro_use] extern crate approx;
+ /// # use std::f32;
+ /// # use nalgebra::{Rotation2, Rotation3, UnitQuaternion, Vector3};
+ /// let rot = Rotation3::new(Vector3::z() * f32::consts::FRAC_PI_2);
+ /// let transformed_vector = rot.inverse_transform_unit_vector(&Vector3::x_axis());
+ ///
+ /// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6);
+ /// ```
+ #[inline]
+ pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> {
+ Unit::new_unchecked(self.inverse_transform_vector(&**v))
+ }
}
impl Eq for Rotation where DefaultAllocator: Allocator {}
diff --git a/src/geometry/unit_complex.rs b/src/geometry/unit_complex.rs
index a8ac5bcd..723ac6c0 100755
--- a/src/geometry/unit_complex.rs
+++ b/src/geometry/unit_complex.rs
@@ -360,6 +360,22 @@ where
pub fn inverse_transform_vector(&self, v: &Vector2) -> Vector2 {
self.inverse() * v
}
+
+ /// Rotate the given vector by the inverse of this unit complex number.
+ ///
+ /// # Example
+ /// ```
+ /// # #[macro_use] extern crate approx;
+ /// # use nalgebra::{UnitComplex, Vector2};
+ /// # use std::f32;
+ /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2);
+ /// let transformed_vector = rot.inverse_transform_unit_vector(&Vector2::x_axis());
+ /// assert_relative_eq!(transformed_vector, -Vector2::y_axis(), epsilon = 1.0e-6);
+ /// ```
+ #[inline]
+ pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> {
+ self.inverse() * v
+ }
}
impl fmt::Display for UnitComplex {
From a623e63d08b1e11a73df0237c06e011987d1606a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Sun, 25 Oct 2020 11:23:51 +0100
Subject: [PATCH 35/55] Add the conversion of an array of four elements to a
quaternion.
---
src/geometry/quaternion_conversion.rs | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/geometry/quaternion_conversion.rs b/src/geometry/quaternion_conversion.rs
index 37a8bebd..330c97e4 100644
--- a/src/geometry/quaternion_conversion.rs
+++ b/src/geometry/quaternion_conversion.rs
@@ -265,6 +265,15 @@ impl From> for Quaternion {
}
}
+impl From<[N; 4]> for Quaternion {
+ #[inline]
+ fn from(coords: [N; 4]) -> Self {
+ Self {
+ coords: coords.into(),
+ }
+ }
+}
+
impl From<[Quaternion; 2]> for Quaternion
where
N: From<[::Element; 2]>,
From 503040b8bce4956b1d54e824819fcf7b89976763 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Sun, 25 Oct 2020 11:24:05 +0100
Subject: [PATCH 36/55] Add Point.map(f) and Point.apply(f).
---
src/geometry/point.rs | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/src/geometry/point.rs b/src/geometry/point.rs
index d12b9212..e0c26054 100644
--- a/src/geometry/point.rs
+++ b/src/geometry/point.rs
@@ -102,6 +102,45 @@ impl Point
where
DefaultAllocator: Allocator,
{
+ /// Returns a point containing the result of `f` applied to each of its entries.
+ ///
+ /// # Example
+ /// ```
+ /// # use nalgebra::{Point2, Point3};
+ /// let p = Point2::new(1.0, 2.0);
+ /// assert_eq!(p.map(|e| e * 10.0), Point2::new(10.0, 20.0));
+ ///
+ /// // This works in any dimension.
+ /// let p = Point3::new(1.1, 2.1, 3.1);
+ /// assert_eq!(p.map(|e| e as u32), Point3::new(1, 2, 3));
+ /// ```
+ #[inline]
+ pub fn map N2>(&self, f: F) -> Point
+ where
+ DefaultAllocator: Allocator,
+ {
+ self.coords.map(f).into()
+ }
+
+ /// Replaces each component of `self` by the result of a closure `f` applied on it.
+ ///
+ /// # Example
+ /// ```
+ /// # use nalgebra::{Point2, Point3};
+ /// let mut p = Point2::new(1.0, 2.0);
+ /// p.apply(|e| e * 10.0);
+ /// assert_eq!(p, Point2::new(10.0, 20.0));
+ ///
+ /// // This works in any dimension.
+ /// let mut p = Point3::new(1.0, 2.0, 3.0);
+ /// p.apply(|e| e * 10.0);
+ /// assert_eq!(p, Point3::new(10.0, 20.0, 30.0));
+ /// ```
+ #[inline]
+ pub fn apply N>(&mut self, f: F) {
+ self.coords.apply(f)
+ }
+
/// Converts this point into a vector in homogeneous coordinates, i.e., appends a `1` at the
/// end of it.
///
From 55689c238ae8f4933f29f2b253a811e5ca18b1b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Sun, 25 Oct 2020 11:24:20 +0100
Subject: [PATCH 37/55] Add the conversion of a translation to an isometry.
---
src/geometry/isometry_conversion.rs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/src/geometry/isometry_conversion.rs b/src/geometry/isometry_conversion.rs
index 7cb6fe85..bdb128f9 100644
--- a/src/geometry/isometry_conversion.rs
+++ b/src/geometry/isometry_conversion.rs
@@ -151,6 +151,17 @@ where
}
}
+impl> From>
+ for Isometry
+where
+ DefaultAllocator: Allocator,
+{
+ #[inline]
+ fn from(tra: Translation) -> Self {
+ Self::from_parts(tra, R::identity())
+ }
+}
+
impl From> for MatrixN>
where
D: DimNameAdd,
From 9c93a58b5d648697806075dc2cc41cf33cf33464 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Sun, 25 Oct 2020 11:25:38 +0100
Subject: [PATCH 38/55] Add Vector::ith_axis to build an unit vector with its
i-th component set to 1.
---
src/base/construction.rs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/base/construction.rs b/src/base/construction.rs
index f810acdf..aa4cf956 100644
--- a/src/base/construction.rs
+++ b/src/base/construction.rs
@@ -1019,6 +1019,12 @@ where
res
}
+ /// The column unit vector with `N::one()` as its i-th component.
+ #[inline]
+ pub fn ith_axis(i: usize) -> Unit {
+ Unit::new_unchecked(Self::ith(i, N::one()))
+ }
+
/// The column vector with a 1 as its first component, and zero elsewhere.
#[inline]
pub fn x() -> Self
From 097ae44efad1b52f6f1e31df99f6510e52b34979 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Sun, 25 Oct 2020 11:39:27 +0100
Subject: [PATCH 39/55] Add `lerp_slerp` to isometries for interpolation.
---
src/geometry/abstract_rotation.rs | 44 +++++++++++++++++++++
src/geometry/isometry.rs | 66 +++++++++++++++++++++++++++++++
2 files changed, 110 insertions(+)
diff --git a/src/geometry/abstract_rotation.rs b/src/geometry/abstract_rotation.rs
index 52471851..e1cf7c10 100644
--- a/src/geometry/abstract_rotation.rs
+++ b/src/geometry/abstract_rotation.rs
@@ -35,6 +35,14 @@ pub trait AbstractRotation: PartialEq + ClosedMul + Clone
fn inverse_transform_point(&self, p: &Point) -> Point
where
DefaultAllocator: Allocator;
+ /// Perfom a spherical interpolation between two rolations.
+ fn slerp(&self, other: &Self, t: N) -> Self
+ where
+ DefaultAllocator: Allocator;
+ /// Attempts to perfom a spherical interpolation between two rolations.
+ fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option
+ where
+ DefaultAllocator: Allocator;
}
impl AbstractRotation for Rotation
@@ -96,6 +104,22 @@ where
{
self.inverse_transform_point(p)
}
+
+ #[inline]
+ fn slerp(&self, other: &Self, t: N) -> Self
+ where
+ DefaultAllocator: Allocator,
+ {
+ self.slerp(other, t)
+ }
+
+ #[inline]
+ fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option
+ where
+ DefaultAllocator: Allocator,
+ {
+ self.try_slerp(other, t, epsilon)
+ }
}
impl AbstractRotation for UnitQuaternion
@@ -136,6 +160,16 @@ where
fn inverse_transform_point(&self, p: &Point) -> Point {
self.inverse_transform_point(p)
}
+
+ #[inline]
+ fn slerp(&self, other: &Self, t: N) -> Self {
+ self.slerp(other, t)
+ }
+
+ #[inline]
+ fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option {
+ self.try_slerp(other, t, epsilon)
+ }
}
impl AbstractRotation for UnitComplex
@@ -176,4 +210,14 @@ where
fn inverse_transform_point(&self, p: &Point) -> Point {
self.inverse_transform_point(p)
}
+
+ #[inline]
+ fn slerp(&self, other: &Self, t: N) -> Self {
+ self.slerp(other, t)
+ }
+
+ #[inline]
+ fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option {
+ self.try_slerp(other, t, epsilon)
+ }
}
diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs
index b59c8b60..5b7e5dc7 100755
--- a/src/geometry/isometry.rs
+++ b/src/geometry/isometry.rs
@@ -373,6 +373,72 @@ where
pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> {
self.rotation.inverse_transform_unit_vector(v)
}
+
+ /// Interpolates between two isometries using a linear interpolation for the translation part,
+ /// and a spherical interpolation for the rotation part.
+ ///
+ /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
+ /// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// # use nalgebra::geometry::{Translation3, UnitQuaternion};
+ ///
+ /// let t1 = Translation3::new(1.0, 2.0, 3.0);
+ /// let t2 = Translation3::new(3.0, 6.0, 9.0);
+ /// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
+ /// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
+ /// let iso1 = Isometry3::from_parts(t1, q1);
+ /// let iso2 = Isometry3::from_parts(t2, q2);
+ ///
+ /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
+ ///
+ /// assert_eq!(iso3.translation_vector, Vector3::new(2.0, 4.0, 6.0));
+ /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
+ /// ```
+ #[inline]
+ pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
+ where
+ N: RealField,
+ {
+ let tr = self.translation.vector.lerp(&other.translation.vector, t);
+ let rot = self.rotation.slerp(&other.rotation, t);
+ Self::from_parts(tr.into(), rot)
+ }
+
+ /// Attempts to interpolate between two isometries using a linear interpolation for the translation part,
+ /// and a spherical interpolation for the rotation part.
+ ///
+ /// Retuns `None` if the angle between both rotations is 180 degrees (in which case the interpolation
+ /// is not well-defined).
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// # use nalgebra::geometry::{Translation3, UnitQuaternion};
+ ///
+ /// let t1 = Translation3::new(1.0, 2.0, 3.0);
+ /// let t2 = Translation3::new(3.0, 6.0, 9.0);
+ /// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
+ /// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
+ /// let iso1 = Isometry3::from_parts(t1, q1);
+ /// let iso2 = Isometry3::from_parts(t2, q2);
+ ///
+ /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
+ ///
+ /// assert_eq!(iso3.translation_vector, Vector3::new(2.0, 4.0, 6.0));
+ /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
+ /// ```
+ #[inline]
+ pub fn try_lerp_slerp(&self, other: &Self, t: N, epsilon: N) -> Option
+ where
+ N: RealField,
+ {
+ let tr = self.translation.vector.lerp(&other.translation.vector, t);
+ let rot = self.rotation.try_slerp(&other.rotation, t, epsilon)?;
+ Some(Self::from_parts(tr.into(), rot))
+ }
}
// NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation
From 22b8fc92254a71f551d30c14ef9f5e657ea1fc81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Sun, 25 Oct 2020 11:39:38 +0100
Subject: [PATCH 40/55] Update che changelog.
---
CHANGELOG.md | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index db0cdc6b..1c158452 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,20 @@ documented here.
This project adheres to [Semantic Versioning](https://semver.org/).
+## [0.23.0] - WIP
+
+### Added
+ * The `.inverse_transform_unit_vector(v)` was added to `Rotation2/3`, `Isometry2/3`, `UnitQuaternion`, and `UnitComplex`.
+ It applies the corresponding rotation to a unit vector `Unit`.
+ * The `Point.map(f)` and `Point.apply(f)` to apply a function to each component of the point, similarly to `Vector.map(f)`
+ and `Vector.apply(f)`.
+ * The `Quaternion::from([N; 4])` conversion to build a quaternion from an array of four elements.
+ * The `Isometry::from(Translation)` conversion to build an isometry from a translation.
+ * The `Vector::ith_axis(i)` which build a unit vector, e.g., `Unit>` with its i-th component set to 1.0 and the
+ others set to zero.
+ * The `Isometry.lerp_slerp` and `Isometry.try_lerp_slerp` methods to interpolate between two isometries using linear
+ interpolation for the translational part, and spherical interpolation for the rotational part.
+
## [0.22.0]
In this release, we are using the new version 0.2 of simba. One major change of that version is that the
use of `libm` is now opt-in when building targetting `no-std` environment. If you are using floating-point
From 93f361cba8dbc5dad8445d712374d7ff11903156 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Sun, 25 Oct 2020 14:00:47 +0100
Subject: [PATCH 41/55] Add missing slerp implementations.
---
CHANGELOG.md | 2 +
src/geometry/abstract_rotation.rs | 44 -------
src/geometry/isometry.rs | 150 +++++++++++++++++++++++-
src/geometry/rotation_specialization.rs | 71 +++++++++++
src/geometry/unit_complex.rs | 20 ++++
5 files changed, 239 insertions(+), 48 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1c158452..853529f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
others set to zero.
* The `Isometry.lerp_slerp` and `Isometry.try_lerp_slerp` methods to interpolate between two isometries using linear
interpolation for the translational part, and spherical interpolation for the rotational part.
+ * The `Rotation2.slerp`, `Rotation3.slerp`, and `UnitQuaternion.slerp` method for
+ spherical interpolation.
## [0.22.0]
In this release, we are using the new version 0.2 of simba. One major change of that version is that the
diff --git a/src/geometry/abstract_rotation.rs b/src/geometry/abstract_rotation.rs
index e1cf7c10..52471851 100644
--- a/src/geometry/abstract_rotation.rs
+++ b/src/geometry/abstract_rotation.rs
@@ -35,14 +35,6 @@ pub trait AbstractRotation: PartialEq + ClosedMul + Clone
fn inverse_transform_point(&self, p: &Point) -> Point
where
DefaultAllocator: Allocator;
- /// Perfom a spherical interpolation between two rolations.
- fn slerp(&self, other: &Self, t: N) -> Self
- where
- DefaultAllocator: Allocator;
- /// Attempts to perfom a spherical interpolation between two rolations.
- fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option
- where
- DefaultAllocator: Allocator;
}
impl AbstractRotation for Rotation
@@ -104,22 +96,6 @@ where
{
self.inverse_transform_point(p)
}
-
- #[inline]
- fn slerp(&self, other: &Self, t: N) -> Self
- where
- DefaultAllocator: Allocator,
- {
- self.slerp(other, t)
- }
-
- #[inline]
- fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option
- where
- DefaultAllocator: Allocator,
- {
- self.try_slerp(other, t, epsilon)
- }
}
impl AbstractRotation for UnitQuaternion
@@ -160,16 +136,6 @@ where
fn inverse_transform_point(&self, p: &Point) -> Point {
self.inverse_transform_point(p)
}
-
- #[inline]
- fn slerp(&self, other: &Self, t: N) -> Self {
- self.slerp(other, t)
- }
-
- #[inline]
- fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option {
- self.try_slerp(other, t, epsilon)
- }
}
impl AbstractRotation for UnitComplex
@@ -210,14 +176,4 @@ where
fn inverse_transform_point(&self, p: &Point) -> Point {
self.inverse_transform_point(p)
}
-
- #[inline]
- fn slerp(&self, other: &Self, t: N) -> Self {
- self.slerp(other, t)
- }
-
- #[inline]
- fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option {
- self.try_slerp(other, t, epsilon)
- }
}
diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs
index 5b7e5dc7..c8eb34fb 100755
--- a/src/geometry/isometry.rs
+++ b/src/geometry/isometry.rs
@@ -14,10 +14,12 @@ use simba::scalar::{RealField, SubsetOf};
use simba::simd::SimdRealField;
use crate::base::allocator::Allocator;
-use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1};
+use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1, U2, U3};
use crate::base::storage::Owned;
use crate::base::{DefaultAllocator, MatrixN, Scalar, Unit, VectorN};
-use crate::geometry::{AbstractRotation, Point, Translation};
+use crate::geometry::{
+ AbstractRotation, Point, Rotation2, Rotation3, Translation, UnitComplex, UnitQuaternion,
+};
/// A direct isometry, i.e., a rotation followed by a translation, aka. a rigid-body motion, aka. an element of a Special Euclidean (SE) group.
#[repr(C)]
@@ -373,7 +375,9 @@ where
pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> {
self.rotation.inverse_transform_unit_vector(v)
}
+}
+impl Isometry> {
/// Interpolates between two isometries using a linear interpolation for the translation part,
/// and a spherical interpolation for the rotation part.
///
@@ -383,7 +387,7 @@ where
/// # Examples:
///
/// ```
- /// # use nalgebra::geometry::{Translation3, UnitQuaternion};
+ /// # use nalgebra::geometry::{Vector3, Translation3, UnitQuaternion};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(3.0, 6.0, 9.0);
@@ -416,7 +420,7 @@ where
/// # Examples:
///
/// ```
- /// # use nalgebra::geometry::{Translation3, UnitQuaternion};
+ /// # use nalgebra::geometry::{Vector3, Translation3, UnitQuaternion};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(3.0, 6.0, 9.0);
@@ -441,6 +445,144 @@ where
}
}
+impl Isometry> {
+ /// Interpolates between two isometries using a linear interpolation for the translation part,
+ /// and a spherical interpolation for the rotation part.
+ ///
+ /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
+ /// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// # use nalgebra::geometry::{Vector3, Translation3, Rotation3};
+ ///
+ /// let t1 = Translation3::new(1.0, 2.0, 3.0);
+ /// let t2 = Translation3::new(3.0, 6.0, 9.0);
+ /// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
+ /// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
+ /// let iso1 = Isometry3::from_parts(t1, q1);
+ /// let iso2 = Isometry3::from_parts(t2, q2);
+ ///
+ /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
+ ///
+ /// assert_eq!(iso3.translation_vector, Vector3::new(2.0, 4.0, 6.0));
+ /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
+ /// ```
+ #[inline]
+ pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
+ where
+ N: RealField,
+ {
+ let tr = self.translation.vector.lerp(&other.translation.vector, t);
+ let rot = self.rotation.slerp(&other.rotation, t);
+ Self::from_parts(tr.into(), rot)
+ }
+
+ /// Attempts to interpolate between two isometries using a linear interpolation for the translation part,
+ /// and a spherical interpolation for the rotation part.
+ ///
+ /// Retuns `None` if the angle between both rotations is 180 degrees (in which case the interpolation
+ /// is not well-defined).
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// # use nalgebra::geometry::{Vector3, Translation3, Rotation3};
+ ///
+ /// let t1 = Translation3::new(1.0, 2.0, 3.0);
+ /// let t2 = Translation3::new(3.0, 6.0, 9.0);
+ /// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
+ /// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
+ /// let iso1 = Isometry3::from_parts(t1, q1);
+ /// let iso2 = Isometry3::from_parts(t2, q2);
+ ///
+ /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
+ ///
+ /// assert_eq!(iso3.translation_vector, Vector3::new(2.0, 4.0, 6.0));
+ /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
+ /// ```
+ #[inline]
+ pub fn try_lerp_slerp(&self, other: &Self, t: N, epsilon: N) -> Option
+ where
+ N: RealField,
+ {
+ let tr = self.translation.vector.lerp(&other.translation.vector, t);
+ let rot = self.rotation.try_slerp(&other.rotation, t, epsilon)?;
+ Some(Self::from_parts(tr.into(), rot))
+ }
+}
+
+impl Isometry> {
+ /// Interpolates between two isometries using a linear interpolation for the translation part,
+ /// and a spherical interpolation for the rotation part.
+ ///
+ /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
+ /// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// # use nalgebra::geometry::{Vector2, Translation2, UnitComplex, Isometry2};
+ ///
+ /// let t1 = Translation2::new(1.0, 2.0);
+ /// let t2 = Translation2::new(3.0, 6.0);
+ /// let q1 = UnitComplex::new(std::f32::consts::FRAC_PI_4);
+ /// let q2 = UnitComplex::new(-std::f32::consts::PI);
+ /// let iso1 = Isometry2::from_parts(t1, q1);
+ /// let iso2 = Isometry2::from_parts(t2, q2);
+ ///
+ /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
+ ///
+ /// assert_eq!(iso3.translation_vector, Vector2::new(2.0, 4.0));
+ /// assert_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
+ /// ```
+ #[inline]
+ pub fn slerp(&self, other: &Self, t: N) -> Self
+ where
+ N: RealField,
+ {
+ let tr = self.translation.vector.lerp(&other.translation.vector, t);
+ let rot = self.rotation.slerp(&other.rotation, t);
+ Self::from_parts(tr.into(), rot)
+ }
+}
+
+impl Isometry> {
+ /// Interpolates between two isometries using a linear interpolation for the translation part,
+ /// and a spherical interpolation for the rotation part.
+ ///
+ /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
+ /// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// # use nalgebra::geometry::{Vector2, Translation2, Rotation2, Isometry2};
+ ///
+ /// let t1 = Translation2::new(1.0, 2.0);
+ /// let t2 = Translation2::new(3.0, 6.0);
+ /// let q1 = Rotation2::new(std::f32::consts::FRAC_PI_4);
+ /// let q2 = Rotation2::new(-std::f32::consts::PI);
+ /// let iso1 = Isometry2::from_parts(t1, q1);
+ /// let iso2 = Isometry2::from_parts(t2, q2);
+ ///
+ /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
+ ///
+ /// assert_eq!(iso3.translation_vector, Vector2::new(2.0, 4.0));
+ /// assert_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
+ /// ```
+ #[inline]
+ pub fn slerp(&self, other: &Self, t: N) -> Self
+ where
+ N: RealField,
+ {
+ let tr = self.translation.vector.lerp(&other.translation.vector, t);
+ let rot = self.rotation.slerp(&other.rotation, t);
+ Self::from_parts(tr.into(), rot)
+ }
+}
+
// NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation
// and makes it hard to use it, e.g., for Transform × Isometry implementation.
// This is OK since all constructors of the isometry enforce the Rotation bound already (and
diff --git a/src/geometry/rotation_specialization.rs b/src/geometry/rotation_specialization.rs
index 1ee4a9df..7f140c8b 100644
--- a/src/geometry/rotation_specialization.rs
+++ b/src/geometry/rotation_specialization.rs
@@ -236,6 +236,30 @@ impl Rotation2 {
pub fn scaled_axis(&self) -> VectorN {
Vector1::new(self.angle())
}
+
+ /// Spherical linear interpolation between two rotation matrices.
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// # use nalgebra::geometry::Rotation2;
+ ///
+ /// let rot1 = Rotation2::new(std::f32::consts::FRAC_PI_4);
+ /// let rot2 = Rotation2::new(-std::f32::consts::PI);
+ ///
+ /// let rot = rot1.slerp(&rot2, 1.0 / 3.0);
+ ///
+ /// assert_eq!(rot.angle(), std::f32::consts::FRAC_PI_2);
+ /// ```
+ #[inline]
+ pub fn slerp(&self, other: &Self, t: N) -> Self
+ where
+ N::Element: SimdRealField,
+ {
+ let c1 = UnitComplex::from(*self);
+ let c2 = UnitComplex::from(*other);
+ c1.slerp(&c2, t).into()
+ }
}
impl Distribution> for Standard
@@ -862,6 +886,53 @@ where
Self::identity()
}
}
+
+ /// Spherical linear interpolation between two rotation matrices.
+ ///
+ /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
+ /// is not well-defined). Use `.try_slerp` instead to avoid the panic.
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// # use nalgebra::geometry::Rotation3;
+ ///
+ /// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
+ /// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
+ ///
+ /// let q = q1.slerp(&q2, 1.0 / 3.0);
+ ///
+ /// assert_eq!(q.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
+ /// ```
+ #[inline]
+ pub fn slerp(&self, other: &Self, t: N) -> Self
+ where
+ N: RealField,
+ {
+ let q1 = UnitQuaternion::from(*self);
+ let q2 = UnitQuaternion::from(*other);
+ q1.slerp(&q2, t).into()
+ }
+
+ /// Computes the spherical linear interpolation between two rotation matrices or returns `None`
+ /// if both rotations are approximately 180 degrees apart (in which case the interpolation is
+ /// not well-defined).
+ ///
+ /// # Arguments
+ /// * `self`: the first rotation to interpolate from.
+ /// * `other`: the second rotation to interpolate toward.
+ /// * `t`: the interpolation parameter. Should be between 0 and 1.
+ /// * `epsilon`: the value below which the sinus of the angle separating both rotations
+ /// must be to return `None`.
+ #[inline]
+ pub fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option
+ where
+ N: RealField,
+ {
+ let q1 = Rotation3::from(*self);
+ let q2 = Rotation3::from(*other);
+ q1.try_slerp(&q2, t, epsilon).map(|q| q.into())
+ }
}
impl Distribution> for Standard
diff --git a/src/geometry/unit_complex.rs b/src/geometry/unit_complex.rs
index 723ac6c0..f6a4f619 100755
--- a/src/geometry/unit_complex.rs
+++ b/src/geometry/unit_complex.rs
@@ -376,6 +376,26 @@ where
pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> {
self.inverse() * v
}
+
+ /// Spherical linear interpolation between two rotations represented as unit complex numbers.
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// # use nalgebra::geometry::UnitComplex;
+ ///
+ /// let rot1 = UnitComplex::new(std::f32::consts::FRAC_PI_4);
+ /// let rot2 = UnitComplex::new(-std::f32::consts::PI);
+ ///
+ /// let rot = rot1.slerp(&rot2, 1.0 / 3.0);
+ ///
+ /// assert_eq!(rot.angle(), std::f32::consts::FRAC_PI_2);
+ /// ```
+
+ #[inline]
+ pub fn slerp(&self, other: &Self, t: N) -> Self {
+ Self::new(self.angle() * (N::one() - t) + other.angle() * t)
+ }
}
impl fmt::Display for UnitComplex {
From c410a32c8279d2d3b10c654a893d431025d6135c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Crozet=20S=C3=A9bastien?=
Date: Sun, 25 Oct 2020 14:15:26 +0100
Subject: [PATCH 42/55] Fix doc-tests.
---
src/geometry/isometry.rs | 58 +++++++++++++------------
src/geometry/rotation_specialization.rs | 3 +-
src/geometry/unit_complex.rs | 3 +-
3 files changed, 34 insertions(+), 30 deletions(-)
diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs
index c8eb34fb..edb27b5a 100755
--- a/src/geometry/isometry.rs
+++ b/src/geometry/isometry.rs
@@ -387,10 +387,10 @@ impl Isometry> {
/// # Examples:
///
/// ```
- /// # use nalgebra::geometry::{Vector3, Translation3, UnitQuaternion};
+ /// # use nalgebra::{Vector3, Translation3, Isometry3, UnitQuaternion};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
- /// let t2 = Translation3::new(3.0, 6.0, 9.0);
+ /// let t2 = Translation3::new(4.0, 8.0, 12.0);
/// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
/// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
/// let iso1 = Isometry3::from_parts(t1, q1);
@@ -398,7 +398,7 @@ impl Isometry> {
///
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
///
- /// assert_eq!(iso3.translation_vector, Vector3::new(2.0, 4.0, 6.0));
+ /// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0));
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
/// ```
#[inline]
@@ -420,10 +420,10 @@ impl Isometry> {
/// # Examples:
///
/// ```
- /// # use nalgebra::geometry::{Vector3, Translation3, UnitQuaternion};
+ /// # use nalgebra::{Vector3, Translation3, Isometry3, UnitQuaternion};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
- /// let t2 = Translation3::new(3.0, 6.0, 9.0);
+ /// let t2 = Translation3::new(4.0, 8.0, 12.0);
/// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
/// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
/// let iso1 = Isometry3::from_parts(t1, q1);
@@ -431,7 +431,7 @@ impl