Merge pull request #1362 from dimforge/dev-0.32.4

Release v0.32.4
This commit is contained in:
Sébastien Crozet 2024-02-19 11:00:54 +01:00 committed by GitHub
commit 4a5855a1c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 524 additions and 79 deletions

View File

@ -4,6 +4,37 @@ documented here.
This project adheres to [Semantic Versioning](https://semver.org/).
## [0.32.4] (19 Feb 2023)
- Add the `glam-0.25` feature to enable conversion from/to types from `glam` v0.25.
## [0.32.3] (09 July 2023)
### Modified
- Statically sized matrices are now serialized as tuples to match how serde
serialized plain arrays.
- Dont require `Scalar` for matrix `PartialEq` and `Eq`.
### Added
- Allow trailing punctuation in macros `vector!`, `matrix!`, `point!`, etc.
- Add the methods `Matrix1::as_scalar`, `::as_scalar_mut`, `::to_scalar`, `::into_scalar`.
- Add `Rotation3::euler_angles_ordered`, a generalized euler angles calculation.
- Add the `glam-0.24` feature to enable conversion from/to types from `glam` v0.24.
- Add the `lerp` method to points.
- Implement `Clone` for `MatrixIter`.
### Fixed
- Fixed severe catastrophic cancellation issue in variance calculation.
## [0.32.2] (07 March 2023)
### Added
- Add the `glam-0.23` to enable conversion from/to type from `glam` v0.23.
## [0.32.1] (14 Jan. 2023)
### Modified
- Updated `nalgebra-macros` to use the new `Dyn`, avoiding macro-generated deprecation warnings.
## [0.32.0] (14 Jan. 2023)
### Modified

View File

@ -1,6 +1,6 @@
[package]
name = "nalgebra"
version = "0.32.0"
version = "0.32.4"
authors = [ "Sébastien Crozet <developer@crozet.re>" ]
description = "General-purpose linear algebra library with transformations and statically-sized or dynamically-sized matrices."
@ -47,6 +47,9 @@ convert-glam019 = [ "glam019" ]
convert-glam020 = [ "glam020" ]
convert-glam021 = [ "glam021" ]
convert-glam022 = [ "glam022" ]
convert-glam023 = [ "glam023" ]
convert-glam024 = [ "glam024" ]
convert-glam025 = [ "glam025" ]
# Serialization
## To use serde in a #[no-std] environment, enable the
@ -56,7 +59,7 @@ convert-glam022 = [ "glam022" ]
serde-serialize-no-std = [ "serde", "num-complex/serde" ]
serde-serialize = [ "serde-serialize-no-std", "serde/std" ]
rkyv-serialize-no-std = [ "rkyv/size_32" ]
rkyv-serialize = [ "rkyv-serialize-no-std", "rkyv/std", "rkyv/validation", "bytecheck" ]
rkyv-serialize = [ "rkyv-serialize-no-std", "rkyv/std", "rkyv/validation" ]
# Randomness
## To use rand in a #[no-std] environment, enable the
@ -71,7 +74,7 @@ slow-tests = []
rkyv-safe-deser = [ "rkyv-serialize", "rkyv/validation" ]
[dependencies]
nalgebra-macros = { version = "0.1", path = "nalgebra-macros", optional = true }
nalgebra-macros = { version = "0.2.1", path = "nalgebra-macros", optional = true }
typenum = "1.12"
rand-package = { package = "rand", version = "0.8", optional = true, default-features = false }
num-traits = { version = "0.2", default-features = false }
@ -83,8 +86,7 @@ alga = { version = "0.9", default-features = false, optional = true }
rand_distr = { version = "0.4", default-features = false, optional = true }
matrixmultiply = { version = "0.3", optional = true }
serde = { version = "1.0", default-features = false, features = [ "derive" ], optional = true }
rkyv = { version = "0.7", default-features = false, optional = true }
bytecheck = { version = "~0.6.1", optional = true }
rkyv = { version = "0.7.41", default-features = false, optional = true }
mint = { version = "0.5", optional = true }
quickcheck = { version = "1", optional = true }
pest = { version = "2", optional = true }
@ -101,6 +103,9 @@ glam019 = { package = "glam", version = "0.19", optional = true }
glam020 = { package = "glam", version = "0.20", optional = true }
glam021 = { package = "glam", version = "0.21", optional = true }
glam022 = { package = "glam", version = "0.22", optional = true }
glam023 = { package = "glam", version = "0.23", optional = true }
glam024 = { package = "glam", version = "0.24", optional = true }
glam025 = { package = "glam", version = "0.25", optional = true }
cust_core = { version = "0.1", optional = true }
rayon = { version = "1.6", optional = true }
@ -109,6 +114,7 @@ serde_json = "1.0"
rand_xorshift = "0.3"
rand_isaac = "0.3"
criterion = { version = "0.4", features = ["html_reports"] }
nalgebra = { path = ".", features = ["debug", "compare", "rand", "macros"]}
# For matrix comparison macro
matrixcompare = "0.3.0"

View File

@ -29,22 +29,3 @@
</p>
-----
## Acknowledgements
nalgebra is supported by our **platinum** sponsors:
<p>
<a href="https://embark-studios.com">
<img src="https://www.embark.dev/img/logo_black.png" width="301px">
</a>
</p>
And our gold sponsors:
<p>
<a href="https://fragcolor.com">
<img src="https://dimforge.com/img/fragcolor_logo2_color_black.svg" width="300px">
</a>
<a href="https://resolutiongames.com/">
<img src="https://dimforge.com/img/logo_resolution_games.png" width="300px" />
</a>
</p>

View File

@ -7,24 +7,24 @@ use na::DefaultAllocator;
use crate::traits::{Alloc, Number, Dimension};
use crate::aliases::TVec;
/// Component-wise approximate equality beween two vectors.
/// Component-wise approximate equality between two vectors.
pub fn epsilon_equal<T: Number, const D: usize>(x: &TVec<T, D>, y: &TVec<T, D>, epsilon: T) -> TVec<bool, D>
where DefaultAllocator: Alloc<T, D> {
x.zip_map(y, |x, y| abs_diff_eq!(x, y, epsilon = epsilon))
}
/// Component-wise approximate equality beween two scalars.
/// Component-wise approximate equality between two scalars.
pub fn epsilon_equal2<T: AbsDiffEq<Epsilon = T>>(x: T, y: T, epsilon: T) -> bool {
abs_diff_eq!(x, y, epsilon = epsilon)
}
/// Component-wise approximate non-equality beween two vectors.
/// Component-wise approximate non-equality between two vectors.
pub fn epsilon_not_equal<T: Number, const D: usize>(x: &TVec<T, D>, y: &TVec<T, D>, epsilon: T) -> TVec<bool, D>
where DefaultAllocator: Alloc<T, D> {
x.zip_map(y, |x, y| abs_diff_ne!(x, y, epsilon = epsilon))
}
/// Component-wise approximate non-equality beween two scalars.
/// Component-wise approximate non-equality between two scalars.
pub fn epsilon_not_equal2<T: AbsDiffEq<Epsilon = T>>(x: T, y: T, epsilon: T) -> bool {
abs_diff_ne!(x, y, epsilon = epsilon)
}

View File

@ -80,7 +80,7 @@ pub fn quat_to_mat3<T: RealNumber>(x: &Qua<T>) -> TMat3<T> {
.into_inner()
}
/// Converts a quaternion to a rotation matrix in homogenous coordinates.
/// Converts a quaternion to a rotation matrix in homogeneous coordinates.
pub fn quat_to_mat4<T: RealNumber>(x: &Qua<T>) -> TMat4<T> {
UnitQuaternion::new_unchecked(*x).to_homogeneous()
}

View File

@ -1,6 +1,6 @@
[package]
name = "nalgebra-macros"
version = "0.1.0"
version = "0.2.1"
authors = [ "Andreas Longva", "Sébastien Crozet <developer@crozet.re>" ]
edition = "2018"
description = "Procedural macros for nalgebra"

View File

@ -223,7 +223,7 @@ impl Parse for Vector {
elements: Vec::new(),
})
} else {
let elements = MatrixRowSyntax::parse_separated_nonempty(input)?
let elements = MatrixRowSyntax::parse_terminated(input)?
.into_iter()
.collect();
Ok(Self { elements })

View File

@ -94,6 +94,12 @@ fn dmatrix_small_dims_exhaustive() {
DMatrix::from_row_slice(4, 4, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]));
}
#[test]
fn matrix_trailing_semi() {
matrix![1, 2;];
dmatrix![1, 2;];
}
// Skip rustfmt because it just makes the test bloated without making it more readable
#[rustfmt::skip]
#[test]
@ -151,6 +157,13 @@ fn dvector_small_dims_exhaustive() {
assert_eq_and_type!(dvector![1, 2, 3, 4, 5, 6], DVector::from_column_slice(&[1, 2, 3, 4, 5, 6]));
}
#[test]
fn vector_trailing_comma() {
vector![1, 2,];
point![1, 2,];
dvector![1, 2,];
}
#[test]
fn matrix_trybuild_tests() {
let t = trybuild::TestCases::new();

View File

@ -494,7 +494,7 @@ where
assert_eq!(source_minor_indices.len(), values.len());
let nnz = values.len();
// Count the number of occurences of each minor index
// Count the number of occurrences of each minor index
let mut minor_counts = vec![0; minor_dim];
for minor_idx in source_minor_indices {
minor_counts[*minor_idx] += 1;

View File

@ -98,7 +98,7 @@ where
/// Faster sparse-sparse matrix multiplication, `C <- beta * C + alpha * op(A) * op(B)`.
/// This will not return an error even if the patterns don't match.
/// Should be used for situations where pattern creation immediately preceeds multiplication.
/// Should be used for situations where pattern creation immediately precedes multiplication.
///
/// Panics if the dimensions of the matrices involved are not compatible with the expression.
pub fn spmm_csc_prealloc_unchecked<T>(

View File

@ -89,7 +89,7 @@ where
/// Faster sparse-sparse matrix multiplication, `C <- beta * C + alpha * op(A) * op(B)`.
/// This will not return an error even if the patterns don't match.
/// Should be used for situations where pattern creation immediately preceeds multiplication.
/// Should be used for situations where pattern creation immediately precedes multiplication.
///
/// Panics if the dimensions of the matrices involved are not compatible with the expression.
pub fn spmm_csr_prealloc_unchecked<T>(

View File

@ -81,32 +81,32 @@ pub type MatrixXx5<T> = Matrix<T, Dyn, U5, VecStorage<T, Dyn, U5>>;
#[cfg(any(feature = "std", feature = "alloc"))]
pub type MatrixXx6<T> = Matrix<T, Dyn, U6, VecStorage<T, Dyn, U6>>;
/// A heap-allocated, row-major, matrix with 1 rows and a dynamic number of columns.
/// A heap-allocated, column-major, matrix with 1 rows and a dynamic number of columns.
///
/// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.**
#[cfg(any(feature = "std", feature = "alloc"))]
pub type Matrix1xX<T> = Matrix<T, U1, Dyn, VecStorage<T, U1, Dyn>>;
/// A heap-allocated, row-major, matrix with 2 rows and a dynamic number of columns.
/// A heap-allocated, column-major, matrix with 2 rows and a dynamic number of columns.
///
/// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.**
#[cfg(any(feature = "std", feature = "alloc"))]
pub type Matrix2xX<T> = Matrix<T, U2, Dyn, VecStorage<T, U2, Dyn>>;
/// A heap-allocated, row-major, matrix with 3 rows and a dynamic number of columns.
/// A heap-allocated, column-major, matrix with 3 rows and a dynamic number of columns.
///
/// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.**
#[cfg(any(feature = "std", feature = "alloc"))]
pub type Matrix3xX<T> = Matrix<T, U3, Dyn, VecStorage<T, U3, Dyn>>;
/// A heap-allocated, row-major, matrix with 4 rows and a dynamic number of columns.
/// A heap-allocated, column-major, matrix with 4 rows and a dynamic number of columns.
///
/// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.**
#[cfg(any(feature = "std", feature = "alloc"))]
pub type Matrix4xX<T> = Matrix<T, U4, Dyn, VecStorage<T, U4, Dyn>>;
/// A heap-allocated, row-major, matrix with 5 rows and a dynamic number of columns.
/// A heap-allocated, column-major, matrix with 5 rows and a dynamic number of columns.
///
/// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.**
#[cfg(any(feature = "std", feature = "alloc"))]
pub type Matrix5xX<T> = Matrix<T, U5, Dyn, VecStorage<T, U5, Dyn>>;
/// A heap-allocated, row-major, matrix with 6 rows and a dynamic number of columns.
/// A heap-allocated, column-major, matrix with 6 rows and a dynamic number of columns.
///
/// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.**
#[cfg(any(feature = "std", feature = "alloc"))]

View File

@ -20,9 +20,9 @@ use std::mem::MaybeUninit;
/// Every allocator must be both static and dynamic. Though not all implementations may share the
/// same `Buffer` type.
pub trait Allocator<T, R: Dim, C: Dim = U1>: Any + Sized {
/// The type of buffer this allocator can instanciate.
/// The type of buffer this allocator can instantiate.
type Buffer: StorageMut<T, R, C> + IsContiguous + Clone + Debug;
/// The type of buffer with uninitialized components this allocator can instanciate.
/// The type of buffer with uninitialized components this allocator can instantiate.
type BufferUninit: RawStorageMut<MaybeUninit<T>, R, C> + IsContiguous;
/// Allocates a buffer with the given number of rows and columns without initializing its content.

View File

@ -5,12 +5,15 @@ use std::ops::Mul;
#[cfg(feature = "serde-serialize-no-std")]
use serde::de::{Error, SeqAccess, Visitor};
#[cfg(feature = "serde-serialize-no-std")]
use serde::ser::SerializeSeq;
use serde::ser::SerializeTuple;
#[cfg(feature = "serde-serialize-no-std")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "serde-serialize-no-std")]
use std::marker::PhantomData;
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
use crate::base::allocator::Allocator;
use crate::base::default_allocator::DefaultAllocator;
use crate::base::dimension::{Const, ToTypenum};
@ -189,7 +192,7 @@ where
where
S: Serializer,
{
let mut serializer = serializer.serialize_seq(Some(R * C))?;
let mut serializer = serializer.serialize_tuple(R * C)?;
for e in self.as_slice().iter() {
serializer.serialize_element(e)?;
@ -208,7 +211,7 @@ where
where
D: Deserializer<'a>,
{
deserializer.deserialize_seq(ArrayStorageVisitor::new())
deserializer.deserialize_tuple(R * C, ArrayStorageVisitor::new())
}
}

View File

@ -8,6 +8,8 @@ use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Sub};
use typenum::{self, Diff, Max, Maximum, Min, Minimum, Prod, Quot, Sum, Unsigned};
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
#[cfg(feature = "serde-serialize-no-std")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};

View File

@ -1077,7 +1077,7 @@ where
}
// Move the elements of `data` in such a way that the matrix with
// the rows `[i, i + nremove[` deleted is represented in a contigous
// the rows `[i, i + nremove[` deleted is represented in a contiguous
// way in `data` after this method completes.
// Every deleted element are manually dropped by this method.
unsafe fn compress_rows<T: Scalar>(

View File

@ -15,9 +15,9 @@ use crate::base::storage::{RawStorage, RawStorageMut};
use crate::base::{Matrix, MatrixView, MatrixViewMut, Scalar};
macro_rules! iterator {
(struct $Name:ident for $Storage:ident.$ptr: ident -> $Ptr:ty, $Ref:ty, $SRef: ty) => {
(struct $Name:ident for $Storage:ident.$ptr: ident -> $Ptr:ty, $Ref:ty, $SRef: ty, $($derives:ident),* $(,)?) => {
/// An iterator through a dense matrix with arbitrary strides matrix.
#[derive(Debug)]
#[derive($($derives),*)]
pub struct $Name<'a, T, R: Dim, C: Dim, S: 'a + $Storage<T, R, C>> {
ptr: $Ptr,
inner_ptr: $Ptr,
@ -39,7 +39,7 @@ macro_rules! iterator {
let ptr = storage.$ptr();
// If we have a size of 0, 'ptr' must be
// dangling. Howver, 'inner_offset' might
// dangling. However, 'inner_offset' might
// not be zero if only one dimension is zero, so
// we don't want to call 'offset'.
// This pointer will never actually get used
@ -177,8 +177,8 @@ macro_rules! iterator {
};
}
iterator!(struct MatrixIter for RawStorage.ptr -> *const T, &'a T, &'a S);
iterator!(struct MatrixIterMut for RawStorageMut.ptr_mut -> *mut T, &'a mut T, &'a mut S);
iterator!(struct MatrixIter for RawStorage.ptr -> *const T, &'a T, &'a S, Clone, Debug);
iterator!(struct MatrixIterMut for RawStorageMut.ptr_mut -> *mut T, &'a mut T, &'a mut S, Debug);
/*
*

View File

@ -13,6 +13,8 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "rkyv-serialize-no-std")]
use super::rkyv_wrappers::CustomPhantom;
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
#[cfg(feature = "rkyv-serialize-no-std")]
use rkyv::{with::With, Archive, Archived};
@ -1859,14 +1861,14 @@ where
impl<T, R: Dim, C: Dim, S> Eq for Matrix<T, R, C, S>
where
T: Scalar + Eq,
T: Eq,
S: RawStorage<T, R, C>,
{
}
impl<T, R, R2, C, C2, S, S2> PartialEq<Matrix<T, R2, C2, S2>> for Matrix<T, R, C, S>
where
T: Scalar + PartialEq,
T: PartialEq,
C: Dim,
C2: Dim,
R: Dim,
@ -2244,3 +2246,102 @@ where
Unit::new_unchecked(crate::convert_ref(self.as_ref()))
}
}
impl<T, S> Matrix<T, U1, U1, S>
where
S: RawStorage<T, U1, U1>,
{
/// Returns a reference to the single element in this matrix.
///
/// As opposed to indexing, using this provides type-safety
/// when flattening dimensions.
///
/// # Example
/// ```
/// # use nalgebra::Vector3;
/// let v = Vector3::new(0., 0., 1.);
/// let inner_product: f32 = *(v.transpose() * v).as_scalar();
/// ```
///
///```compile_fail
/// # use nalgebra::Vector3;
/// let v = Vector3::new(0., 0., 1.);
/// let inner_product = (v * v.transpose()).item(); // Typo, does not compile.
///```
pub fn as_scalar(&self) -> &T {
&self[(0, 0)]
}
/// Get a mutable reference to the single element in this matrix
///
/// As opposed to indexing, using this provides type-safety
/// when flattening dimensions.
///
/// # Example
/// ```
/// # use nalgebra::Vector3;
/// let v = Vector3::new(0., 0., 1.);
/// let mut inner_product = (v.transpose() * v);
/// *inner_product.as_scalar_mut() = 3.;
/// ```
///
///```compile_fail
/// # use nalgebra::Vector3;
/// let v = Vector3::new(0., 0., 1.);
/// let mut inner_product = (v * v.transpose());
/// *inner_product.as_scalar_mut() = 3.;
///```
pub fn as_scalar_mut(&mut self) -> &mut T
where
S: RawStorageMut<T, U1>,
{
&mut self[(0, 0)]
}
/// Convert this 1x1 matrix by reference into a scalar.
///
/// As opposed to indexing, using this provides type-safety
/// when flattening dimensions.
///
/// # Example
/// ```
/// # use nalgebra::Vector3;
/// let v = Vector3::new(0., 0., 1.);
/// let mut inner_product: f32 = (v.transpose() * v).to_scalar();
/// ```
///
///```compile_fail
/// # use nalgebra::Vector3;
/// let v = Vector3::new(0., 0., 1.);
/// let mut inner_product: f32 = (v * v.transpose()).to_scalar();
///```
pub fn to_scalar(&self) -> T
where
T: Clone,
{
self.as_scalar().clone()
}
}
impl<T> super::alias::Matrix1<T> {
/// Convert this 1x1 matrix into a scalar.
///
/// As opposed to indexing, using this provides type-safety
/// when flattening dimensions.
///
/// # Example
/// ```
/// # use nalgebra::{Vector3, Matrix2, U1};
/// let v = Vector3::new(0., 0., 1.);
/// let inner_product: f32 = (v.transpose() * v).into_scalar();
/// assert_eq!(inner_product, 1.);
/// ```
///
///```compile_fail
/// # use nalgebra::Vector3;
/// let v = Vector3::new(0., 0., 1.);
/// let mut inner_product: f32 = (v * v.transpose()).into_scalar();
///```
pub fn into_scalar(self) -> T {
let [[scalar]] = self.data.0;
scalar
}
}

View File

@ -11,7 +11,7 @@ use crate::{
use rayon::iter::plumbing::Producer;
use rayon::{iter::plumbing::bridge, prelude::*};
/// A rayon parallel iterator over the colums of a matrix. It is created
/// A rayon parallel iterator over the columns of a matrix. It is created
/// using the [`par_column_iter`] method of [`Matrix`].
///
/// *Only available if compiled with the feature `rayon`.*
@ -89,7 +89,7 @@ pub struct ParColumnIterMut<
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))]
/// *only availabe if compiled with the feature `rayon`*
/// *only available if compiled with the feature `rayon`*
impl<'a, T, R, Cols, S> ParColumnIterMut<'a, T, R, Cols, S>
where
R: Dim,
@ -161,7 +161,7 @@ where
S: Sync,
{
/// Iterate through the columns of the matrix in parallel using rayon.
/// This iterates over *immutable* references ot the columns of the matrix,
/// This iterates over *immutable* references to the columns of the matrix,
/// if *mutable* access to the columns is required, use [`par_column_iter_mut`]
/// instead.
///

View File

@ -335,12 +335,12 @@ impl<T: Scalar, R: Dim, C: Dim, S: RawStorage<T, R, C>> Matrix<T, R, C, S> {
if self.is_empty() {
T::zero()
} else {
let val = self.iter().cloned().fold((T::zero(), T::zero()), |a, b| {
(a.0 + b.clone() * b.clone(), a.1 + b)
});
let denom = T::one() / crate::convert::<_, T>(self.len() as f64);
let vd = val.1 * denom.clone();
val.0 * denom - vd.clone() * vd
let n_elements: T = crate::convert(self.len() as f64);
let mean = self.mean();
self.iter().cloned().fold(T::zero(), |acc, x| {
acc + (x.clone() - mean.clone()) * (x.clone() - mean.clone())
}) / n_elements
}
}

View File

@ -34,7 +34,7 @@ pub unsafe trait InitStatus<T>: Copy {
/// A type implementing `InitStatus` indicating that the value is completely initialized.
pub struct Init;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// A type implementing `InitStatus` indicating that the value is completely unitialized.
/// A type implementing `InitStatus` indicating that the value is completely uninitialized.
pub struct Uninit;
unsafe impl<T> InitStatus<T> for Init {

View File

@ -9,6 +9,9 @@ use crate::base::DefaultAllocator;
use crate::storage::RawStorage;
use crate::{Dim, Matrix, OMatrix, RealField, Scalar, SimdComplexField, SimdRealField};
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
/// A wrapper that ensures the underlying algebraic entity has a unit norm.
///
/// **It is likely that the only piece of documentation that you need in this page are:**

View File

@ -148,7 +148,7 @@ impl<T, R: Dim, C: Dim> VecStorage<T, R, C> {
};
// Avoid double-free by forgetting `self` because its data buffer has
// been transfered to `new_data`.
// been transferred to `new_data`.
std::mem::forget(self);
new_data
}

View File

@ -1,6 +1,9 @@
// The macros break if the references are taken out, for some reason.
#![allow(clippy::op_ref)]
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
use crate::{
Isometry3, Matrix4, Normed, OVector, Point3, Quaternion, Scalar, SimdRealField, Translation3,
Unit, UnitQuaternion, Vector3, Zero, U8,

3
src/geometry/isometry.rs Executable file → Normal file
View File

@ -14,6 +14,9 @@ use crate::base::storage::Owned;
use crate::base::{Const, DefaultAllocator, OMatrix, SVector, Scalar, Unit};
use crate::geometry::{AbstractRotation, Point, Translation};
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
/// A direct isometry, i.e., a rotation followed by a translation (aka. a rigid-body motion).
///
/// This is also known as an element of a Special Euclidean (SE) group.

View File

@ -42,7 +42,7 @@ impl<T: SimdRealField> Isometry3<T> {
/// 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
/// Returns `None` if the angle between both rotations is 180 degrees (in which case the interpolation
/// is not well-defined).
///
/// # Examples:
@ -118,7 +118,7 @@ impl<T: SimdRealField> IsometryMatrix3<T> {
/// 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
/// Returns `None` if the angle between both rotations is 180 degrees (in which case the interpolation
/// is not well-defined).
///
/// # Examples:

View File

@ -17,6 +17,9 @@ use crate::base::{Matrix4, Vector, Vector3};
use crate::geometry::{Point3, Projective3};
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
/// A 3D orthographic projection stored as a homogeneous 4x4 matrix.
#[repr(C)]
#[cfg_attr(

View File

@ -18,6 +18,9 @@ use crate::base::{Matrix4, Vector, Vector3};
use crate::geometry::{Point3, Projective3};
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
/// A 3D perspective projection stored as a homogeneous 4x4 matrix.
#[repr(C)]
#[cfg_attr(

View File

@ -1,9 +1,11 @@
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
use num::One;
use num::{One, Zero};
use std::cmp::Ordering;
use std::fmt;
use std::hash;
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
#[cfg(feature = "serde-serialize-no-std")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -13,6 +15,7 @@ use crate::base::allocator::Allocator;
use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1};
use crate::base::iter::{MatrixIter, MatrixIterMut};
use crate::base::{Const, DefaultAllocator, OVector, Scalar};
use simba::scalar::{ClosedAdd, ClosedMul, ClosedSub};
use std::mem::MaybeUninit;
/// A point in an euclidean space.
@ -221,6 +224,31 @@ where
unsafe { res.assume_init() }
}
/// Linear interpolation between two points.
///
/// Returns `self * (1.0 - t) + rhs.coords * t`, i.e., the linear blend of the points
/// `self` and `rhs` using the scalar value `t`.
///
/// The value for a is not restricted to the range `[0, 1]`.
///
/// # Examples:
///
/// ```
/// # use nalgebra::Point3;
/// let a = Point3::new(1.0, 2.0, 3.0);
/// let b = Point3::new(10.0, 20.0, 30.0);
/// assert_eq!(a.lerp(&b, 0.1), Point3::new(1.9, 3.8, 5.7));
/// ```
#[must_use]
pub fn lerp(&self, rhs: &OPoint<T, D>, t: T) -> OPoint<T, D>
where
T: Scalar + Zero + One + ClosedAdd + ClosedSub + ClosedMul,
{
OPoint {
coords: self.coords.lerp(&rhs.coords, t),
}
}
/// Creates a new point with the given coordinates.
#[deprecated(note = "Use Point::from(vector) instead.")]
#[inline]

5
src/geometry/quaternion.rs Executable file → Normal file
View File

@ -19,6 +19,9 @@ use crate::base::{
use crate::geometry::{Point3, Rotation};
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
/// A quaternion. See the type alias `UnitQuaternion = Unit<Quaternion>` for a quaternion
/// that may be used as a rotation.
#[repr(C)]
@ -1577,7 +1580,7 @@ where
#[inline]
#[must_use]
pub fn inverse_transform_point(&self, pt: &Point3<T>) -> Point3<T> {
// TODO: would it be useful performancewise not to call inverse explicitly (i-e. implement
// TODO: would it be useful performance-wise not to call inverse explicitly (i-e. implement
// the inverse transformation explicitly here) ?
self.inverse() * pt
}

3
src/geometry/rotation.rs Executable file → Normal file
View File

@ -17,6 +17,9 @@ use crate::base::dimension::{DimNameAdd, DimNameSum, U1};
use crate::base::{Const, DefaultAllocator, OMatrix, SMatrix, SVector, Scalar, Unit};
use crate::geometry::Point;
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
/// A rotation matrix.
///
/// This is also known as an element of a Special Orthogonal (SO) group.

View File

@ -478,9 +478,10 @@ where
SB: Storage<T, U3>,
SC: Storage<T, U3>,
{
// GramSchmidt process
let zaxis = dir.normalize();
let xaxis = up.cross(&zaxis).normalize();
let yaxis = zaxis.cross(&xaxis).normalize();
let yaxis = zaxis.cross(&xaxis);
Self::from_matrix_unchecked(SMatrix::<T, 3, 3>::new(
xaxis.x.clone(),
@ -979,6 +980,176 @@ impl<T: SimdRealField> Rotation3<T> {
)
}
}
/// Represent this rotation as Euler angles.
///
/// Returns the angles produced in the order provided by seq parameter, along with the
/// observability flag. The Euler axes passed to seq must form an orthonormal basis. If the
/// rotation is gimbal locked, then the observability flag is false.
///
/// # Panics
///
/// Panics if the Euler axes in `seq` are not orthonormal.
///
/// # Example 1:
/// ```
/// use std::f64::consts::PI;
/// use approx::assert_relative_eq;
/// use nalgebra::{Matrix3, Rotation3, Unit, Vector3};
///
/// // 3-1-2
/// let n = [
/// Unit::new_unchecked(Vector3::new(0.0, 0.0, 1.0)),
/// Unit::new_unchecked(Vector3::new(1.0, 0.0, 0.0)),
/// Unit::new_unchecked(Vector3::new(0.0, 1.0, 0.0)),
/// ];
///
/// let r1 = Rotation3::from_axis_angle(&n[2], 20.0 * PI / 180.0);
/// let r2 = Rotation3::from_axis_angle(&n[1], 30.0 * PI / 180.0);
/// let r3 = Rotation3::from_axis_angle(&n[0], 45.0 * PI / 180.0);
///
/// let d = r3 * r2 * r1;
///
/// let (angles, observable) = d.euler_angles_ordered(n, false);
/// assert!(observable);
/// assert_relative_eq!(angles[0] * 180.0 / PI, 45.0, epsilon = 1e-12);
/// assert_relative_eq!(angles[1] * 180.0 / PI, 30.0, epsilon = 1e-12);
/// assert_relative_eq!(angles[2] * 180.0 / PI, 20.0, epsilon = 1e-12);
/// ```
///
/// # Example 2:
/// ```
/// use std::f64::consts::PI;
/// use approx::assert_relative_eq;
/// use nalgebra::{Matrix3, Rotation3, Unit, Vector3};
///
/// let sqrt_2 = 2.0_f64.sqrt();
/// let n = [
/// Unit::new_unchecked(Vector3::new(1.0 / sqrt_2, 1.0 / sqrt_2, 0.0)),
/// Unit::new_unchecked(Vector3::new(1.0 / sqrt_2, -1.0 / sqrt_2, 0.0)),
/// Unit::new_unchecked(Vector3::new(0.0, 0.0, 1.0)),
/// ];
///
/// let r1 = Rotation3::from_axis_angle(&n[2], 20.0 * PI / 180.0);
/// let r2 = Rotation3::from_axis_angle(&n[1], 30.0 * PI / 180.0);
/// let r3 = Rotation3::from_axis_angle(&n[0], 45.0 * PI / 180.0);
///
/// let d = r3 * r2 * r1;
///
/// let (angles, observable) = d.euler_angles_ordered(n, false);
/// assert!(observable);
/// assert_relative_eq!(angles[0] * 180.0 / PI, 45.0, epsilon = 1e-12);
/// assert_relative_eq!(angles[1] * 180.0 / PI, 30.0, epsilon = 1e-12);
/// assert_relative_eq!(angles[2] * 180.0 / PI, 20.0, epsilon = 1e-12);
/// ```
///
/// Algorithm based on:
/// Malcolm D. Shuster, F. Landis Markley, “General formula for extraction the Euler
/// angles”, Journal of guidance, control, and dynamics, vol. 29.1, pp. 215-221. 2006,
/// and modified to be able to produce extrinsic rotations.
#[must_use]
pub fn euler_angles_ordered(
&self,
mut seq: [Unit<Vector3<T>>; 3],
extrinsic: bool,
) -> ([T; 3], bool)
where
T: RealField + Copy,
{
let mut angles = [T::zero(); 3];
let eps = T::from_subset(&1e-7);
let _2 = T::from_subset(&2.0);
if extrinsic {
seq.reverse();
}
let [n1, n2, n3] = &seq;
assert_relative_eq!(n1.dot(n2), T::zero(), epsilon = eps);
assert_relative_eq!(n3.dot(n1), T::zero(), epsilon = eps);
let n1_c_n2 = n1.cross(n2);
let s1 = n1_c_n2.dot(n3);
let c1 = n1.dot(n3);
let lambda = s1.atan2(c1);
let mut c = Matrix3::zeros();
c.column_mut(0).copy_from(n2);
c.column_mut(1).copy_from(&n1_c_n2);
c.column_mut(2).copy_from(n1);
c.transpose_mut();
let r1l = Matrix3::new(
T::one(),
T::zero(),
T::zero(),
T::zero(),
c1,
s1,
T::zero(),
-s1,
c1,
);
let o_t = &c * self.matrix() * (c.transpose() * r1l);
angles[1] = o_t.m33.acos();
let safe1 = angles[1].abs() >= eps;
let safe2 = (angles[1] - T::pi()).abs() >= eps;
let observable = safe1 && safe2;
angles[1] += lambda;
if observable {
angles[0] = o_t.m13.atan2(-o_t.m23);
angles[2] = o_t.m31.atan2(o_t.m32);
} else {
// gimbal lock detected
if extrinsic {
// angle1 is initialized to zero
if !safe1 {
angles[2] = (o_t.m12 - o_t.m21).atan2(o_t.m11 + o_t.m22);
} else {
angles[2] = -(o_t.m12 + o_t.m21).atan2(o_t.m11 - o_t.m22);
};
} else {
// angle3 is initialized to zero
if !safe1 {
angles[0] = (o_t.m12 - o_t.m21).atan2(o_t.m11 + o_t.m22);
} else {
angles[0] = (o_t.m12 + o_t.m21).atan2(o_t.m11 - o_t.m22);
};
};
};
let adjust = if seq[0] == seq[2] {
// lambda = 0, so ensure angle2 -> [0, pi]
angles[1] < T::zero() || angles[1] > T::pi()
} else {
// lamda = + or - pi/2, so ensure angle2 -> [-pi/2, pi/2]
angles[1] < -T::frac_pi_2() || angles[1] > T::frac_pi_2()
};
// dont adjust gimbal locked rotation
if adjust && observable {
angles[0] += T::pi();
angles[1] = _2 * lambda - angles[1];
angles[2] -= T::pi();
}
// ensure all angles are within [-pi, pi]
for angle in angles.as_mut_slice().iter_mut() {
if *angle < -T::pi() {
*angle += T::two_pi();
} else if *angle > T::pi() {
*angle -= T::two_pi();
}
}
if extrinsic {
angles.reverse();
}
(angles, observable)
}
}
#[cfg(feature = "rand-no-std")]

3
src/geometry/scale.rs Executable file → Normal file
View File

@ -15,6 +15,9 @@ use crate::ClosedMul;
use crate::geometry::Point;
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
/// A scale which supports non-uniform scaling.
#[repr(C)]
#[cfg_attr(

3
src/geometry/similarity.rs Executable file → Normal file
View File

@ -15,6 +15,9 @@ use crate::base::storage::Owned;
use crate::base::{Const, DefaultAllocator, OMatrix, SVector, Scalar};
use crate::geometry::{AbstractRotation, Isometry, Point, Translation};
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
/// A similarity, i.e., an uniform scaling, followed by a rotation, followed by a translation.
#[repr(C)]
#[derive(Debug, Copy, Clone)]

View File

@ -122,7 +122,7 @@ macro_rules! category_mul_impl(
)*}
);
// We require stability uppon multiplication.
// We require stability upon multiplication.
impl<T: TCategory> TCategoryMul<T> for T {
type Representative = T;
}

3
src/geometry/translation.rs Executable file → Normal file
View File

@ -15,6 +15,9 @@ use crate::base::{Const, DefaultAllocator, OMatrix, SVector, Scalar};
use crate::geometry::Point;
#[cfg(feature = "rkyv-serialize")]
use rkyv::bytecheck;
/// A translation.
#[repr(C)]
#[cfg_attr(

View File

@ -347,7 +347,7 @@ where
#[inline]
#[must_use]
pub fn inverse_transform_point(&self, pt: &Point2<T>) -> Point2<T> {
// TODO: would it be useful performancewise not to call inverse explicitly (i-e. implement
// TODO: would it be useful performance-wise not to call inverse explicitly (i-e. implement
// the inverse transformation explicitly here) ?
self.inverse() * pt
}

View File

@ -47,11 +47,11 @@ impl<T: RealField, D1: Dim, S1: Storage<T, D1>> Vector<T, D1, S1> {
let u_f = cmp::min(i, vec - 1);
if u_i == u_f {
conv[i] += self[u_i].clone() * kernel[(i - u_i)].clone();
conv[i] += self[u_i].clone() * kernel[i - u_i].clone();
} else {
for u in u_i..(u_f + 1) {
if i - u < ker {
conv[i] += self[u].clone() * kernel[(i - u)].clone();
conv[i] += self[u].clone() * kernel[i - u].clone();
}
}
}

View File

@ -724,7 +724,7 @@ where
/// Sort the estimated components of the SVD by its singular values in descending order.
/// Such an ordering is often implicitly required when the decompositions are used for estimation or fitting purposes.
/// Using this function is only required if `new_unordered` or `try_new_unorderd` were used and the specific sorting is required afterward.
/// Using this function is only required if `new_unordered` or `try_new_unordered` were used and the specific sorting is required afterward.
pub fn sort_by_singular_values(&mut self) {
const VALUE_PROCESSED: usize = usize::MAX;

View File

@ -498,7 +498,7 @@ where
}
}
// Remove dupliate entries on a sorted CsMatrix.
// Remove duplicate entries on a sorted CsMatrix.
pub(crate) fn dedup(&mut self)
where
T: Zero + ClosedAdd,

View File

@ -16,3 +16,9 @@ mod v020;
mod v021;
#[cfg(feature = "glam022")]
mod v022;
#[cfg(feature = "glam023")]
mod v023;
#[cfg(feature = "glam024")]
mod v024;
#[cfg(feature = "glam025")]
mod v025;

18
src/third_party/glam/v023/mod.rs vendored Normal file
View File

@ -0,0 +1,18 @@
#[path = "../common/glam_isometry.rs"]
mod glam_isometry;
#[path = "../common/glam_matrix.rs"]
mod glam_matrix;
#[path = "../common/glam_point.rs"]
mod glam_point;
#[path = "../common/glam_quaternion.rs"]
mod glam_quaternion;
#[path = "../common/glam_rotation.rs"]
mod glam_rotation;
#[path = "../common/glam_similarity.rs"]
mod glam_similarity;
#[path = "../common/glam_translation.rs"]
mod glam_translation;
#[path = "../common/glam_unit_complex.rs"]
mod glam_unit_complex;
pub(self) use glam023 as glam;

18
src/third_party/glam/v024/mod.rs vendored Normal file
View File

@ -0,0 +1,18 @@
#[path = "../common/glam_isometry.rs"]
mod glam_isometry;
#[path = "../common/glam_matrix.rs"]
mod glam_matrix;
#[path = "../common/glam_point.rs"]
mod glam_point;
#[path = "../common/glam_quaternion.rs"]
mod glam_quaternion;
#[path = "../common/glam_rotation.rs"]
mod glam_rotation;
#[path = "../common/glam_similarity.rs"]
mod glam_similarity;
#[path = "../common/glam_translation.rs"]
mod glam_translation;
#[path = "../common/glam_unit_complex.rs"]
mod glam_unit_complex;
pub(self) use glam024 as glam;

18
src/third_party/glam/v025/mod.rs vendored Normal file
View File

@ -0,0 +1,18 @@
#[path = "../common/glam_isometry.rs"]
mod glam_isometry;
#[path = "../common/glam_matrix.rs"]
mod glam_matrix;
#[path = "../common/glam_point.rs"]
mod glam_point;
#[path = "../common/glam_quaternion.rs"]
mod glam_quaternion;
#[path = "../common/glam_rotation.rs"]
mod glam_rotation;
#[path = "../common/glam_similarity.rs"]
mod glam_similarity;
#[path = "../common/glam_translation.rs"]
mod glam_translation;
#[path = "../common/glam_unit_complex.rs"]
mod glam_unit_complex;
pub(self) use glam025 as glam;

View File

@ -11,6 +11,7 @@ mod reshape;
#[cfg(feature = "rkyv-serialize-no-std")]
mod rkyv;
mod serde;
mod variance;
#[cfg(feature = "compare")]
mod matrixcompare;

18
tests/core/variance.rs Normal file
View File

@ -0,0 +1,18 @@
use nalgebra::DVector;
#[test]
fn test_variance_catastrophic_cancellation() {
let long_repeating_vector = DVector::repeat(10_000, 100000000.0);
assert_eq!(long_repeating_vector.variance(), 0.0);
let short_vec = DVector::from_vec(vec![1., 2., 3.]);
assert_eq!(short_vec.variance(), 2.0 / 3.0);
let short_vec =
DVector::<f64>::from_vec(vec![1.0e8 + 4.0, 1.0e8 + 7.0, 1.0e8 + 13.0, 1.0e8 + 16.0]);
assert_eq!(short_vec.variance(), 22.5);
let short_vec =
DVector::<f64>::from_vec(vec![1.0e9 + 4.0, 1.0e9 + 7.0, 1.0e9 + 13.0, 1.0e9 + 16.0]);
assert_eq!(short_vec.variance(), 22.5);
}

View File

@ -123,7 +123,7 @@ fn symmetric_eigen_singular_24x24() {
//
// /*
// * NOTE: for the following tests, we use only upper-triangular matrices.
// * Thes ensures the schur decomposition will work, and allows use to test the eigenvector
// * This ensures the schur decomposition will work, and allows use to test the eigenvector
// * computation.
// */
// fn eigen(n: usize) -> bool {
@ -134,11 +134,11 @@ fn symmetric_eigen_singular_24x24() {
// verify_eigenvectors(m, eig)
// }
//
// fn eigen_with_adjascent_duplicate_diagonals(n: usize) -> bool {
// fn eigen_with_adjacent_duplicate_diagonals(n: usize) -> bool {
// let n = cmp::max(1, cmp::min(n, 10));
// let mut m = DMatrix::<f64>::new_random(n, n).upper_triangle();
//
// // Suplicate some adjascent diagonal elements.
// // Suplicate some adjacent diagonal elements.
// for i in 0 .. n / 2 {
// m[(i * 2 + 1, i * 2 + 1)] = m[(i * 2, i * 2)];
// }
@ -147,7 +147,7 @@ fn symmetric_eigen_singular_24x24() {
// verify_eigenvectors(m, eig)
// }
//
// fn eigen_with_nonadjascent_duplicate_diagonals(n: usize) -> bool {
// fn eigen_with_nonadjacent_duplicate_diagonals(n: usize) -> bool {
// let n = cmp::max(3, cmp::min(n, 10));
// let mut m = DMatrix::<f64>::new_random(n, n).upper_triangle();
//