commit
944afe24e8
62
CHANGELOG.md
62
CHANGELOG.md
@ -4,6 +4,30 @@ documented here.
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [0.28.0]
|
||||
### Added
|
||||
- Implement `Hash` for `Transform`.
|
||||
- Implement `Borrow` and `BorrowMut` for contiguous slices.
|
||||
|
||||
### Modified
|
||||
- The `OPoint<T, D>` type has been added. It takes the dimension number as a type-level integer (e.g. `Const<3>`) instead
|
||||
of a const-generic. The type `Point<T, const D: usize>` is now an alias for `OPoint`. This changes doesn't affect any
|
||||
of the existing code using `Point`. However, it will allow the use `OPoint` in a generic context where the dimension
|
||||
cannot be easily expressed as a const-generic (because of the current limitation of const-generics in Rust).
|
||||
- Several clippy warnings were fixed. This results in some method signature changes (e.g. taking `self` instead of `&self`)
|
||||
but this should not have any practical infulances on existing codebase.
|
||||
- The `Point::new` constructors are no longer const-fn. This is due to some limitations in const-fn
|
||||
not allowing custom trait-bounds. Use the `point!` macro instead to build points in const environments.
|
||||
- `Dynamic::new` and `Unit::new_unchecked` are now const-fn.
|
||||
- Methods returning `Result<(), ()>` now return `bool` instead.
|
||||
|
||||
### Fixed
|
||||
- Fixed a potential unsoundess issue when converting a mutable slice to a `&mut[T]`.
|
||||
|
||||
## [0.27.1]
|
||||
### Fixed
|
||||
- Fixed a bug in the conversion from `glam::Vec2` or `glam::DVec2` to `Isometry2`.
|
||||
|
||||
## [0.27.0]
|
||||
This removes the `convert-glam` and `convert-glam-unchecked` optional features.
|
||||
Instead, this adds the `convert-glam013`, `convert-glam014`, and `convert-glam015` optional features for
|
||||
@ -34,7 +58,7 @@ conversions targeting the versions 0.13, 0.14, and 0.15 of `glam`.
|
||||
Fix a regression introduced in 0.26.0 preventing `DVector` from being serialized with `serde`.
|
||||
|
||||
## [0.26.0]
|
||||
This releases integrates `min-const-generics` to nalgebra. See
|
||||
This release integrates `min-const-generics` to nalgebra. See
|
||||
[our blog post](https://www.dimforge.com/blog/2021/04/12/integrating-const-generics-to-nalgebra)
|
||||
for details about this release.
|
||||
|
||||
@ -74,7 +98,7 @@ for details about this release.
|
||||
|
||||
## [0.25.3]
|
||||
### Added
|
||||
- The `Vector::simd_cap_magnitude` method to cap the magnitude of the a vector with
|
||||
- The `Vector::simd_cap_magnitude` method to cap the magnitude of the vector with
|
||||
SIMD components.
|
||||
|
||||
## [0.25.2]
|
||||
@ -105,7 +129,7 @@ This updates all the dependencies of nalgebra to their latest version, including
|
||||
|
||||
### New crate: nalgebra-sparse
|
||||
Alongside this release of `nalgebra`, we are releasing `nalgebra-sparse`: a crate dedicated to sparse matrix
|
||||
computation with `nalgebra`. The `sparse` module of `nalgebra`itself still exists for backward compatibility
|
||||
computation with `nalgebra`. The `sparse` module of `nalgebra`itself still exists for backward compatibility,
|
||||
but it will be deprecated soon in favor of the `nalgebra-sparse` crate.
|
||||
|
||||
### Added
|
||||
@ -121,12 +145,12 @@ but it will be deprecated soon in favor of the `nalgebra-sparse` crate.
|
||||
## [0.24.0]
|
||||
|
||||
### Added
|
||||
* The `DualQuaternion` type. It is still work-in-progress but the basics are here:
|
||||
* The `DualQuaternion` type. It is still work-in-progress, but the basics are here:
|
||||
creation from its real and dual part, multiplication of two dual quaternions,
|
||||
and normalization.
|
||||
|
||||
### Removed
|
||||
* There is no blanket `impl<T> PartialEq for Unit<T>` any more. Instead, it is
|
||||
* There is no blanket `impl<T> PartialEq for Unit<T>` anymore. Instead, it is
|
||||
implemented specifically for `UnitComplex`, `UnitQuaternion` and `Unit<Vector>`.
|
||||
|
||||
## [0.23.2]
|
||||
@ -153,7 +177,7 @@ In this release we improved the documentation of the matrix and vector types by:
|
||||
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<Vector3<f32>>` with its i-th component set to 1.0 and the
|
||||
* The `Vector::ith_axis(i)` which build a unit vector, e.g., `Unit<Vector3<f32>>` 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.
|
||||
@ -162,7 +186,7 @@ In this release we improved the documentation of the matrix and vector types by:
|
||||
|
||||
## [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
|
||||
use of `libm` is now opt-in when building targeting `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.
|
||||
|
||||
@ -170,7 +194,7 @@ of nalgebra for your code to compile again.
|
||||
* 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.
|
||||
positive-definite. It can be used 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()`.
|
||||
@ -341,7 +365,7 @@ library (i.e. it supports `#![no_std]`). See the corresponding [documentation](h
|
||||
* Add methods `.rotation_between_axis(...)` and `.scaled_rotation_between_axis(...)` to `UnitComplex`
|
||||
to compute the rotation matrix between two 2D **unit** vectors.
|
||||
* Add methods `.axis_angle()` to `UnitComplex` and `UnitQuaternion` in order to retrieve both the
|
||||
unit rotation axis and the rotation angle simultaneously.
|
||||
unit rotation axis, and the rotation angle simultaneously.
|
||||
* Add functions to construct a random matrix with a user-defined distribution: `::from_distribution(...)`.
|
||||
|
||||
## [0.14.0]
|
||||
@ -362,7 +386,7 @@ library (i.e. it supports `#![no_std]`). See the corresponding [documentation](h
|
||||
the matrix `M` such that for all vector `v` we have
|
||||
`M * v == self.cross(&v)`.
|
||||
* `.iamin()` that returns the index of the vector entry with
|
||||
smallest absolute value.
|
||||
the smallest absolute value.
|
||||
* The `mint` feature that can be enabled in order to allow conversions from
|
||||
and to types of the [mint](https://crates.io/crates/mint) crate.
|
||||
* Aliases for matrix and vector slices. Their are named by adding `Slice`
|
||||
@ -400,7 +424,7 @@ This adds support for serialization using the
|
||||
* The alias `MatrixNM` is now deprecated. Use `MatrixMN` instead (we
|
||||
reordered M and N to be in alphabetical order).
|
||||
* In-place componentwise multiplication and division
|
||||
`.component_mul_mut(...)` and `.component_div_mut(...)` have bee deprecated
|
||||
`.component_mul_mut(...)` and `.component_div_mut(...)` have been deprecated
|
||||
for a future renaming. Use `.component_mul_assign(...)` and
|
||||
`.component_div_assign(...)` instead.
|
||||
|
||||
@ -502,7 +526,7 @@ This version is a major rewrite of the library. Major changes are:
|
||||
All other mathematical traits, except `Axpy` have been removed from
|
||||
**nalgebra**.
|
||||
* Methods are now preferred to free functions because they do not require any
|
||||
trait to be used any more.
|
||||
trait to be used anymore.
|
||||
* Most algebraic entities can be parametrized by type-level integers
|
||||
to specify their dimensions. Using `Dynamic` instead of a type-level
|
||||
integer indicates that the dimension known at run-time only.
|
||||
@ -578,7 +602,7 @@ only:
|
||||
* The free functions `::prepend_rotation`, `::append_rotation`,
|
||||
`::append_rotation_wrt_center`, `::append_rotation_wrt_point`,
|
||||
`::append_transformation`, and `::append_translation ` have been removed.
|
||||
Instead create the rotation or translation object explicitly and use
|
||||
Instead, create the rotation or translation object explicitly and use
|
||||
multiplication to compose it with anything else.
|
||||
|
||||
* The free function `::outer` has been removed. Use column-vector ×
|
||||
@ -604,7 +628,7 @@ Binary operations are now allowed between references as well. For example
|
||||
|
||||
### Modified
|
||||
Removed unused parameters to methods from the `ApproxEq` trait. Those were
|
||||
required before rust 1.0 to help type inference. The are not needed any more
|
||||
required before rust 1.0 to help type inference. They are not needed any more
|
||||
since it now allowed to write for a type `T` that implements `ApproxEq`:
|
||||
`<T as ApproxEq>::approx_epsilon()`. This replaces the old form:
|
||||
`ApproxEq::approx_epsilon(None::<T>)`.
|
||||
@ -623,7 +647,7 @@ since it now allowed to write for a type `T` that implements `ApproxEq`:
|
||||
`UnitQuaternion::from_axisangle`. The new `::new` method now requires a
|
||||
not-normalized quaternion.
|
||||
|
||||
Methods names starting with `new_with_` now start with `from_`. This is more
|
||||
Method names starting with `new_with_` now start with `from_`. This is more
|
||||
idiomatic in Rust.
|
||||
|
||||
The `Norm` trait now uses an associated type instead of a type parameter.
|
||||
@ -654,8 +678,8 @@ crate for vectors, rotations and points. To enable them, activate the
|
||||
|
||||
## [0.8.0]
|
||||
### Modified
|
||||
* Almost everything (types, methods, and traits) now use full names instead
|
||||
of abbreviations (e.g. `Vec3` becomes `Vector3`). Most changes are abvious.
|
||||
* Almost everything (types, methods, and traits) now use fulls names instead
|
||||
of abbreviations (e.g. `Vec3` becomes `Vector3`). Most changes are obvious.
|
||||
Note however that:
|
||||
- `::sqnorm` becomes `::norm_squared`.
|
||||
- `::sqdist` becomes `::distance_squared`.
|
||||
@ -689,11 +713,11 @@ you [there](https://users.nphysics.org)!
|
||||
|
||||
### Removed
|
||||
* Removed zero-sized elements `Vector0`, `Point0`.
|
||||
* Removed 4-dimensional transformations `Rotation4` and `Isometry4` (which had an implementation to incomplete to be useful).
|
||||
* Removed 4-dimensional transformations `Rotation4` and `Isometry4` (which had an implementation too incomplete to be useful).
|
||||
|
||||
### Modified
|
||||
* Vectors are now multipliable with isometries. This will result into a pure rotation (this is how
|
||||
vectors differ from point semantically: they design directions so they are not translatable).
|
||||
vectors differ from point semantically: they design directions, so they are not translatable).
|
||||
* `{Isometry3, Rotation3}::look_at` reimplemented and renamed to `::look_at_rh` and `::look_at_lh` to agree
|
||||
with the computer graphics community (in particular, the GLM library). Use the `::look_at_rh`
|
||||
variant to build a view matrix that
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nalgebra"
|
||||
version = "0.27.0"
|
||||
version = "0.28.0"
|
||||
authors = [ "Sébastien Crozet <developer@crozet.re>" ]
|
||||
|
||||
description = "General-purpose linear algebra library with transformations and statically-sized or dynamically-sized matrices."
|
||||
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||
authors = [ "You" ]
|
||||
|
||||
[dependencies]
|
||||
nalgebra = "0.27.0"
|
||||
nalgebra = "0.28.0"
|
||||
|
||||
[[bin]]
|
||||
name = "example"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nalgebra-glm"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
authors = ["sebcrozet <developer@crozet.re>"]
|
||||
|
||||
description = "A computer-graphics oriented API for nalgebra, inspired by the C++ GLM library."
|
||||
@ -27,4 +27,4 @@ abomonation-serialize = [ "nalgebra/abomonation-serialize" ]
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
approx = { version = "0.5", default-features = false }
|
||||
simba = { version = "0.5", default-features = false }
|
||||
nalgebra = { path = "..", version = "0.27", default-features = false }
|
||||
nalgebra = { path = "..", version = "0.28", default-features = false }
|
||||
|
@ -21,7 +21,7 @@
|
||||
**nalgebra-glm** using the module prefix `glm::`. For example you will write `glm::rotate(...)` instead
|
||||
of the more verbose `nalgebra_glm::rotate(...)`:
|
||||
|
||||
```rust
|
||||
```
|
||||
extern crate nalgebra_glm as glm;
|
||||
```
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nalgebra-lapack"
|
||||
version = "0.18.0"
|
||||
version = "0.19.0"
|
||||
authors = [ "Sébastien Crozet <developer@crozet.re>", "Andrew Straw <strawman@astraw.com>" ]
|
||||
|
||||
description = "Matrix decompositions using nalgebra matrices and Lapack bindings."
|
||||
@ -29,7 +29,7 @@ accelerate = ["lapack-src/accelerate"]
|
||||
intel-mkl = ["lapack-src/intel-mkl"]
|
||||
|
||||
[dependencies]
|
||||
nalgebra = { version = "0.27", path = ".." }
|
||||
nalgebra = { version = "0.28", path = ".." }
|
||||
num-traits = "0.2"
|
||||
num-complex = { version = "0.4", default-features = false }
|
||||
simba = "0.5"
|
||||
@ -39,7 +39,7 @@ lapack-src = { version = "0.8", default-features = false }
|
||||
# clippy = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
nalgebra = { version = "0.27", features = [ "arbitrary", "rand" ], path = ".." }
|
||||
nalgebra = { version = "0.28", features = [ "arbitrary", "rand" ], path = ".." }
|
||||
proptest = { version = "1", default-features = false, features = ["std"] }
|
||||
quickcheck = "1"
|
||||
approx = "0.5"
|
||||
|
@ -80,6 +80,7 @@ where
|
||||
}
|
||||
|
||||
/// Retrieves the lower-triangular factor of the cholesky decomposition.
|
||||
#[must_use]
|
||||
pub fn l(&self) -> OMatrix<T, D, D> {
|
||||
let mut res = self.l.clone();
|
||||
res.fill_upper_triangle(Zero::zero(), 1);
|
||||
@ -91,6 +92,7 @@ where
|
||||
///
|
||||
/// This is an allocation-less version of `self.l()`. The values of the strict upper-triangular
|
||||
/// part are garbage and should be ignored by further computations.
|
||||
#[must_use]
|
||||
pub fn l_dirty(&self) -> &OMatrix<T, D, D> {
|
||||
&self.l
|
||||
}
|
||||
|
@ -302,6 +302,7 @@ where
|
||||
|
||||
/// The determinant of the decomposed matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn determinant(&self) -> T {
|
||||
let mut det = T::one();
|
||||
for e in self.eigenvalues.iter() {
|
||||
|
@ -89,6 +89,7 @@ where
|
||||
|
||||
/// Computes the hessenberg matrix of this decomposition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn h(&self) -> OMatrix<T, D, D> {
|
||||
let mut h = self.h.clone_owned();
|
||||
h.fill_lower_triangle(T::zero(), 2);
|
||||
@ -109,6 +110,7 @@ where
|
||||
|
||||
/// Computes the unitary matrix `Q` of this decomposition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn q(&self) -> OMatrix<T, D, D> {
|
||||
let n = self.h.nrows() as i32;
|
||||
let mut q = self.h.clone_owned();
|
||||
|
@ -30,7 +30,7 @@
|
||||
//! the system installation of netlib without LAPACKE (note the E) or
|
||||
//! CBLAS:
|
||||
//!
|
||||
//! ```.ignore
|
||||
//! ```ignore
|
||||
//! sudo apt-get install gfortran libblas3gf liblapack3gf
|
||||
//! export CARGO_FEATURE_SYSTEM_NETLIB=1
|
||||
//! export CARGO_FEATURE_EXCLUDE_LAPACKE=1
|
||||
@ -44,7 +44,7 @@
|
||||
//!
|
||||
//! On macOS, do this to use Apple's Accelerate framework:
|
||||
//!
|
||||
//! ```.ignore
|
||||
//! ```ignore
|
||||
//! export CARGO_FEATURES="--no-default-features --features accelerate"
|
||||
//! cargo build ${CARGO_FEATURES}
|
||||
//! ```
|
||||
|
@ -85,6 +85,7 @@ where
|
||||
|
||||
/// Gets the lower-triangular matrix part of the decomposition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn l(&self) -> OMatrix<T, R, DimMinimum<R, C>> {
|
||||
let (nrows, ncols) = self.lu.data.shape();
|
||||
let mut res = self.lu.columns_generic(0, nrows.min(ncols)).into_owned();
|
||||
@ -97,6 +98,7 @@ where
|
||||
|
||||
/// Gets the upper-triangular matrix part of the decomposition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn u(&self) -> OMatrix<T, DimMinimum<R, C>, C> {
|
||||
let (nrows, ncols) = self.lu.data.shape();
|
||||
let mut res = self.lu.rows_generic(0, nrows.min(ncols)).into_owned();
|
||||
@ -111,6 +113,7 @@ where
|
||||
/// Computing the permutation matrix explicitly is costly and usually not necessary.
|
||||
/// To permute rows of a matrix or vector, use the method `self.permute(...)` instead.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn p(&self) -> OMatrix<T, R, R> {
|
||||
let (dim, _) = self.lu.data.shape();
|
||||
let mut id = Matrix::identity_generic(dim, dim);
|
||||
@ -124,6 +127,7 @@ where
|
||||
|
||||
/// Gets the LAPACK permutation indices.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn permutation_indices(&self) -> &OVector<i32, DimMinimum<R, C>> {
|
||||
&self.p
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ where
|
||||
|
||||
/// Retrieves the upper trapezoidal submatrix `R` of this decomposition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn r(&self) -> OMatrix<T, DimMinimum<R, C>, C> {
|
||||
let (nrows, ncols) = self.qr.data.shape();
|
||||
self.qr.rows_generic(0, nrows.min(ncols)).upper_triangle()
|
||||
@ -117,6 +118,7 @@ where
|
||||
|
||||
/// Computes the orthogonal matrix `Q` of this decomposition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn q(&self) -> OMatrix<T, R, DimMinimum<R, C>> {
|
||||
let (nrows, ncols) = self.qr.data.shape();
|
||||
let min_nrows_ncols = nrows.min(ncols);
|
||||
|
@ -138,6 +138,7 @@ where
|
||||
/// Computes the real eigenvalues of the decomposed matrix.
|
||||
///
|
||||
/// Return `None` if some eigenvalues are complex.
|
||||
#[must_use]
|
||||
pub fn eigenvalues(&self) -> Option<OVector<T, D>> {
|
||||
if self.im.iter().all(|e| e.is_zero()) {
|
||||
Some(self.re.clone())
|
||||
@ -147,6 +148,7 @@ where
|
||||
}
|
||||
|
||||
/// Computes the complex eigenvalues of the decomposed matrix.
|
||||
#[must_use]
|
||||
pub fn complex_eigenvalues(&self) -> OVector<Complex<T>, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<Complex<T>, D>,
|
||||
|
@ -175,6 +175,7 @@ macro_rules! svd_impl(
|
||||
///
|
||||
/// All singular value below epsilon will be set to zero instead of being inverted.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pseudo_inverse(&self, epsilon: $t) -> OMatrix<$t, C, R> {
|
||||
let nrows = self.u.data.shape().0;
|
||||
let ncols = self.vt.data.shape().1;
|
||||
@ -207,6 +208,7 @@ macro_rules! svd_impl(
|
||||
/// This is the number of singular values that are not too small (i.e. greater than
|
||||
/// the given `epsilon`).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn rank(&self, epsilon: $t) -> usize {
|
||||
let mut i = 0;
|
||||
|
||||
|
@ -138,6 +138,7 @@ where
|
||||
|
||||
/// The determinant of the decomposed matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn determinant(&self) -> T {
|
||||
let mut det = T::one();
|
||||
for e in self.eigenvalues.iter() {
|
||||
@ -150,6 +151,7 @@ where
|
||||
/// Rebuild the original matrix.
|
||||
///
|
||||
/// This is useful if some of the eigenvalues have been manually modified.
|
||||
#[must_use]
|
||||
pub fn recompose(&self) -> OMatrix<T, D, D> {
|
||||
let mut u_t = self.eigenvectors.clone();
|
||||
for i in 0..self.eigenvalues.len() {
|
||||
|
@ -21,5 +21,5 @@ quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nalgebra = { version = "0.27.0", path = ".." }
|
||||
nalgebra = { version = "0.28.0", path = ".." }
|
||||
trybuild = "1.0.42"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nalgebra-sparse"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
authors = [ "Andreas Longva", "Sébastien Crozet <developer@crozet.re>" ]
|
||||
edition = "2018"
|
||||
description = "Sparse matrix computation based on nalgebra."
|
||||
@ -20,7 +20,7 @@ compare = [ "matrixcompare-core" ]
|
||||
slow-tests = []
|
||||
|
||||
[dependencies]
|
||||
nalgebra = { version="0.27", path = "../" }
|
||||
nalgebra = { version="0.28", path = "../" }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
proptest = { version = "1.0", optional = true }
|
||||
matrixcompare-core = { version = "0.1.0", optional = true }
|
||||
@ -28,7 +28,7 @@ matrixcompare-core = { version = "0.1.0", optional = true }
|
||||
[dev-dependencies]
|
||||
itertools = "0.10"
|
||||
matrixcompare = { version = "0.3.0", features = [ "proptest-support" ] }
|
||||
nalgebra = { version="0.27", path = "../", features = ["compare"] }
|
||||
nalgebra = { version="0.28", path = "../", features = ["compare"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
# Enable certain features when building docs for docs.rs
|
||||
|
@ -7,7 +7,7 @@
|
||||
//! The following example illustrates how to convert between matrix formats with the `From`
|
||||
//! implementations.
|
||||
//!
|
||||
//! ```rust
|
||||
//! ```
|
||||
//! use nalgebra_sparse::{csr::CsrMatrix, csc::CscMatrix, coo::CooMatrix};
|
||||
//! use nalgebra::DMatrix;
|
||||
//!
|
||||
|
@ -20,7 +20,7 @@ use crate::SparseFormatError;
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// ```
|
||||
/// use nalgebra_sparse::{coo::CooMatrix, csr::CsrMatrix, csc::CscMatrix};
|
||||
///
|
||||
/// // Initialize a matrix with all zeros (no explicitly stored entries).
|
||||
@ -45,6 +45,43 @@ pub struct CooMatrix<T> {
|
||||
values: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T: na::Scalar> CooMatrix<T> {
|
||||
/// Pushes a dense matrix into the sparse one.
|
||||
///
|
||||
/// This adds the dense matrix `m` starting at the `r`th row and `c`th column
|
||||
/// to the matrix.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
///
|
||||
/// Panics if any part of the dense matrix is out of bounds of the sparse matrix
|
||||
/// when inserted at `(r, c)`.
|
||||
#[inline]
|
||||
pub fn push_matrix<R: na::Dim, C: na::Dim, S: nalgebra::storage::Storage<T, R, C>>(
|
||||
&mut self,
|
||||
r: usize,
|
||||
c: usize,
|
||||
m: &na::Matrix<T, R, C, S>,
|
||||
) {
|
||||
let block_nrows = m.nrows();
|
||||
let block_ncols = m.ncols();
|
||||
let max_row_with_block = r + block_nrows - 1;
|
||||
let max_col_with_block = c + block_ncols - 1;
|
||||
assert!(max_row_with_block < self.nrows);
|
||||
assert!(max_col_with_block < self.ncols);
|
||||
|
||||
self.reserve(block_ncols * block_nrows);
|
||||
|
||||
for (col_idx, col) in m.column_iter().enumerate() {
|
||||
for (row_idx, v) in col.iter().enumerate() {
|
||||
self.row_indices.push(r + row_idx);
|
||||
self.col_indices.push(c + col_idx);
|
||||
self.values.push(v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CooMatrix<T> {
|
||||
/// Construct a zero COO matrix of the given dimensions.
|
||||
///
|
||||
@ -173,12 +210,14 @@ impl<T> CooMatrix<T> {
|
||||
|
||||
/// The number of rows in the matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nrows(&self) -> usize {
|
||||
self.nrows
|
||||
}
|
||||
|
||||
/// The number of columns in the matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ncols(&self) -> usize {
|
||||
self.ncols
|
||||
}
|
||||
@ -189,21 +228,25 @@ impl<T> CooMatrix<T> {
|
||||
/// entries, then it may have a different number of non-zeros as reported by `nnz()` compared
|
||||
/// to its CSR representation.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nnz(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
/// The row indices of the explicitly stored entries.
|
||||
#[must_use]
|
||||
pub fn row_indices(&self) -> &[usize] {
|
||||
&self.row_indices
|
||||
}
|
||||
|
||||
/// The column indices of the explicitly stored entries.
|
||||
#[must_use]
|
||||
pub fn col_indices(&self) -> &[usize] {
|
||||
&self.col_indices
|
||||
}
|
||||
|
||||
/// The values of the explicitly stored entries.
|
||||
#[must_use]
|
||||
pub fn values(&self) -> &[T] {
|
||||
&self.values
|
||||
}
|
||||
|
@ -32,11 +32,13 @@ impl<T> CsMatrix<T> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pattern(&self) -> &SparsityPattern {
|
||||
&self.sparsity_pattern
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn values(&self) -> &[T] {
|
||||
&self.values
|
||||
}
|
||||
@ -48,6 +50,7 @@ impl<T> CsMatrix<T> {
|
||||
|
||||
/// Returns the raw data represented as a tuple `(major_offsets, minor_indices, values)`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cs_data(&self) -> (&[usize], &[usize], &[T]) {
|
||||
let pattern = self.pattern();
|
||||
(
|
||||
@ -88,6 +91,7 @@ impl<T> CsMatrix<T> {
|
||||
|
||||
/// Internal method for simplifying access to a lane's data
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_index_range(&self, row_index: usize) -> Option<Range<usize>> {
|
||||
let row_begin = *self.sparsity_pattern.major_offsets().get(row_index)?;
|
||||
let row_end = *self.sparsity_pattern.major_offsets().get(row_index + 1)?;
|
||||
@ -111,6 +115,7 @@ impl<T> CsMatrix<T> {
|
||||
|
||||
/// Returns an entry for the given major/minor indices, or `None` if the indices are out
|
||||
/// of bounds.
|
||||
#[must_use]
|
||||
pub fn get_entry(&self, major_index: usize, minor_index: usize) -> Option<SparseEntry<T>> {
|
||||
let row_range = self.get_index_range(major_index)?;
|
||||
let (_, minor_indices, values) = self.cs_data();
|
||||
@ -139,6 +144,7 @@ impl<T> CsMatrix<T> {
|
||||
get_mut_entry_from_slices(minor_dim, minor_indices, values, minor_index)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_lane(&self, index: usize) -> Option<CsLane<T>> {
|
||||
let range = self.get_index_range(index)?;
|
||||
let (_, minor_indices, values) = self.cs_data();
|
||||
@ -150,6 +156,7 @@ impl<T> CsMatrix<T> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_lane_mut(&mut self, index: usize) -> Option<CsLaneMut<T>> {
|
||||
let range = self.get_index_range(index)?;
|
||||
let minor_dim = self.pattern().minor_dim();
|
||||
@ -172,6 +179,7 @@ impl<T> CsMatrix<T> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn filter<P>(&self, predicate: P) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -207,6 +215,7 @@ impl<T> CsMatrix<T> {
|
||||
}
|
||||
|
||||
/// Returns the diagonal of the matrix as a sparse matrix.
|
||||
#[must_use]
|
||||
pub fn diagonal_as_matrix(&self) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -372,26 +381,31 @@ macro_rules! impl_cs_lane_common_methods {
|
||||
($name:ty) => {
|
||||
impl<'a, T> $name {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn minor_dim(&self) -> usize {
|
||||
self.minor_dim
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nnz(&self) -> usize {
|
||||
self.minor_indices.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn minor_indices(&self) -> &[usize] {
|
||||
self.minor_indices
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn values(&self) -> &[T] {
|
||||
self.values
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_entry(&self, global_col_index: usize) -> Option<SparseEntry<T>> {
|
||||
get_entry_from_slices(
|
||||
self.minor_dim,
|
||||
@ -416,6 +430,7 @@ impl<'a, T> CsLaneMut<'a, T> {
|
||||
(self.minor_indices, self.values)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_entry_mut(&mut self, global_minor_index: usize) -> Option<SparseEntryMut<T>> {
|
||||
get_mut_entry_from_slices(
|
||||
self.minor_dim,
|
||||
|
@ -19,7 +19,7 @@ use std::slice::{Iter, IterMut};
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```rust
|
||||
/// ```
|
||||
/// use nalgebra_sparse::csc::CscMatrix;
|
||||
/// use nalgebra::{DMatrix, Matrix3x4};
|
||||
/// use matrixcompare::assert_matrix_eq;
|
||||
@ -97,7 +97,7 @@ use std::slice::{Iter, IterMut};
|
||||
/// represents the matrix in a column-by-column fashion. The entries associated with column `j` are
|
||||
/// determined as follows:
|
||||
///
|
||||
/// ```rust
|
||||
/// ```
|
||||
/// # let col_offsets: Vec<usize> = vec![0, 0];
|
||||
/// # let row_indices: Vec<usize> = vec![];
|
||||
/// # let values: Vec<i32> = vec![];
|
||||
@ -192,12 +192,14 @@ impl<T> CscMatrix<T> {
|
||||
|
||||
/// The number of rows in the matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nrows(&self) -> usize {
|
||||
self.cs.pattern().minor_dim()
|
||||
}
|
||||
|
||||
/// The number of columns in the matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ncols(&self) -> usize {
|
||||
self.cs.pattern().major_dim()
|
||||
}
|
||||
@ -208,24 +210,28 @@ impl<T> CscMatrix<T> {
|
||||
/// number of algebraically zero entries in the matrix. Explicitly stored entries can still
|
||||
/// be zero. Corresponds to the number of entries in the sparsity pattern.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nnz(&self) -> usize {
|
||||
self.pattern().nnz()
|
||||
}
|
||||
|
||||
/// The column offsets defining part of the CSC format.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn col_offsets(&self) -> &[usize] {
|
||||
self.pattern().major_offsets()
|
||||
}
|
||||
|
||||
/// The row indices defining part of the CSC format.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row_indices(&self) -> &[usize] {
|
||||
self.pattern().minor_indices()
|
||||
}
|
||||
|
||||
/// The non-zero values defining part of the CSC format.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn values(&self) -> &[T] {
|
||||
self.cs.values()
|
||||
}
|
||||
@ -298,6 +304,7 @@ impl<T> CscMatrix<T> {
|
||||
/// ------
|
||||
/// Panics if column index is out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn col(&self, index: usize) -> CscCol<T> {
|
||||
self.get_col(index).expect("Row index must be in bounds")
|
||||
}
|
||||
@ -315,12 +322,14 @@ impl<T> CscMatrix<T> {
|
||||
|
||||
/// Return the column at the given column index, or `None` if out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_col(&self, index: usize) -> Option<CscCol<T>> {
|
||||
self.cs.get_lane(index).map(|lane| CscCol { lane })
|
||||
}
|
||||
|
||||
/// Mutable column access for the given column index, or `None` if out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_col_mut(&mut self, index: usize) -> Option<CscColMut<T>> {
|
||||
self.cs.get_lane_mut(index).map(|lane| CscColMut { lane })
|
||||
}
|
||||
@ -381,6 +390,7 @@ impl<T> CscMatrix<T> {
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying sparsity pattern.
|
||||
#[must_use]
|
||||
pub fn pattern(&self) -> &SparsityPattern {
|
||||
self.cs.pattern()
|
||||
}
|
||||
@ -397,6 +407,7 @@ impl<T> CscMatrix<T> {
|
||||
///
|
||||
/// Each call to this function incurs the cost of a binary search among the explicitly
|
||||
/// stored row entries for the given column.
|
||||
#[must_use]
|
||||
pub fn get_entry(&self, row_index: usize, col_index: usize) -> Option<SparseEntry<T>> {
|
||||
self.cs.get_entry(col_index, row_index)
|
||||
}
|
||||
@ -422,6 +433,7 @@ impl<T> CscMatrix<T> {
|
||||
/// Panics
|
||||
/// ------
|
||||
/// Panics if `row_index` or `col_index` is out of bounds.
|
||||
#[must_use]
|
||||
pub fn index_entry(&self, row_index: usize, col_index: usize) -> SparseEntry<T> {
|
||||
self.get_entry(row_index, col_index)
|
||||
.expect("Out of bounds matrix indices encountered")
|
||||
@ -441,6 +453,7 @@ impl<T> CscMatrix<T> {
|
||||
}
|
||||
|
||||
/// Returns a triplet of slices `(col_offsets, row_indices, values)` that make up the CSC data.
|
||||
#[must_use]
|
||||
pub fn csc_data(&self) -> (&[usize], &[usize], &[T]) {
|
||||
self.cs.cs_data()
|
||||
}
|
||||
@ -453,6 +466,7 @@ impl<T> CscMatrix<T> {
|
||||
|
||||
/// Creates a sparse matrix that contains only the explicit entries decided by the
|
||||
/// given predicate.
|
||||
#[must_use]
|
||||
pub fn filter<P>(&self, predicate: P) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -470,6 +484,7 @@ impl<T> CscMatrix<T> {
|
||||
/// Returns a new matrix representing the upper triangular part of this matrix.
|
||||
///
|
||||
/// The result includes the diagonal of the matrix.
|
||||
#[must_use]
|
||||
pub fn upper_triangle(&self) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -480,6 +495,7 @@ impl<T> CscMatrix<T> {
|
||||
/// Returns a new matrix representing the lower triangular part of this matrix.
|
||||
///
|
||||
/// The result includes the diagonal of the matrix.
|
||||
#[must_use]
|
||||
pub fn lower_triangle(&self) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -488,6 +504,7 @@ impl<T> CscMatrix<T> {
|
||||
}
|
||||
|
||||
/// Returns the diagonal of the matrix as a sparse matrix.
|
||||
#[must_use]
|
||||
pub fn diagonal_as_csc(&self) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -498,6 +515,7 @@ impl<T> CscMatrix<T> {
|
||||
}
|
||||
|
||||
/// Compute the transpose of the matrix.
|
||||
#[must_use]
|
||||
pub fn transpose(&self) -> CscMatrix<T>
|
||||
where
|
||||
T: Scalar,
|
||||
@ -617,24 +635,28 @@ macro_rules! impl_csc_col_common_methods {
|
||||
impl<'a, T> $name {
|
||||
/// The number of global rows in the column.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nrows(&self) -> usize {
|
||||
self.lane.minor_dim()
|
||||
}
|
||||
|
||||
/// The number of non-zeros in this column.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nnz(&self) -> usize {
|
||||
self.lane.nnz()
|
||||
}
|
||||
|
||||
/// The row indices corresponding to explicitly stored entries in this column.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row_indices(&self) -> &[usize] {
|
||||
self.lane.minor_indices()
|
||||
}
|
||||
|
||||
/// The values corresponding to explicitly stored entries in this column.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn values(&self) -> &[T] {
|
||||
self.lane.values()
|
||||
}
|
||||
@ -643,6 +665,7 @@ macro_rules! impl_csc_col_common_methods {
|
||||
///
|
||||
/// Each call to this function incurs the cost of a binary search among the explicitly
|
||||
/// stored row entries.
|
||||
#[must_use]
|
||||
pub fn get_entry(&self, global_row_index: usize) -> Option<SparseEntry<T>> {
|
||||
self.lane.get_entry(global_row_index)
|
||||
}
|
||||
@ -669,6 +692,7 @@ impl<'a, T> CscColMut<'a, T> {
|
||||
}
|
||||
|
||||
/// Returns a mutable entry for the given global row index.
|
||||
#[must_use]
|
||||
pub fn get_entry_mut(&mut self, global_row_index: usize) -> Option<SparseEntryMut<T>> {
|
||||
self.lane.get_entry_mut(global_row_index)
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use std::slice::{Iter, IterMut};
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```rust
|
||||
/// ```
|
||||
/// use nalgebra_sparse::csr::CsrMatrix;
|
||||
/// use nalgebra::{DMatrix, Matrix3x4};
|
||||
/// use matrixcompare::assert_matrix_eq;
|
||||
@ -97,7 +97,7 @@ use std::slice::{Iter, IterMut};
|
||||
/// represents the matrix in a row-by-row fashion. The entries associated with row `i` are
|
||||
/// determined as follows:
|
||||
///
|
||||
/// ```rust
|
||||
/// ```
|
||||
/// # let row_offsets: Vec<usize> = vec![0, 0];
|
||||
/// # let col_indices: Vec<usize> = vec![];
|
||||
/// # let values: Vec<i32> = vec![];
|
||||
@ -192,12 +192,14 @@ impl<T> CsrMatrix<T> {
|
||||
|
||||
/// The number of rows in the matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nrows(&self) -> usize {
|
||||
self.cs.pattern().major_dim()
|
||||
}
|
||||
|
||||
/// The number of columns in the matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ncols(&self) -> usize {
|
||||
self.cs.pattern().minor_dim()
|
||||
}
|
||||
@ -208,12 +210,14 @@ impl<T> CsrMatrix<T> {
|
||||
/// number of algebraically zero entries in the matrix. Explicitly stored entries can still
|
||||
/// be zero. Corresponds to the number of entries in the sparsity pattern.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nnz(&self) -> usize {
|
||||
self.cs.pattern().nnz()
|
||||
}
|
||||
|
||||
/// The row offsets defining part of the CSR format.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row_offsets(&self) -> &[usize] {
|
||||
let (offsets, _, _) = self.cs.cs_data();
|
||||
offsets
|
||||
@ -221,6 +225,7 @@ impl<T> CsrMatrix<T> {
|
||||
|
||||
/// The column indices defining part of the CSR format.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn col_indices(&self) -> &[usize] {
|
||||
let (_, indices, _) = self.cs.cs_data();
|
||||
indices
|
||||
@ -228,6 +233,7 @@ impl<T> CsrMatrix<T> {
|
||||
|
||||
/// The non-zero values defining part of the CSR format.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn values(&self) -> &[T] {
|
||||
self.cs.values()
|
||||
}
|
||||
@ -300,6 +306,7 @@ impl<T> CsrMatrix<T> {
|
||||
/// ------
|
||||
/// Panics if row index is out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row(&self, index: usize) -> CsrRow<T> {
|
||||
self.get_row(index).expect("Row index must be in bounds")
|
||||
}
|
||||
@ -317,12 +324,14 @@ impl<T> CsrMatrix<T> {
|
||||
|
||||
/// Return the row at the given row index, or `None` if out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_row(&self, index: usize) -> Option<CsrRow<T>> {
|
||||
self.cs.get_lane(index).map(|lane| CsrRow { lane })
|
||||
}
|
||||
|
||||
/// Mutable row access for the given row index, or `None` if out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_row_mut(&mut self, index: usize) -> Option<CsrRowMut<T>> {
|
||||
self.cs.get_lane_mut(index).map(|lane| CsrRowMut { lane })
|
||||
}
|
||||
@ -383,6 +392,7 @@ impl<T> CsrMatrix<T> {
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying sparsity pattern.
|
||||
#[must_use]
|
||||
pub fn pattern(&self) -> &SparsityPattern {
|
||||
self.cs.pattern()
|
||||
}
|
||||
@ -399,6 +409,7 @@ impl<T> CsrMatrix<T> {
|
||||
///
|
||||
/// Each call to this function incurs the cost of a binary search among the explicitly
|
||||
/// stored column entries for the given row.
|
||||
#[must_use]
|
||||
pub fn get_entry(&self, row_index: usize, col_index: usize) -> Option<SparseEntry<T>> {
|
||||
self.cs.get_entry(row_index, col_index)
|
||||
}
|
||||
@ -424,6 +435,7 @@ impl<T> CsrMatrix<T> {
|
||||
/// Panics
|
||||
/// ------
|
||||
/// Panics if `row_index` or `col_index` is out of bounds.
|
||||
#[must_use]
|
||||
pub fn index_entry(&self, row_index: usize, col_index: usize) -> SparseEntry<T> {
|
||||
self.get_entry(row_index, col_index)
|
||||
.expect("Out of bounds matrix indices encountered")
|
||||
@ -443,6 +455,7 @@ impl<T> CsrMatrix<T> {
|
||||
}
|
||||
|
||||
/// Returns a triplet of slices `(row_offsets, col_indices, values)` that make up the CSR data.
|
||||
#[must_use]
|
||||
pub fn csr_data(&self) -> (&[usize], &[usize], &[T]) {
|
||||
self.cs.cs_data()
|
||||
}
|
||||
@ -455,6 +468,7 @@ impl<T> CsrMatrix<T> {
|
||||
|
||||
/// Creates a sparse matrix that contains only the explicit entries decided by the
|
||||
/// given predicate.
|
||||
#[must_use]
|
||||
pub fn filter<P>(&self, predicate: P) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -470,6 +484,7 @@ impl<T> CsrMatrix<T> {
|
||||
/// Returns a new matrix representing the upper triangular part of this matrix.
|
||||
///
|
||||
/// The result includes the diagonal of the matrix.
|
||||
#[must_use]
|
||||
pub fn upper_triangle(&self) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -480,6 +495,7 @@ impl<T> CsrMatrix<T> {
|
||||
/// Returns a new matrix representing the lower triangular part of this matrix.
|
||||
///
|
||||
/// The result includes the diagonal of the matrix.
|
||||
#[must_use]
|
||||
pub fn lower_triangle(&self) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -488,6 +504,7 @@ impl<T> CsrMatrix<T> {
|
||||
}
|
||||
|
||||
/// Returns the diagonal of the matrix as a sparse matrix.
|
||||
#[must_use]
|
||||
pub fn diagonal_as_csr(&self) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
@ -498,6 +515,7 @@ impl<T> CsrMatrix<T> {
|
||||
}
|
||||
|
||||
/// Compute the transpose of the matrix.
|
||||
#[must_use]
|
||||
pub fn transpose(&self) -> CsrMatrix<T>
|
||||
where
|
||||
T: Scalar,
|
||||
@ -617,24 +635,28 @@ macro_rules! impl_csr_row_common_methods {
|
||||
impl<'a, T> $name {
|
||||
/// The number of global columns in the row.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ncols(&self) -> usize {
|
||||
self.lane.minor_dim()
|
||||
}
|
||||
|
||||
/// The number of non-zeros in this row.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nnz(&self) -> usize {
|
||||
self.lane.nnz()
|
||||
}
|
||||
|
||||
/// The column indices corresponding to explicitly stored entries in this row.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn col_indices(&self) -> &[usize] {
|
||||
self.lane.minor_indices()
|
||||
}
|
||||
|
||||
/// The values corresponding to explicitly stored entries in this row.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn values(&self) -> &[T] {
|
||||
self.lane.values()
|
||||
}
|
||||
@ -644,6 +666,7 @@ macro_rules! impl_csr_row_common_methods {
|
||||
/// Each call to this function incurs the cost of a binary search among the explicitly
|
||||
/// stored column entries.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_entry(&self, global_col_index: usize) -> Option<SparseEntry<T>> {
|
||||
self.lane.get_entry(global_col_index)
|
||||
}
|
||||
@ -673,6 +696,7 @@ impl<'a, T> CsrRowMut<'a, T> {
|
||||
|
||||
/// Returns a mutable entry for the given global column index.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_entry_mut(&mut self, global_col_index: usize) -> Option<SparseEntryMut<T>> {
|
||||
self.lane.get_entry_mut(global_col_index)
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ impl CscSymbolicCholesky {
|
||||
}
|
||||
|
||||
/// The pattern of the Cholesky factor `L`.
|
||||
#[must_use]
|
||||
pub fn l_pattern(&self) -> &SparsityPattern {
|
||||
&self.l_pattern
|
||||
}
|
||||
@ -171,6 +172,7 @@ impl<T: RealField> CscCholesky<T> {
|
||||
}
|
||||
|
||||
/// Returns a reference to the Cholesky factor `L`.
|
||||
#[must_use]
|
||||
pub fn l(&self) -> &CscMatrix<T> {
|
||||
&self.l_factor
|
||||
}
|
||||
@ -260,6 +262,7 @@ impl<T: RealField> CscCholesky<T> {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `B` is not square.
|
||||
#[must_use = "Did you mean to use solve_mut()?"]
|
||||
pub fn solve<'a>(&'a self, b: impl Into<DMatrixSlice<'a, T>>) -> DMatrix<T> {
|
||||
let b = b.into();
|
||||
let mut output = b.clone_owned();
|
||||
|
@ -73,7 +73,7 @@
|
||||
//!
|
||||
//! # Example: COO -> CSR -> matrix-vector product
|
||||
//!
|
||||
//! ```rust
|
||||
//! ```
|
||||
//! use nalgebra_sparse::{coo::CooMatrix, csr::CsrMatrix};
|
||||
//! use nalgebra::{DMatrix, DVector};
|
||||
//! use matrixcompare::assert_matrix_eq;
|
||||
@ -93,6 +93,9 @@
|
||||
//! coo.push(1, 2, 1.3);
|
||||
//! coo.push(2, 2, 4.1);
|
||||
//!
|
||||
//! // ... or add entire dense matrices like so:
|
||||
//! // coo.push_matrix(0, 0, &dense);
|
||||
//!
|
||||
//! // The simplest way to construct a CSR matrix is to first construct a COO matrix, and
|
||||
//! // then convert it to CSR. The `From` trait is implemented for conversions between different
|
||||
//! // sparse matrix types.
|
||||
@ -170,6 +173,7 @@ pub struct SparseFormatError {
|
||||
|
||||
impl SparseFormatError {
|
||||
/// The type of error.
|
||||
#[must_use]
|
||||
pub fn kind(&self) -> &SparseFormatErrorKind {
|
||||
&self.kind
|
||||
}
|
||||
|
@ -90,7 +90,7 @@
|
||||
//! `C <- 3.0 * C + 2.0 * A^T * B`, where `A`, `B`, `C` are matrices and `A^T` is the transpose
|
||||
//! of `A`. The simplest way to write this is:
|
||||
//!
|
||||
//! ```rust
|
||||
//! ```
|
||||
//! # use nalgebra_sparse::csr::CsrMatrix;
|
||||
//! # let a = CsrMatrix::identity(10); let b = CsrMatrix::identity(10);
|
||||
//! # let mut c = CsrMatrix::identity(10);
|
||||
@ -109,7 +109,7 @@
|
||||
//!
|
||||
//! An alternative way to implement this expression (here using CSR matrices) is:
|
||||
//!
|
||||
//! ```rust
|
||||
//! ```
|
||||
//! # use nalgebra_sparse::csr::CsrMatrix;
|
||||
//! # let a = CsrMatrix::identity(10); let b = CsrMatrix::identity(10);
|
||||
//! # let mut c = CsrMatrix::identity(10);
|
||||
@ -140,11 +140,13 @@ pub enum Op<T> {
|
||||
|
||||
impl<T> Op<T> {
|
||||
/// Returns a reference to the inner value that the operation applies to.
|
||||
#[must_use]
|
||||
pub fn inner_ref(&self) -> &T {
|
||||
self.as_ref().into_inner()
|
||||
}
|
||||
|
||||
/// Returns an `Op` applied to a reference of the inner value.
|
||||
#[must_use]
|
||||
pub fn as_ref(&self) -> Op<&T> {
|
||||
match self {
|
||||
Op::NoOp(obj) => Op::NoOp(&obj),
|
||||
|
@ -96,11 +96,13 @@ impl OperationError {
|
||||
}
|
||||
|
||||
/// The operation error kind.
|
||||
#[must_use]
|
||||
pub fn kind(&self) -> &OperationErrorKind {
|
||||
&self.error_kind
|
||||
}
|
||||
|
||||
/// The underlying error message.
|
||||
#[must_use]
|
||||
pub fn message(&self) -> &str {
|
||||
self.message.as_str()
|
||||
}
|
||||
|
@ -60,18 +60,21 @@ impl SparsityPattern {
|
||||
|
||||
/// The offsets for the major dimension.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn major_offsets(&self) -> &[usize] {
|
||||
&self.major_offsets
|
||||
}
|
||||
|
||||
/// The indices for the minor dimension.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn minor_indices(&self) -> &[usize] {
|
||||
&self.minor_indices
|
||||
}
|
||||
|
||||
/// The number of major lanes in the pattern.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn major_dim(&self) -> usize {
|
||||
assert!(self.major_offsets.len() > 0);
|
||||
self.major_offsets.len() - 1
|
||||
@ -79,12 +82,14 @@ impl SparsityPattern {
|
||||
|
||||
/// The number of minor lanes in the pattern.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn minor_dim(&self) -> usize {
|
||||
self.minor_dim
|
||||
}
|
||||
|
||||
/// The number of "non-zeros", i.e. explicitly stored entries in the pattern.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nnz(&self) -> usize {
|
||||
self.minor_indices.len()
|
||||
}
|
||||
@ -96,12 +101,14 @@ impl SparsityPattern {
|
||||
///
|
||||
/// Panics if `major_index` is out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lane(&self, major_index: usize) -> &[usize] {
|
||||
self.get_lane(major_index).unwrap()
|
||||
}
|
||||
|
||||
/// Get the lane at the given index, or `None` if out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_lane(&self, major_index: usize) -> Option<&[usize]> {
|
||||
let offset_begin = *self.major_offsets().get(major_index)?;
|
||||
let offset_end = *self.major_offsets().get(major_index + 1)?;
|
||||
@ -197,6 +204,7 @@ impl SparsityPattern {
|
||||
/// assert_eq!(entries, vec![(0, 0), (0, 2), (1, 1), (2, 0)]);
|
||||
/// ```
|
||||
///
|
||||
#[must_use]
|
||||
pub fn entries(&self) -> SparsityPatternIter {
|
||||
SparsityPatternIter::from_pattern(self)
|
||||
}
|
||||
@ -228,6 +236,7 @@ impl SparsityPattern {
|
||||
///
|
||||
/// This is analogous to matrix transposition, i.e. an entry `(i, j)` becomes `(j, i)` in the
|
||||
/// new pattern.
|
||||
#[must_use]
|
||||
pub fn transpose(&self) -> Self {
|
||||
// By using unit () values, we can use the same routines as for CSR/CSC matrices
|
||||
let values = vec![(); self.nnz()];
|
||||
|
@ -252,3 +252,95 @@ fn coo_push_out_of_bounds_entries() {
|
||||
assert_panics!(coo.clone().push(3, 2, 1));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coo_push_matrix_valid_entries() {
|
||||
let mut coo = CooMatrix::new(3, 3);
|
||||
|
||||
// Works with static
|
||||
{
|
||||
// new is row-major...
|
||||
let inserted = nalgebra::SMatrix::<i32, 2, 2>::new(1, 2, 3, 4);
|
||||
coo.push_matrix(1, 1, &inserted);
|
||||
|
||||
// insert happens column-major, so expect transposition when read this way
|
||||
assert_eq!(
|
||||
coo.triplet_iter().collect::<Vec<_>>(),
|
||||
vec![(1, 1, &1), (2, 1, &3), (1, 2, &2), (2, 2, &4)]
|
||||
);
|
||||
}
|
||||
|
||||
// Works with owned dynamic
|
||||
{
|
||||
let inserted = nalgebra::DMatrix::<i32>::repeat(1, 2, 5);
|
||||
coo.push_matrix(0, 0, &inserted);
|
||||
|
||||
assert_eq!(
|
||||
coo.triplet_iter().collect::<Vec<_>>(),
|
||||
vec![
|
||||
(1, 1, &1),
|
||||
(2, 1, &3),
|
||||
(1, 2, &2),
|
||||
(2, 2, &4),
|
||||
(0, 0, &5),
|
||||
(0, 1, &5)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Works with sliced
|
||||
{
|
||||
let source = nalgebra::SMatrix::<i32, 2, 2>::new(6, 7, 8, 9);
|
||||
let sliced = source.fixed_slice::<2, 1>(0, 0);
|
||||
coo.push_matrix(1, 0, &sliced);
|
||||
|
||||
assert_eq!(
|
||||
coo.triplet_iter().collect::<Vec<_>>(),
|
||||
vec![
|
||||
(1, 1, &1),
|
||||
(2, 1, &3),
|
||||
(1, 2, &2),
|
||||
(2, 2, &4),
|
||||
(0, 0, &5),
|
||||
(0, 1, &5),
|
||||
(1, 0, &6),
|
||||
(2, 0, &8)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coo_push_matrix_out_of_bounds_entries() {
|
||||
// 0x0
|
||||
{
|
||||
let inserted = nalgebra::SMatrix::<i32, 1, 1>::new(1);
|
||||
assert_panics!(CooMatrix::new(0, 0).push_matrix(0, 0, &inserted));
|
||||
}
|
||||
// 0x1
|
||||
{
|
||||
let inserted = nalgebra::SMatrix::<i32, 1, 1>::new(1);
|
||||
assert_panics!(CooMatrix::new(1, 0).push_matrix(0, 0, &inserted));
|
||||
}
|
||||
// 1x0
|
||||
{
|
||||
let inserted = nalgebra::SMatrix::<i32, 1, 1>::new(1);
|
||||
assert_panics!(CooMatrix::new(0, 1).push_matrix(0, 0, &inserted));
|
||||
}
|
||||
|
||||
// 3x3 exceeds col-dim
|
||||
{
|
||||
let inserted = nalgebra::SMatrix::<i32, 1, 2>::repeat(1);
|
||||
assert_panics!(CooMatrix::new(3, 3).push_matrix(0, 2, &inserted));
|
||||
}
|
||||
// 3x3 exceeds row-dim
|
||||
{
|
||||
let inserted = nalgebra::SMatrix::<i32, 2, 1>::repeat(1);
|
||||
assert_panics!(CooMatrix::new(3, 3).push_matrix(2, 0, &inserted));
|
||||
}
|
||||
// 3x3 exceeds row-dim and row-dim
|
||||
{
|
||||
let inserted = nalgebra::SMatrix::<i32, 2, 2>::repeat(1);
|
||||
assert_panics!(CooMatrix::new(3, 3).push_matrix(2, 2, &inserted));
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ pub trait Reallocator<T: Scalar, RFrom: Dim, CFrom: Dim, RTo: Dim, CTo: Dim>:
|
||||
/// Reallocates a buffer of shape `(RTo, CTo)`, possibly reusing a previously allocated buffer
|
||||
/// `buf`. Data stored by `buf` are linearly copied to the output:
|
||||
///
|
||||
/// # Safety
|
||||
/// * The copy is performed as if both were just arrays (without a matrix structure).
|
||||
/// * If `buf` is larger than the output size, then extra elements of `buf` are truncated.
|
||||
/// * If `buf` is smaller than the output size, then extra elements of the output are left
|
||||
|
@ -101,8 +101,8 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[T] {
|
||||
unsafe { std::slice::from_raw_parts(self.ptr(), R * C) }
|
||||
unsafe fn as_slice_unchecked(&self) -> &[T] {
|
||||
std::slice::from_raw_parts(self.ptr(), R * C)
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,8 +118,8 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
unsafe { std::slice::from_raw_parts_mut(self.ptr_mut(), R * C) }
|
||||
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
|
||||
std::slice::from_raw_parts_mut(self.ptr_mut(), R * C)
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,11 +286,7 @@ where
|
||||
unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> {
|
||||
for element in self.as_mut_slice() {
|
||||
let temp = bytes;
|
||||
bytes = if let Some(remainder) = element.exhume(temp) {
|
||||
remainder
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
bytes = element.exhume(temp)?
|
||||
}
|
||||
Some(bytes)
|
||||
}
|
||||
@ -327,7 +323,7 @@ mod rkyv_impl {
|
||||
for ArrayStorage<T, R, C>
|
||||
{
|
||||
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
|
||||
Ok(self.0.serialize(serializer)?)
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,6 +193,7 @@ where
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn dot<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> T
|
||||
where
|
||||
SB: Storage<T, R2, C2>,
|
||||
@ -221,6 +222,7 @@ where
|
||||
/// assert_ne!(vec1.dotc(&vec2), vec1.dot(&vec2));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn dotc<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> T
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -248,6 +250,7 @@ where
|
||||
/// assert_eq!(mat1.tr_dot(&mat2), 9.1);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn tr_dot<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> T
|
||||
where
|
||||
SB: Storage<T, R2, C2>,
|
||||
@ -275,6 +278,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn array_axcpy<T>(
|
||||
y: &mut [T],
|
||||
a: T,
|
||||
@ -331,6 +335,7 @@ where
|
||||
/// assert_eq!(vec1, Vector3::new(6.0, 12.0, 18.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub fn axcpy<D2: Dim, SB>(&mut self, a: T, x: &Vector<T, D2, SB>, c: T, b: T)
|
||||
where
|
||||
SB: Storage<T, D2>,
|
||||
@ -341,13 +346,17 @@ where
|
||||
let rstride1 = self.strides().0;
|
||||
let rstride2 = x.strides().0;
|
||||
|
||||
let y = self.data.as_mut_slice();
|
||||
let x = x.data.as_slice();
|
||||
unsafe {
|
||||
// SAFETY: the conversion to slices is OK because we access the
|
||||
// elements taking the strides into account.
|
||||
let y = self.data.as_mut_slice_unchecked();
|
||||
let x = x.data.as_slice_unchecked();
|
||||
|
||||
if !b.is_zero() {
|
||||
array_axcpy(y, a, x, c, b, rstride1, rstride2, x.len());
|
||||
} else {
|
||||
array_axc(y, a, x, c, rstride1, rstride2, x.len());
|
||||
if !b.is_zero() {
|
||||
array_axcpy(y, a, x, c, b, rstride1, rstride2, x.len());
|
||||
} else {
|
||||
array_axc(y, a, x, c, rstride1, rstride2, x.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1379,12 +1388,12 @@ where
|
||||
{
|
||||
work.gemv(T::one(), mid, &rhs.column(0), T::zero());
|
||||
self.column_mut(0)
|
||||
.gemv_tr(alpha.inlined_clone(), &rhs, work, beta.inlined_clone());
|
||||
.gemv_tr(alpha.inlined_clone(), rhs, work, beta.inlined_clone());
|
||||
|
||||
for j in 1..rhs.ncols() {
|
||||
work.gemv(T::one(), mid, &rhs.column(j), T::zero());
|
||||
self.column_mut(j)
|
||||
.gemv_tr(alpha.inlined_clone(), &rhs, work, beta.inlined_clone());
|
||||
.gemv_tr(alpha.inlined_clone(), rhs, work, beta.inlined_clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,7 +386,7 @@ impl<T: Scalar + Zero + One + ClosedMul + ClosedAdd, D: DimName, S: Storage<T, D
|
||||
(D::dim() - 1, 0),
|
||||
(Const::<1>, DimNameDiff::<D, U1>::name()),
|
||||
)
|
||||
.tr_dot(&shift);
|
||||
.tr_dot(shift);
|
||||
let post_translation = self.generic_slice(
|
||||
(0, 0),
|
||||
(DimNameDiff::<D, U1>::name(), DimNameDiff::<D, U1>::name()),
|
||||
@ -423,7 +423,7 @@ where
|
||||
(D::dim() - 1, 0),
|
||||
(Const::<1>, DimNameDiff::<D, U1>::name()),
|
||||
);
|
||||
let n = normalizer.tr_dot(&v);
|
||||
let n = normalizer.tr_dot(v);
|
||||
|
||||
if !n.is_zero() {
|
||||
return transform * (v / n);
|
||||
|
@ -28,6 +28,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(a.abs(), Matrix2::new(0.0, 1.0, 2.0, 3.0))
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn abs(&self) -> OMatrix<T, R, C>
|
||||
where
|
||||
T: Signed,
|
||||
@ -49,6 +50,7 @@ macro_rules! component_binop_impl(
|
||||
($($binop: ident, $binop_mut: ident, $binop_assign: ident, $cmpy: ident, $Trait: ident . $op: ident . $op_assign: ident, $desc:expr, $desc_cmpy:expr, $desc_mut:expr);* $(;)*) => {$(
|
||||
#[doc = $desc]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn $binop<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> MatrixComponentOp<T, R1, C1, R2, C2>
|
||||
where T: $Trait,
|
||||
R2: Dim, C2: Dim,
|
||||
@ -251,6 +253,7 @@ impl<T: Scalar, R1: Dim, C1: Dim, SA: Storage<T, R1, C1>> Matrix<T, R1, C1, SA>
|
||||
/// assert_eq!(u.inf(&v), expected)
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inf(&self, other: &Self) -> OMatrix<T, R1, C1>
|
||||
where
|
||||
T: SimdPartialOrd,
|
||||
@ -271,6 +274,7 @@ impl<T: Scalar, R1: Dim, C1: Dim, SA: Storage<T, R1, C1>> Matrix<T, R1, C1, SA>
|
||||
/// assert_eq!(u.sup(&v), expected)
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sup(&self, other: &Self) -> OMatrix<T, R1, C1>
|
||||
where
|
||||
T: SimdPartialOrd,
|
||||
@ -291,6 +295,7 @@ impl<T: Scalar, R1: Dim, C1: Dim, SA: Storage<T, R1, C1>> Matrix<T, R1, C1, SA>
|
||||
/// assert_eq!(u.inf_sup(&v), expected)
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inf_sup(&self, other: &Self) -> (OMatrix<T, R1, C1>, OMatrix<T, R1, C1>)
|
||||
where
|
||||
T: SimdPartialOrd,
|
||||
|
@ -53,7 +53,10 @@ impl<T: Scalar, R: Dim, C: Dim> OMatrix<T, R, C>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, R, C>,
|
||||
{
|
||||
/// Creates a new uninitialized matrix. If the matrix has a compile-time dimension, this panics
|
||||
/// Creates a new uninitialized matrix.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the matrix has a compile-time dimension, this panics
|
||||
/// if `nrows != R::to_usize()` or `ncols != C::to_usize()`.
|
||||
#[inline]
|
||||
pub unsafe fn new_uninitialized_generic(nrows: R, ncols: C) -> mem::MaybeUninit<Self> {
|
||||
@ -827,7 +830,7 @@ where
|
||||
Standard: Distribution<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> OMatrix<T, R, C> {
|
||||
fn sample<G: Rng + ?Sized>(&self, rng: &mut G) -> OMatrix<T, R, C> {
|
||||
let nrows = R::try_to_usize().unwrap_or_else(|| rng.gen_range(0..10));
|
||||
let ncols = C::try_to_usize().unwrap_or_else(|| rng.gen_range(0..10));
|
||||
|
||||
@ -864,7 +867,7 @@ where
|
||||
{
|
||||
/// Generate a uniformly distributed random unit vector.
|
||||
#[inline]
|
||||
fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Unit<OVector<T, D>> {
|
||||
fn sample<G: Rng + ?Sized>(&self, rng: &mut G) -> Unit<OVector<T, D>> {
|
||||
Unit::new_normalize(OVector::from_distribution_generic(
|
||||
D::name(),
|
||||
Const::<1>,
|
||||
@ -906,6 +909,7 @@ macro_rules! componentwise_constructors_impl(
|
||||
impl<T> Matrix<T, Const<$R>, Const<$C>, ArrayStorage<T, $R, $C>> {
|
||||
/// Initializes this matrix from its components.
|
||||
#[inline]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub const fn new($($($args: T),*),*) -> Self {
|
||||
unsafe {
|
||||
Self::from_data_statically_unchecked(
|
||||
|
@ -10,6 +10,7 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim>
|
||||
{
|
||||
/// Creates, without bound-checking, a matrix slice from an array and with dimensions and strides specified by generic types instances.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method is unsafe because the input data array is not checked to contain enough elements.
|
||||
/// The generic types `R`, `C`, `RStride`, `CStride` can either be type-level integers or integers wrapped with `Dynamic::new()`.
|
||||
#[inline]
|
||||
@ -59,6 +60,7 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim>
|
||||
impl<'a, T: Scalar, R: Dim, C: Dim> MatrixSlice<'a, T, R, C> {
|
||||
/// Creates, without bound-checking, a matrix slice from an array and with dimensions specified by generic types instances.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method is unsafe because the input data array is not checked to contain enough elements.
|
||||
/// The generic types `R` and `C` can either be type-level integers or integers wrapped with `Dynamic::new()`.
|
||||
#[inline]
|
||||
@ -146,6 +148,7 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim>
|
||||
{
|
||||
/// Creates, without bound-checking, a mutable matrix slice from an array and with dimensions and strides specified by generic types instances.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method is unsafe because the input data array is not checked to contain enough elements.
|
||||
/// The generic types `R`, `C`, `RStride`, `CStride` can either be type-level integers or integers wrapped with `Dynamic::new()`.
|
||||
#[inline]
|
||||
@ -217,6 +220,7 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim>
|
||||
impl<'a, T: Scalar, R: Dim, C: Dim> MatrixSliceMutMN<'a, T, R, C> {
|
||||
/// Creates, without bound-checking, a mutable matrix slice from an array and with dimensions specified by generic types instances.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method is unsafe because the input data array is not checked to contain enough elements.
|
||||
/// The generic types `R` and `C` can either be type-level integers or integers wrapped with `Dynamic::new()`.
|
||||
#[inline]
|
||||
|
@ -1,8 +1,8 @@
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
use alloc::vec::Vec;
|
||||
use simba::scalar::{SubsetOf, SupersetOf};
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::convert::{AsMut, AsRef, From, Into};
|
||||
use std::mem;
|
||||
|
||||
use simba::simd::{PrimitiveSimdValue, SimdValue};
|
||||
|
||||
@ -110,11 +110,11 @@ impl<T: Scalar, const D: usize> From<[T; D]> for SVector<T, D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, const D: usize> Into<[T; D]> for SVector<T, D> {
|
||||
impl<T: Scalar, const D: usize> From<SVector<T, D>> for [T; D] {
|
||||
#[inline]
|
||||
fn into(self) -> [T; D] {
|
||||
fn from(vec: SVector<T, D>) -> Self {
|
||||
// TODO: unfortunately, we must clone because we can move out of an array.
|
||||
self.data.0[0].clone()
|
||||
vec.data.0[0].clone()
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,13 +128,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, const D: usize> Into<[T; D]> for RowSVector<T, D>
|
||||
impl<T: Scalar, const D: usize> From<RowSVector<T, D>> for [T; D]
|
||||
where
|
||||
Const<D>: IsNotStaticOne,
|
||||
{
|
||||
#[inline]
|
||||
fn into(self) -> [T; D] {
|
||||
self.transpose().into()
|
||||
fn from(vec: RowSVector<T, D>) -> [T; D] {
|
||||
vec.transpose().into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ macro_rules! impl_from_into_asref_1D(
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[T; $SZ] {
|
||||
unsafe {
|
||||
mem::transmute(self.data.ptr())
|
||||
&*(self.data.ptr() as *const [T; $SZ])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,7 +157,7 @@ macro_rules! impl_from_into_asref_1D(
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [T; $SZ] {
|
||||
unsafe {
|
||||
mem::transmute(self.data.ptr_mut())
|
||||
&mut *(self.data.ptr_mut() as *mut [T; $SZ])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -186,39 +186,54 @@ impl<T: Scalar, const R: usize, const C: usize> From<[[T; R]; C]> for SMatrix<T,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, const R: usize, const C: usize> Into<[[T; R]; C]> for SMatrix<T, R, C> {
|
||||
impl<T: Scalar, const R: usize, const C: usize> From<SMatrix<T, R, C>> for [[T; R]; C] {
|
||||
#[inline]
|
||||
fn into(self) -> [[T; R]; C] {
|
||||
self.data.0
|
||||
fn from(vec: SMatrix<T, R, C>) -> Self {
|
||||
vec.data.0
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from_into_asref_2D(
|
||||
($(($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr));* $(;)*) => {$(
|
||||
impl<T: Scalar, S> AsRef<[[T; $SZRows]; $SZCols]> for Matrix<T, $NRows, $NCols, S>
|
||||
macro_rules! impl_from_into_asref_borrow_2D(
|
||||
|
||||
//does the impls on one case for either AsRef/AsMut and Borrow/BorrowMut
|
||||
(
|
||||
($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr);
|
||||
$Ref:ident.$ref:ident(), $Mut:ident.$mut:ident()
|
||||
) => {
|
||||
impl<T: Scalar, S> $Ref<[[T; $SZRows]; $SZCols]> for Matrix<T, $NRows, $NCols, S>
|
||||
where S: ContiguousStorage<T, $NRows, $NCols> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[[T; $SZRows]; $SZCols] {
|
||||
fn $ref(&self) -> &[[T; $SZRows]; $SZCols] {
|
||||
unsafe {
|
||||
mem::transmute(self.data.ptr())
|
||||
&*(self.data.ptr() as *const [[T; $SZRows]; $SZCols])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, S> AsMut<[[T; $SZRows]; $SZCols]> for Matrix<T, $NRows, $NCols, S>
|
||||
impl<T: Scalar, S> $Mut<[[T; $SZRows]; $SZCols]> for Matrix<T, $NRows, $NCols, S>
|
||||
where S: ContiguousStorageMut<T, $NRows, $NCols> {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [[T; $SZRows]; $SZCols] {
|
||||
fn $mut(&mut self) -> &mut [[T; $SZRows]; $SZCols] {
|
||||
unsafe {
|
||||
mem::transmute(self.data.ptr_mut())
|
||||
&mut *(self.data.ptr_mut() as *mut [[T; $SZRows]; $SZCols])
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//collects the mappings from typenum pairs to consts
|
||||
($(($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr));* $(;)*) => {$(
|
||||
impl_from_into_asref_borrow_2D!(
|
||||
($NRows, $NCols) => ($SZRows, $SZCols); AsRef.as_ref(), AsMut.as_mut()
|
||||
);
|
||||
impl_from_into_asref_borrow_2D!(
|
||||
($NRows, $NCols) => ($SZRows, $SZCols); Borrow.borrow(), BorrowMut.borrow_mut()
|
||||
);
|
||||
)*}
|
||||
);
|
||||
|
||||
// Implement for matrices with shape 2x2 .. 6x6.
|
||||
impl_from_into_asref_2D!(
|
||||
impl_from_into_asref_borrow_2D!(
|
||||
(U2, U2) => (2, 2); (U2, U3) => (2, 3); (U2, U4) => (2, 4); (U2, U5) => (2, 5); (U2, U6) => (2, 6);
|
||||
(U3, U2) => (3, 2); (U3, U3) => (3, 3); (U3, U4) => (3, 4); (U3, U5) => (3, 5); (U3, U6) => (3, 6);
|
||||
(U4, U2) => (4, 2); (U4, U3) => (4, 3); (U4, U4) => (4, 4); (U4, U5) => (4, 5); (U4, U6) => (4, 6);
|
||||
@ -427,21 +442,21 @@ impl<'a, T: Scalar> From<Vec<T>> for DVector<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: ContiguousStorage<T, R, C>> Into<&'a [T]>
|
||||
for &'a Matrix<T, R, C, S>
|
||||
impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: ContiguousStorage<T, R, C>>
|
||||
From<&'a Matrix<T, R, C, S>> for &'a [T]
|
||||
{
|
||||
#[inline]
|
||||
fn into(self) -> &'a [T] {
|
||||
self.as_slice()
|
||||
fn from(matrix: &'a Matrix<T, R, C, S>) -> Self {
|
||||
matrix.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: ContiguousStorageMut<T, R, C>> Into<&'a mut [T]>
|
||||
for &'a mut Matrix<T, R, C, S>
|
||||
impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: ContiguousStorageMut<T, R, C>>
|
||||
From<&'a mut Matrix<T, R, C, S>> for &'a mut [T]
|
||||
{
|
||||
#[inline]
|
||||
fn into(self) -> &'a mut [T] {
|
||||
self.as_mut_slice()
|
||||
fn from(matrix: &'a mut Matrix<T, R, C, S>) -> Self {
|
||||
matrix.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,6 +467,12 @@ impl<'a, T: Scalar + Copy> From<&'a [T]> for DVectorSlice<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Scalar> From<DVectorSlice<'a, T>> for &'a [T] {
|
||||
fn from(vec: DVectorSlice<'a, T>) -> &'a [T] {
|
||||
vec.data.into_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Scalar + Copy> From<&'a mut [T]> for DVectorSliceMut<'a, T> {
|
||||
#[inline]
|
||||
fn from(slice: &'a mut [T]) -> Self {
|
||||
@ -459,6 +480,12 @@ impl<'a, T: Scalar + Copy> From<&'a mut [T]> for DVectorSliceMut<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Scalar> From<DVectorSliceMut<'a, T>> for &'a mut [T] {
|
||||
fn from(vec: DVectorSliceMut<'a, T>) -> &'a mut [T] {
|
||||
vec.data.into_slice_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + PrimitiveSimdValue, R: Dim, C: Dim> From<[OMatrix<T::Element, R, C>; 2]>
|
||||
for OMatrix<T, R, C>
|
||||
where
|
||||
|
@ -4,7 +4,6 @@
|
||||
//! components using their names. For example, if `v` is a 3D vector, one can write `v.z` instead
|
||||
//! of `v[2]`.
|
||||
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::base::dimension::{U1, U2, U3, U4, U5, U6};
|
||||
@ -38,7 +37,7 @@ macro_rules! deref_impl(
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { mem::transmute(self.data.ptr()) }
|
||||
unsafe { &*(self.data.ptr() as *const Self::Target) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +45,7 @@ macro_rules! deref_impl(
|
||||
where S: ContiguousStorageMut<T, $R, $C> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { mem::transmute(self.data.ptr_mut()) }
|
||||
unsafe { &mut *(self.data.ptr_mut() as *mut Self::Target) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use crate::base::array_storage::ArrayStorage;
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use crate::base::dimension::Dynamic;
|
||||
use crate::base::dimension::{Dim, DimName};
|
||||
use crate::base::storage::{Storage, StorageMut};
|
||||
use crate::base::storage::{ContiguousStorageMut, Storage, StorageMut};
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
use crate::base::vec_storage::VecStorage;
|
||||
use crate::base::Scalar;
|
||||
|
@ -20,7 +20,7 @@ pub struct Dynamic {
|
||||
impl Dynamic {
|
||||
/// A dynamic size equal to `value`.
|
||||
#[inline]
|
||||
pub fn new(value: usize) -> Self {
|
||||
pub const fn new(value: usize) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
@ -11,13 +11,14 @@ use crate::base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, Shap
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
use crate::base::dimension::Dynamic;
|
||||
use crate::base::dimension::{Const, Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimSub, DimSum, U1};
|
||||
use crate::base::storage::{ReshapableStorage, Storage, StorageMut};
|
||||
use crate::base::storage::{ContiguousStorageMut, ReshapableStorage, Storage, StorageMut};
|
||||
use crate::base::{DefaultAllocator, Matrix, OMatrix, RowVector, Scalar, Vector};
|
||||
|
||||
/// # Rows and columns extraction
|
||||
impl<T: Scalar + Zero, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Extracts the upper triangular part of this matrix (including the diagonal).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn upper_triangle(&self) -> OMatrix<T, R, C>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, R, C>,
|
||||
@ -30,6 +31,7 @@ impl<T: Scalar + Zero, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Extracts the lower triangular part of this matrix (including the diagonal).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lower_triangle(&self) -> OMatrix<T, R, C>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, R, C>,
|
||||
@ -42,6 +44,7 @@ impl<T: Scalar + Zero, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Creates a new matrix by extracting the given set of rows from `self`.
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
#[must_use]
|
||||
pub fn select_rows<'a, I>(&self, irows: I) -> OMatrix<T, Dynamic, C>
|
||||
where
|
||||
I: IntoIterator<Item = &'a usize>,
|
||||
@ -78,6 +81,7 @@ impl<T: Scalar + Zero, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Creates a new matrix by extracting the given set of columns from `self`.
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
#[must_use]
|
||||
pub fn select_columns<'a, I>(&self, icols: I) -> OMatrix<T, R, Dynamic>
|
||||
where
|
||||
I: IntoIterator<Item = &'a usize>,
|
||||
@ -583,6 +587,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Inserts `ninsert.value()` columns starting at the `i-th` place of this matrix.
|
||||
///
|
||||
/// # Safety
|
||||
/// The added column values are not initialized.
|
||||
#[inline]
|
||||
pub unsafe fn insert_columns_generic_uninitialized<D>(
|
||||
@ -664,6 +669,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Inserts `ninsert.value()` rows at the `i-th` place of this matrix.
|
||||
///
|
||||
/// # Safety
|
||||
/// The added rows values are not initialized.
|
||||
/// This is the generic implementation of `.insert_rows(...)` and
|
||||
/// `.insert_fixed_rows(...)` which have nicer API interfaces.
|
||||
|
@ -44,7 +44,7 @@ impl<D: Dim> DimRange<D> for usize {
|
||||
#[test]
|
||||
fn dimrange_usize() {
|
||||
assert_eq!(DimRange::contained_by(&0, Const::<0>), false);
|
||||
assert_eq!(DimRange::contained_by(&0, Const::<1>), true);
|
||||
assert!(DimRange::contained_by(&0, Const::<1>));
|
||||
}
|
||||
|
||||
impl<D: Dim> DimRange<D> for ops::Range<usize> {
|
||||
@ -68,24 +68,23 @@ impl<D: Dim> DimRange<D> for ops::Range<usize> {
|
||||
|
||||
#[test]
|
||||
fn dimrange_range_usize() {
|
||||
use std::usize::MAX;
|
||||
assert_eq!(DimRange::contained_by(&(0..0), Const::<0>), false);
|
||||
assert_eq!(DimRange::contained_by(&(0..1), Const::<0>), false);
|
||||
assert_eq!(DimRange::contained_by(&(0..1), Const::<1>), true);
|
||||
assert!(DimRange::contained_by(&(0..1), Const::<1>));
|
||||
assert!(DimRange::contained_by(
|
||||
&((usize::MAX - 1)..usize::MAX),
|
||||
Dynamic::new(usize::MAX)
|
||||
));
|
||||
assert_eq!(
|
||||
DimRange::contained_by(&((MAX - 1)..MAX), Dynamic::new(MAX)),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&((MAX - 1)..MAX), Dynamic::new(MAX)),
|
||||
DimRange::length(&((usize::MAX - 1)..usize::MAX), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(1)
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&(MAX..(MAX - 1)), Dynamic::new(MAX)),
|
||||
DimRange::length(&(usize::MAX..(usize::MAX - 1)), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(0)
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&(MAX..MAX), Dynamic::new(MAX)),
|
||||
DimRange::length(&(usize::MAX..usize::MAX), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(0)
|
||||
);
|
||||
}
|
||||
@ -111,20 +110,19 @@ impl<D: Dim> DimRange<D> for ops::RangeFrom<usize> {
|
||||
|
||||
#[test]
|
||||
fn dimrange_rangefrom_usize() {
|
||||
use std::usize::MAX;
|
||||
assert_eq!(DimRange::contained_by(&(0..), Const::<0>), false);
|
||||
assert_eq!(DimRange::contained_by(&(0..), Const::<0>), false);
|
||||
assert_eq!(DimRange::contained_by(&(0..), Const::<1>), true);
|
||||
assert!(DimRange::contained_by(&(0..), Const::<1>));
|
||||
assert!(DimRange::contained_by(
|
||||
&((usize::MAX - 1)..),
|
||||
Dynamic::new(usize::MAX)
|
||||
));
|
||||
assert_eq!(
|
||||
DimRange::contained_by(&((MAX - 1)..), Dynamic::new(MAX)),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&((MAX - 1)..), Dynamic::new(MAX)),
|
||||
DimRange::length(&((usize::MAX - 1)..), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(1)
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&(MAX..), Dynamic::new(MAX)),
|
||||
DimRange::length(&(usize::MAX..), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(0)
|
||||
);
|
||||
}
|
||||
@ -177,7 +175,7 @@ impl<D: Dim> DimRange<D> for ops::RangeFull {
|
||||
|
||||
#[test]
|
||||
fn dimrange_rangefull() {
|
||||
assert_eq!(DimRange::contained_by(&(..), Const::<0>), true);
|
||||
assert!(DimRange::contained_by(&(..), Const::<0>));
|
||||
assert_eq!(DimRange::length(&(..), Const::<1>), Const::<1>);
|
||||
}
|
||||
|
||||
@ -206,32 +204,31 @@ impl<D: Dim> DimRange<D> for ops::RangeInclusive<usize> {
|
||||
|
||||
#[test]
|
||||
fn dimrange_rangeinclusive_usize() {
|
||||
use std::usize::MAX;
|
||||
assert_eq!(DimRange::contained_by(&(0..=0), Const::<0>), false);
|
||||
assert_eq!(DimRange::contained_by(&(0..=0), Const::<1>), true);
|
||||
assert!(DimRange::contained_by(&(0..=0), Const::<1>));
|
||||
assert_eq!(
|
||||
DimRange::contained_by(&(MAX..=MAX), Dynamic::new(MAX)),
|
||||
DimRange::contained_by(&(usize::MAX..=usize::MAX), Dynamic::new(usize::MAX)),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::contained_by(&((MAX - 1)..=MAX), Dynamic::new(MAX)),
|
||||
DimRange::contained_by(&((usize::MAX - 1)..=usize::MAX), Dynamic::new(usize::MAX)),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::contained_by(&((MAX - 1)..=(MAX - 1)), Dynamic::new(MAX)),
|
||||
true
|
||||
);
|
||||
assert!(DimRange::contained_by(
|
||||
&((usize::MAX - 1)..=(usize::MAX - 1)),
|
||||
Dynamic::new(usize::MAX)
|
||||
));
|
||||
assert_eq!(DimRange::length(&(0..=0), Const::<1>), Dynamic::new(1));
|
||||
assert_eq!(
|
||||
DimRange::length(&((MAX - 1)..=MAX), Dynamic::new(MAX)),
|
||||
DimRange::length(&((usize::MAX - 1)..=usize::MAX), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(2)
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&(MAX..=(MAX - 1)), Dynamic::new(MAX)),
|
||||
DimRange::length(&(usize::MAX..=(usize::MAX - 1)), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(0)
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&(MAX..=MAX), Dynamic::new(MAX)),
|
||||
DimRange::length(&(usize::MAX..=usize::MAX), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(1)
|
||||
);
|
||||
}
|
||||
@ -257,21 +254,20 @@ impl<D: Dim> DimRange<D> for ops::RangeTo<usize> {
|
||||
|
||||
#[test]
|
||||
fn dimrange_rangeto_usize() {
|
||||
use std::usize::MAX;
|
||||
assert_eq!(DimRange::contained_by(&(..0), Const::<0>), true);
|
||||
assert!(DimRange::contained_by(&(..0), Const::<0>));
|
||||
assert_eq!(DimRange::contained_by(&(..1), Const::<0>), false);
|
||||
assert_eq!(DimRange::contained_by(&(..0), Const::<1>), true);
|
||||
assert!(DimRange::contained_by(&(..0), Const::<1>));
|
||||
assert!(DimRange::contained_by(
|
||||
&(..(usize::MAX - 1)),
|
||||
Dynamic::new(usize::MAX)
|
||||
));
|
||||
assert_eq!(
|
||||
DimRange::contained_by(&(..(MAX - 1)), Dynamic::new(MAX)),
|
||||
true
|
||||
DimRange::length(&(..(usize::MAX - 1)), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(usize::MAX - 1)
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&(..(MAX - 1)), Dynamic::new(MAX)),
|
||||
Dynamic::new(MAX - 1)
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&(..MAX), Dynamic::new(MAX)),
|
||||
Dynamic::new(MAX)
|
||||
DimRange::length(&(..usize::MAX), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(usize::MAX)
|
||||
);
|
||||
}
|
||||
|
||||
@ -296,21 +292,20 @@ impl<D: Dim> DimRange<D> for ops::RangeToInclusive<usize> {
|
||||
|
||||
#[test]
|
||||
fn dimrange_rangetoinclusive_usize() {
|
||||
use std::usize::MAX;
|
||||
assert_eq!(DimRange::contained_by(&(..=0), Const::<0>), false);
|
||||
assert_eq!(DimRange::contained_by(&(..=1), Const::<0>), false);
|
||||
assert_eq!(DimRange::contained_by(&(..=0), Const::<1>), true);
|
||||
assert!(DimRange::contained_by(&(..=0), Const::<1>));
|
||||
assert_eq!(
|
||||
DimRange::contained_by(&(..=(MAX)), Dynamic::new(MAX)),
|
||||
DimRange::contained_by(&(..=(usize::MAX)), Dynamic::new(usize::MAX)),
|
||||
false
|
||||
);
|
||||
assert!(DimRange::contained_by(
|
||||
&(..=(usize::MAX - 1)),
|
||||
Dynamic::new(usize::MAX)
|
||||
));
|
||||
assert_eq!(
|
||||
DimRange::contained_by(&(..=(MAX - 1)), Dynamic::new(MAX)),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
DimRange::length(&(..=(MAX - 1)), Dynamic::new(MAX)),
|
||||
Dynamic::new(MAX)
|
||||
DimRange::length(&(..=(usize::MAX - 1)), Dynamic::new(usize::MAX)),
|
||||
Dynamic::new(usize::MAX)
|
||||
);
|
||||
}
|
||||
|
||||
@ -485,6 +480,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Produces a view of the data at the given index, or
|
||||
/// `None` if the index is out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get<'a, I>(&'a self, index: I) -> Option<I::Output>
|
||||
where
|
||||
I: MatrixIndex<'a, T, R, C, S>,
|
||||
@ -495,6 +491,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Produces a mutable view of the data at the given index, or
|
||||
/// `None` if the index is out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_mut<'a, I>(&'a mut self, index: I) -> Option<I::OutputMut>
|
||||
where
|
||||
S: StorageMut<T, R, C>,
|
||||
@ -506,6 +503,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Produces a view of the data at the given index, or
|
||||
/// panics if the index is out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn index<'a, I>(&'a self, index: I) -> I::Output
|
||||
where
|
||||
I: MatrixIndex<'a, T, R, C, S>,
|
||||
@ -527,6 +525,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Produces a view of the data at the given index, without doing
|
||||
/// any bounds checking.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub unsafe fn get_unchecked<'a, I>(&'a self, index: I) -> I::Output
|
||||
where
|
||||
I: MatrixIndex<'a, T, R, C, S>,
|
||||
@ -537,6 +536,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Returns a mutable view of the data at the given index, without doing
|
||||
/// any bounds checking.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub unsafe fn get_unchecked_mut<'a, I>(&'a mut self, index: I) -> I::OutputMut
|
||||
where
|
||||
S: StorageMut<T, R, C>,
|
||||
|
@ -20,6 +20,7 @@ impl<T: Scalar + Zero + One + ClosedAdd + ClosedSub + ClosedMul, D: Dim, S: Stor
|
||||
/// let y = Vector3::new(10.0, 20.0, 30.0);
|
||||
/// assert_eq!(x.lerp(&y, 0.1), Vector3::new(1.9, 3.8, 5.7));
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn lerp<S2: Storage<T, D>>(&self, rhs: &Vector<T, D, S2>, t: T) -> OVector<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
@ -45,6 +46,7 @@ impl<T: Scalar + Zero + One + ClosedAdd + ClosedSub + ClosedMul, D: Dim, S: Stor
|
||||
///
|
||||
/// assert_eq!(v, v2.normalize());
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn slerp<S2: Storage<T, D>>(&self, rhs: &Vector<T, D, S2>, t: T) -> OVector<T, D>
|
||||
where
|
||||
T: RealField,
|
||||
@ -72,6 +74,7 @@ impl<T: RealField, D: Dim, S: Storage<T, D>> Unit<Vector<T, D, S>> {
|
||||
///
|
||||
/// assert_eq!(v, v2);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn slerp<S2: Storage<T, D>>(
|
||||
&self,
|
||||
rhs: &Unit<Vector<T, D, S2>>,
|
||||
@ -89,6 +92,7 @@ impl<T: RealField, D: Dim, S: Storage<T, D>> Unit<Vector<T, D, S>> {
|
||||
///
|
||||
/// Returns `None` if the two vectors are almost collinear and with opposite direction
|
||||
/// (in this case, there is an infinity of possible results).
|
||||
#[must_use]
|
||||
pub fn try_slerp<S2: Storage<T, D>>(
|
||||
&self,
|
||||
rhs: &Unit<Vector<T, D, S2>>,
|
||||
|
@ -96,6 +96,10 @@ macro_rules! iterator {
|
||||
let stride = self.strides.0.value();
|
||||
self.ptr = self.ptr.add(stride);
|
||||
}
|
||||
|
||||
// We want either `& *last` or `&mut *last` here, depending
|
||||
// on the mutability of `$Ref`.
|
||||
#[allow(clippy::transmute_ptr_to_ref)]
|
||||
Some(mem::transmute(old))
|
||||
}
|
||||
}
|
||||
@ -139,13 +143,13 @@ macro_rules! iterator {
|
||||
let inner_remaining = self.size % inner_size;
|
||||
|
||||
// Compute pointer to last element
|
||||
let last = self.ptr.offset(
|
||||
(outer_remaining * outer_stride + inner_remaining * inner_stride)
|
||||
as isize,
|
||||
);
|
||||
let last = self
|
||||
.ptr
|
||||
.add((outer_remaining * outer_stride + inner_remaining * inner_stride));
|
||||
|
||||
// We want either `& *last` or `&mut *last` here, depending
|
||||
// on the mutability of `$Ref`.
|
||||
#[allow(clippy::transmute_ptr_to_ref)]
|
||||
Some(mem::transmute(last))
|
||||
}
|
||||
}
|
||||
|
@ -336,7 +336,7 @@ mod rkyv_impl {
|
||||
for Matrix<T, R, C, S>
|
||||
{
|
||||
fn serialize(&self, serializer: &mut _S) -> Result<Self::Resolver, _S::Error> {
|
||||
Ok(self.data.serialize(serializer)?)
|
||||
self.data.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,6 +441,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// let mat = Matrix3x4::<f32>::zeros();
|
||||
/// assert_eq!(mat.shape(), (3, 4));
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn shape(&self) -> (usize, usize) {
|
||||
let (nrows, ncols) = self.data.shape();
|
||||
(nrows.value(), ncols.value())
|
||||
@ -455,6 +456,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// let mat = Matrix3x4::<f32>::zeros();
|
||||
/// assert_eq!(mat.nrows(), 3);
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nrows(&self) -> usize {
|
||||
self.shape().0
|
||||
}
|
||||
@ -468,6 +470,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// let mat = Matrix3x4::<f32>::zeros();
|
||||
/// assert_eq!(mat.ncols(), 4);
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ncols(&self) -> usize {
|
||||
self.shape().1
|
||||
}
|
||||
@ -483,6 +486,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// // The column strides is the number of steps (here 2) multiplied by the corresponding dimension.
|
||||
/// assert_eq!(mat.strides(), (1, 10));
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn strides(&self) -> (usize, usize) {
|
||||
let (srows, scols) = self.data.strides();
|
||||
(srows.value(), scols.value())
|
||||
@ -501,6 +505,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(m[i], m[3]);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn vector_to_matrix_index(&self, i: usize) -> (usize, usize) {
|
||||
let (nrows, ncols) = self.shape();
|
||||
|
||||
@ -529,6 +534,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(unsafe { *ptr }, m[0]);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_ptr(&self) -> *const T {
|
||||
self.data.ptr()
|
||||
}
|
||||
@ -537,6 +543,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
///
|
||||
/// See `relative_eq` from the `RelativeEq` trait for more details.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn relative_eq<R2, C2, SB>(
|
||||
&self,
|
||||
other: &Matrix<T, R2, C2, SB>,
|
||||
@ -559,6 +566,8 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Tests whether `self` and `rhs` are exactly equal.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn eq<R2, C2, SB>(&self, other: &Matrix<T, R2, C2, SB>) -> bool
|
||||
where
|
||||
T: PartialEq,
|
||||
@ -609,6 +618,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Clones this matrix to one that owns its data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn clone_owned(&self) -> OMatrix<T, R, C>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, R, C>,
|
||||
@ -619,6 +629,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Clones this matrix into one that owns its data. The actual type of the result depends on
|
||||
/// matrix storage combination rules for addition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn clone_owned_sum<R2, C2>(&self) -> MatrixSum<T, R, C, R2, C2>
|
||||
where
|
||||
R2: Dim,
|
||||
@ -692,6 +703,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Returns a matrix containing the result of `f` applied to each of its entries.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn map<T2: Scalar, F: FnMut(T) -> T2>(&self, mut f: F) -> OMatrix<T2, R, C>
|
||||
where
|
||||
DefaultAllocator: Allocator<T2, R, C>,
|
||||
@ -738,6 +750,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// - If the matrix has has least one component, then `init_f` is called with the first component
|
||||
/// to compute the initial value. Folding then continues on all the remaining components of the matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn fold_with<T2>(
|
||||
&self,
|
||||
init_f: impl FnOnce(Option<&T>) -> T2,
|
||||
@ -751,6 +764,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Returns a matrix containing the result of `f` applied to each of its entries. Unlike `map`,
|
||||
/// `f` also gets passed the row and column index, i.e. `f(row, col, value)`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn map_with_location<T2: Scalar, F: FnMut(usize, usize, T) -> T2>(
|
||||
&self,
|
||||
mut f: F,
|
||||
@ -778,6 +792,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Returns a matrix containing the result of `f` applied to each entries of `self` and
|
||||
/// `rhs`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn zip_map<T2, N3, S2, F>(&self, rhs: &Matrix<T2, R, C, S2>, mut f: F) -> OMatrix<N3, R, C>
|
||||
where
|
||||
T2: Scalar,
|
||||
@ -813,6 +828,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Returns a matrix containing the result of `f` applied to each entries of `self` and
|
||||
/// `b`, and `c`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn zip_zip_map<T2, N3, N4, S2, S3, F>(
|
||||
&self,
|
||||
b: &Matrix<T2, R, C, S2>,
|
||||
@ -860,6 +876,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Folds a function `f` on each entry of `self`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn fold<Acc>(&self, init: Acc, mut f: impl FnMut(Acc, T) -> Acc) -> Acc {
|
||||
let (nrows, ncols) = self.data.shape();
|
||||
|
||||
@ -879,6 +896,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Folds a function `f` on each pairs of entries from `self` and `rhs`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn zip_fold<T2, R2, C2, S2, Acc>(
|
||||
&self,
|
||||
rhs: &Matrix<T2, R2, C2, S2>,
|
||||
@ -1238,6 +1256,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: StorageMut<T, R, C>> Matrix<T, R, C, S> {
|
||||
impl<T: Scalar, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// Gets a reference to the i-th element of this column vector without bound checking.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub unsafe fn vget_unchecked(&self, i: usize) -> &T {
|
||||
debug_assert!(i < self.nrows(), "Vector index out of bounds.");
|
||||
let i = i * self.strides().0;
|
||||
@ -1248,6 +1267,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
|
||||
impl<T: Scalar, D: Dim, S: StorageMut<T, D>> Vector<T, D, S> {
|
||||
/// Gets a mutable reference to the i-th element of this column vector without bound checking.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub unsafe fn vget_unchecked_mut(&mut self, i: usize) -> &mut T {
|
||||
debug_assert!(i < self.nrows(), "Vector index out of bounds.");
|
||||
let i = i * self.strides().0;
|
||||
@ -1258,6 +1278,7 @@ impl<T: Scalar, D: Dim, S: StorageMut<T, D>> Vector<T, D, S> {
|
||||
impl<T: Scalar, R: Dim, C: Dim, S: ContiguousStorage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Extracts a slice containing the entire matrix entries ordered column-by-columns.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
self.data.as_slice()
|
||||
}
|
||||
@ -1266,6 +1287,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: ContiguousStorage<T, R, C>> Matrix<T, R, C, S
|
||||
impl<T: Scalar, R: Dim, C: Dim, S: ContiguousStorageMut<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Extracts a mutable slice containing the entire matrix entries ordered column-by-columns.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
self.data.as_mut_slice()
|
||||
}
|
||||
@ -1446,6 +1468,7 @@ impl<T: SimdComplexField, D: Dim, S: StorageMut<T, D, D>> Matrix<T, D, D, S> {
|
||||
impl<T: Scalar, D: Dim, S: Storage<T, D, D>> SquareMatrix<T, D, S> {
|
||||
/// The diagonal of this matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn diagonal(&self) -> OVector<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
@ -1457,6 +1480,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D, D>> SquareMatrix<T, D, S> {
|
||||
///
|
||||
/// This is a more efficient version of `self.diagonal().map(f)` since this
|
||||
/// allocates only once.
|
||||
#[must_use]
|
||||
pub fn map_diagonal<T2: Scalar>(&self, mut f: impl FnMut(T) -> T2) -> OVector<T2, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T2, D>,
|
||||
@ -1481,6 +1505,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D, D>> SquareMatrix<T, D, S> {
|
||||
|
||||
/// Computes a trace of a square matrix, i.e., the sum of its diagonal elements.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn trace(&self) -> T
|
||||
where
|
||||
T: Scalar + Zero + ClosedAdd,
|
||||
@ -1504,6 +1529,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D, D>> SquareMatrix<T, D, S> {
|
||||
impl<T: SimdComplexField, D: Dim, S: Storage<T, D, D>> SquareMatrix<T, D, S> {
|
||||
/// The symmetric part of `self`, i.e., `0.5 * (self + self.transpose())`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn symmetric_part(&self) -> OMatrix<T, D, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D, D>,
|
||||
@ -1520,6 +1546,7 @@ impl<T: SimdComplexField, D: Dim, S: Storage<T, D, D>> SquareMatrix<T, D, S> {
|
||||
|
||||
/// The hermitian part of `self`, i.e., `0.5 * (self + self.adjoint())`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn hermitian_part(&self) -> OMatrix<T, D, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D, D>,
|
||||
@ -1542,6 +1569,7 @@ impl<T: Scalar + Zero + One, D: DimAdd<U1> + IsNotStaticOne, S: Storage<T, D, D>
|
||||
/// Yields the homogeneous matrix for this matrix, i.e., appending an additional dimension and
|
||||
/// and setting the diagonal element to `1`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(&self) -> OMatrix<T, DimSum<D, U1>, DimSum<D, U1>>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, DimSum<D, U1>, DimSum<D, U1>>,
|
||||
@ -1553,7 +1581,7 @@ impl<T: Scalar + Zero + One, D: DimAdd<U1> + IsNotStaticOne, S: Storage<T, D, D>
|
||||
let dim = DimSum::<D, U1>::from_usize(self.nrows() + 1);
|
||||
let mut res = OMatrix::identity_generic(dim, dim);
|
||||
res.generic_slice_mut::<D, D>((0, 0), self.data.shape())
|
||||
.copy_from(&self);
|
||||
.copy_from(self);
|
||||
res
|
||||
}
|
||||
}
|
||||
@ -1562,6 +1590,7 @@ impl<T: Scalar + Zero, D: DimAdd<U1>, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// Computes the coordinates in projective space of this vector, i.e., appends a `0` to its
|
||||
/// coordinates.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(&self) -> OVector<T, DimSum<D, U1>>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, DimSum<D, U1>>,
|
||||
@ -1589,6 +1618,7 @@ impl<T: Scalar + Zero, D: DimAdd<U1>, S: Storage<T, D>> Vector<T, D, S> {
|
||||
impl<T: Scalar + Zero, D: DimAdd<U1>, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// Constructs a new vector of higher dimension by appending `element` to the end of `self`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn push(&self, element: T) -> OVector<T, DimSum<D, U1>>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, DimSum<D, U1>>,
|
||||
@ -1789,7 +1819,6 @@ macro_rules! impl_fmt {
|
||||
where
|
||||
T: Scalar + $trait,
|
||||
S: Storage<T, R, C>,
|
||||
DefaultAllocator: Allocator<usize, R, C>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
#[cfg(feature = "std")]
|
||||
@ -1807,20 +1836,17 @@ macro_rules! impl_fmt {
|
||||
4
|
||||
}
|
||||
|
||||
let (nrows, ncols) = self.data.shape();
|
||||
let (nrows, ncols) = self.shape();
|
||||
|
||||
if nrows.value() == 0 || ncols.value() == 0 {
|
||||
if nrows == 0 || ncols == 0 {
|
||||
return write!(f, "[ ]");
|
||||
}
|
||||
|
||||
let mut max_length = 0;
|
||||
let mut lengths: OMatrix<usize, R, C> = Matrix::zeros_generic(nrows, ncols);
|
||||
let (nrows, ncols) = self.shape();
|
||||
|
||||
for i in 0..nrows {
|
||||
for j in 0..ncols {
|
||||
lengths[(i, j)] = val_width(&self[(i, j)], f);
|
||||
max_length = crate::max(max_length, lengths[(i, j)]);
|
||||
max_length = crate::max(max_length, val_width(&self[(i, j)], f));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1837,7 +1863,7 @@ macro_rules! impl_fmt {
|
||||
for i in 0..nrows {
|
||||
write!(f, " │")?;
|
||||
for j in 0..ncols {
|
||||
let number_length = lengths[(i, j)] + 1;
|
||||
let number_length = val_width(&self[(i, j)], f) + 1;
|
||||
let pad = max_length_with_space - number_length;
|
||||
write!(f, " {:>thepad$}", "", thepad = pad)?;
|
||||
match f.precision() {
|
||||
@ -1870,19 +1896,29 @@ impl_fmt!(fmt::UpperHex, "{:X}", "{:1$X}");
|
||||
impl_fmt!(fmt::Binary, "{:b}", "{:.1$b}");
|
||||
impl_fmt!(fmt::Pointer, "{:p}", "{:.1$p}");
|
||||
|
||||
#[test]
|
||||
fn lower_exp() {
|
||||
let test = crate::Matrix2::new(1e6, 2e5, 2e-5, 1.);
|
||||
assert_eq!(
|
||||
format!("{:e}", test),
|
||||
r"
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn empty_display() {
|
||||
let vec: Vec<f64> = Vec::new();
|
||||
let dvector = crate::DVector::from_vec(vec);
|
||||
assert_eq!(format!("{}", dvector), "[ ]")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lower_exp() {
|
||||
let test = crate::Matrix2::new(1e6, 2e5, 2e-5, 1.);
|
||||
assert_eq!(
|
||||
format!("{:e}", test),
|
||||
r"
|
||||
┌ ┐
|
||||
│ 1e6 2e5 │
|
||||
│ 2e-5 1e0 │
|
||||
└ ┘
|
||||
|
||||
"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Cross product
|
||||
@ -1891,6 +1927,7 @@ impl<T: Scalar + ClosedAdd + ClosedSub + ClosedMul, R: Dim, C: Dim, S: Storage<T
|
||||
{
|
||||
/// The perpendicular product between two 2D column vectors, i.e. `a.x * b.y - a.y * b.x`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn perp<R2, C2, SB>(&self, b: &Matrix<T, R2, C2, SB>) -> T
|
||||
where
|
||||
R2: Dim,
|
||||
@ -1920,6 +1957,7 @@ impl<T: Scalar + ClosedAdd + ClosedSub + ClosedMul, R: Dim, C: Dim, S: Storage<T
|
||||
/// Panics if the shape is not 3D vector. In the future, this will be implemented only for
|
||||
/// dynamically-sized matrices and statically-sized 3D matrices.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cross<R2, C2, SB>(&self, b: &Matrix<T, R2, C2, SB>) -> MatrixCross<T, R, C, R2, C2>
|
||||
where
|
||||
R2: Dim,
|
||||
@ -1993,6 +2031,7 @@ impl<T: Scalar + ClosedAdd + ClosedSub + ClosedMul, R: Dim, C: Dim, S: Storage<T
|
||||
impl<T: Scalar + Field, S: Storage<T, U3>> Vector<T, U3, S> {
|
||||
/// Computes the matrix `M` such that for all vector `v` we have `M * v == self.cross(&v)`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cross_matrix(&self) -> OMatrix<T, U3, U3> {
|
||||
OMatrix::<T, U3, U3>::new(
|
||||
T::zero(),
|
||||
@ -2011,6 +2050,7 @@ impl<T: Scalar + Field, S: Storage<T, U3>> Vector<T, U3, S> {
|
||||
impl<T: SimdComplexField, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// The smallest angle between two vectors.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn angle<R2: Dim, C2: Dim, SB>(&self, other: &Matrix<T, R2, C2, SB>) -> T::SimdRealField
|
||||
where
|
||||
SB: Storage<T, R2, C2>,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Range, RangeFrom, RangeFull, RangeTo};
|
||||
use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo};
|
||||
use std::slice;
|
||||
|
||||
use crate::base::allocator::Allocator;
|
||||
@ -77,6 +77,23 @@ macro_rules! slice_storage_impl(
|
||||
$T::from_raw_parts(storage.$get_addr(start.0, start.1), shape, strides)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim>
|
||||
$T<'a, T, R, C, RStride, CStride>
|
||||
where
|
||||
Self: ContiguousStorage<T, R, C>
|
||||
{
|
||||
/// Extracts the original slice from this storage
|
||||
pub fn into_slice(self) -> &'a [T] {
|
||||
let (nrows, ncols) = self.shape();
|
||||
if nrows.value() != 0 && ncols.value() != 0 {
|
||||
let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1);
|
||||
unsafe { slice::from_raw_parts(self.ptr, sz + 1) }
|
||||
} else {
|
||||
unsafe { slice::from_raw_parts(self.ptr, 0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@ -108,6 +125,23 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Clone
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim>
|
||||
SliceStorageMut<'a, T, R, C, RStride, CStride>
|
||||
where
|
||||
Self: ContiguousStorageMut<T, R, C>,
|
||||
{
|
||||
/// Extracts the original slice from this storage
|
||||
pub fn into_slice_mut(self) -> &'a mut [T] {
|
||||
let (nrows, ncols) = self.shape();
|
||||
if nrows.value() != 0 && ncols.value() != 0 {
|
||||
let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1);
|
||||
unsafe { slice::from_raw_parts_mut(self.ptr, sz + 1) }
|
||||
} else {
|
||||
unsafe { slice::from_raw_parts_mut(self.ptr, 0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! storage_impl(
|
||||
($($T: ident),* $(,)*) => {$(
|
||||
unsafe impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Storage<T, R, C>
|
||||
@ -162,14 +196,14 @@ macro_rules! storage_impl(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[T] {
|
||||
unsafe fn as_slice_unchecked(&self) -> &[T] {
|
||||
let (nrows, ncols) = self.shape();
|
||||
if nrows.value() != 0 && ncols.value() != 0 {
|
||||
let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1);
|
||||
unsafe { slice::from_raw_parts(self.ptr, sz + 1) }
|
||||
slice::from_raw_parts(self.ptr, sz + 1)
|
||||
}
|
||||
else {
|
||||
unsafe { slice::from_raw_parts(self.ptr, 0) }
|
||||
slice::from_raw_parts(self.ptr, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,13 +221,13 @@ unsafe impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> StorageMu
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
|
||||
let (nrows, ncols) = self.shape();
|
||||
if nrows.value() != 0 && ncols.value() != 0 {
|
||||
let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1);
|
||||
unsafe { slice::from_raw_parts_mut(self.ptr, sz + 1) }
|
||||
slice::from_raw_parts_mut(self.ptr, sz + 1)
|
||||
} else {
|
||||
unsafe { slice::from_raw_parts_mut(self.ptr, 0) }
|
||||
slice::from_raw_parts_mut(self.ptr, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -806,12 +840,32 @@ impl<D: Dim> SliceRange<D> for RangeFull {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dim> SliceRange<D> for RangeInclusive<usize> {
|
||||
type Size = Dynamic;
|
||||
|
||||
#[inline(always)]
|
||||
fn begin(&self, _: D) -> usize {
|
||||
*self.start()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn end(&self, _: D) -> usize {
|
||||
*self.end() + 1
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn size(&self, _: D) -> Self::Size {
|
||||
Dynamic::new(*self.end() + 1 - *self.start())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: see how much of this overlaps with the general indexing
|
||||
// methods from indexing.rs.
|
||||
impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Slices a sub-matrix containing the rows indexed by the range `rows` and the columns indexed
|
||||
/// by the range `cols`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn slice_range<RowRange, ColRange>(
|
||||
&self,
|
||||
rows: RowRange,
|
||||
@ -830,6 +884,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Slice containing all the rows indexed by the range `rows`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn rows_range<RowRange: SliceRange<R>>(
|
||||
&self,
|
||||
rows: RowRange,
|
||||
@ -839,6 +894,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Slice containing all the columns indexed by the range `rows`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn columns_range<ColRange: SliceRange<C>>(
|
||||
&self,
|
||||
cols: ColRange,
|
||||
|
@ -13,6 +13,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(Vector3::new(-1.0, -2.0, -3.0).amax(), 3.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn amax(&self) -> T
|
||||
where
|
||||
T: Zero + SimdSigned + SimdPartialOrd,
|
||||
@ -33,6 +34,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Complex::new(1.0, 3.0)).camax(), 5.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn camax(&self) -> T::SimdRealField
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -52,6 +54,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(Vector3::new(5u32, 2, 3).max(), 5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn max(&self) -> T
|
||||
where
|
||||
T: SimdPartialOrd + Zero,
|
||||
@ -70,6 +73,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(Vector3::new(10.0, 2.0, 30.0).amin(), 2.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn amin(&self) -> T
|
||||
where
|
||||
T: Zero + SimdPartialOrd + SimdSigned,
|
||||
@ -90,6 +94,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Complex::new(1.0, 3.0)).camin(), 3.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn camin(&self) -> T::SimdRealField
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -112,6 +117,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(Vector3::new(5u32, 2, 3).min(), 2);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn min(&self) -> T
|
||||
where
|
||||
T: SimdPartialOrd + Zero,
|
||||
@ -136,6 +142,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(mat.icamax_full(), (1, 0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn icamax_full(&self) -> (usize, usize)
|
||||
where
|
||||
T: ComplexField,
|
||||
@ -172,6 +179,7 @@ impl<T: Scalar + PartialOrd + Signed, R: Dim, C: Dim, S: Storage<T, R, C>> Matri
|
||||
/// assert_eq!(mat.iamax_full(), (1, 2));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn iamax_full(&self) -> (usize, usize) {
|
||||
assert!(!self.is_empty(), "The input matrix must not be empty.");
|
||||
|
||||
@ -209,6 +217,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// assert_eq!(vec.icamax(), 2);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn icamax(&self) -> usize
|
||||
where
|
||||
T: ComplexField,
|
||||
@ -240,6 +249,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// assert_eq!(vec.argmax(), (2, 13));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn argmax(&self) -> (usize, T)
|
||||
where
|
||||
T: PartialOrd,
|
||||
@ -271,6 +281,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// assert_eq!(vec.imax(), 2);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn imax(&self) -> usize
|
||||
where
|
||||
T: PartialOrd,
|
||||
@ -288,6 +299,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// assert_eq!(vec.iamax(), 1);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn iamax(&self) -> usize
|
||||
where
|
||||
T: PartialOrd + Signed,
|
||||
@ -319,6 +331,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// assert_eq!(vec.argmin(), (1, -15));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn argmin(&self) -> (usize, T)
|
||||
where
|
||||
T: PartialOrd,
|
||||
@ -350,6 +363,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// assert_eq!(vec.imin(), 1);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn imin(&self) -> usize
|
||||
where
|
||||
T: PartialOrd,
|
||||
@ -367,6 +381,7 @@ impl<T: Scalar, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
|
||||
/// assert_eq!(vec.iamin(), 0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn iamin(&self) -> usize
|
||||
where
|
||||
T: PartialOrd + Signed,
|
||||
|
@ -158,6 +158,7 @@ impl<T: SimdComplexField> Norm<T> for UniformNorm {
|
||||
impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// The squared L2 norm of this vector.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn norm_squared(&self) -> T::SimdRealField
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -176,6 +177,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
///
|
||||
/// Use `.apply_norm` to apply a custom norm.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn norm(&self) -> T::SimdRealField
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -187,6 +189,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
///
|
||||
/// Use `.apply_metric_distance` to apply a custom norm.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn metric_distance<R2, C2, S2>(&self, rhs: &Matrix<T, R2, C2, S2>) -> T::SimdRealField
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -211,6 +214,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(v.apply_norm(&EuclideanNorm), v.norm());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn apply_norm(&self, norm: &impl Norm<T>) -> T::SimdRealField
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -233,6 +237,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(v1.apply_metric_distance(&v2, &EuclideanNorm), (v1 - v2).norm());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn apply_metric_distance<R2, C2, S2>(
|
||||
&self,
|
||||
rhs: &Matrix<T, R2, C2, S2>,
|
||||
@ -254,6 +259,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
///
|
||||
/// This function is simply implemented as a call to `norm()`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn magnitude(&self) -> T::SimdRealField
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -267,6 +273,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
///
|
||||
/// This function is simply implemented as a call to `norm_squared()`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn magnitude_squared(&self) -> T::SimdRealField
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -298,6 +305,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// The Lp norm of this matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lp_norm(&self, p: i32) -> T::SimdRealField
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -341,6 +349,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Returns a new vector with the same magnitude as `self` clamped between `0.0` and `max`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cap_magnitude(&self, max: T::RealField) -> OMatrix<T, R, C>
|
||||
where
|
||||
T: ComplexField,
|
||||
@ -357,6 +366,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Returns a new vector with the same magnitude as `self` clamped between `0.0` and `max`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn simd_cap_magnitude(&self, max: T::SimdRealField) -> OMatrix<T, R, C>
|
||||
where
|
||||
T: SimdComplexField,
|
||||
|
@ -158,20 +158,17 @@ macro_rules! componentwise_binop_impl(
|
||||
|
||||
// This is the most common case and should be deduced at compile-time.
|
||||
// TODO: use specialization instead?
|
||||
if self.data.is_contiguous() && rhs.data.is_contiguous() && out.data.is_contiguous() {
|
||||
let arr1 = self.data.as_slice();
|
||||
let arr2 = rhs.data.as_slice();
|
||||
let out = out.data.as_mut_slice();
|
||||
for i in 0 .. arr1.len() {
|
||||
unsafe {
|
||||
unsafe {
|
||||
if self.data.is_contiguous() && rhs.data.is_contiguous() && out.data.is_contiguous() {
|
||||
let arr1 = self.data.as_slice_unchecked();
|
||||
let arr2 = rhs.data.as_slice_unchecked();
|
||||
let out = out.data.as_mut_slice_unchecked();
|
||||
for i in 0 .. arr1.len() {
|
||||
*out.get_unchecked_mut(i) = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for j in 0 .. self.ncols() {
|
||||
for i in 0 .. self.nrows() {
|
||||
unsafe {
|
||||
} else {
|
||||
for j in 0 .. self.ncols() {
|
||||
for i in 0 .. self.nrows() {
|
||||
let val = self.get_unchecked((i, j)).inlined_clone().$method(rhs.get_unchecked((i, j)).inlined_clone());
|
||||
*out.get_unchecked_mut((i, j)) = val;
|
||||
}
|
||||
@ -191,19 +188,17 @@ macro_rules! componentwise_binop_impl(
|
||||
|
||||
// This is the most common case and should be deduced at compile-time.
|
||||
// TODO: use specialization instead?
|
||||
if self.data.is_contiguous() && rhs.data.is_contiguous() {
|
||||
let arr1 = self.data.as_mut_slice();
|
||||
let arr2 = rhs.data.as_slice();
|
||||
for i in 0 .. arr2.len() {
|
||||
unsafe {
|
||||
unsafe {
|
||||
if self.data.is_contiguous() && rhs.data.is_contiguous() {
|
||||
let arr1 = self.data.as_mut_slice_unchecked();
|
||||
let arr2 = rhs.data.as_slice_unchecked();
|
||||
|
||||
for i in 0 .. arr2.len() {
|
||||
arr1.get_unchecked_mut(i).$method_assign(arr2.get_unchecked(i).inlined_clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for j in 0 .. rhs.ncols() {
|
||||
for i in 0 .. rhs.nrows() {
|
||||
unsafe {
|
||||
} else {
|
||||
for j in 0 .. rhs.ncols() {
|
||||
for i in 0 .. rhs.nrows() {
|
||||
self.get_unchecked_mut((i, j)).$method_assign(rhs.get_unchecked((i, j)).inlined_clone())
|
||||
}
|
||||
}
|
||||
@ -221,20 +216,18 @@ macro_rules! componentwise_binop_impl(
|
||||
|
||||
// This is the most common case and should be deduced at compile-time.
|
||||
// TODO: use specialization instead?
|
||||
if self.data.is_contiguous() && rhs.data.is_contiguous() {
|
||||
let arr1 = self.data.as_slice();
|
||||
let arr2 = rhs.data.as_mut_slice();
|
||||
for i in 0 .. arr1.len() {
|
||||
unsafe {
|
||||
unsafe {
|
||||
if self.data.is_contiguous() && rhs.data.is_contiguous() {
|
||||
let arr1 = self.data.as_slice_unchecked();
|
||||
let arr2 = rhs.data.as_mut_slice_unchecked();
|
||||
|
||||
for i in 0 .. arr1.len() {
|
||||
let res = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone());
|
||||
*arr2.get_unchecked_mut(i) = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for j in 0 .. self.ncols() {
|
||||
for i in 0 .. self.nrows() {
|
||||
unsafe {
|
||||
} else {
|
||||
for j in 0 .. self.ncols() {
|
||||
for i in 0 .. self.nrows() {
|
||||
let r = rhs.get_unchecked_mut((i, j));
|
||||
*r = self.get_unchecked((i, j)).inlined_clone().$method(r.inlined_clone())
|
||||
}
|
||||
@ -536,7 +529,7 @@ macro_rules! left_scalar_mul_impl(
|
||||
|
||||
// for rhs in res.iter_mut() {
|
||||
for rhs in res.as_mut_slice().iter_mut() {
|
||||
*rhs = self * *rhs
|
||||
*rhs *= self
|
||||
}
|
||||
|
||||
res
|
||||
@ -676,6 +669,7 @@ where
|
||||
{
|
||||
/// Equivalent to `self.transpose() * rhs`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn tr_mul<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> OMatrix<T, C1, C2>
|
||||
where
|
||||
SB: Storage<T, R2, C2>,
|
||||
@ -692,6 +686,7 @@ where
|
||||
|
||||
/// Equivalent to `self.adjoint() * rhs`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ad_mul<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> OMatrix<T, C1, C2>
|
||||
where
|
||||
T: SimdComplexField,
|
||||
@ -801,6 +796,7 @@ where
|
||||
|
||||
/// The kronecker product of two matrices (aka. tensor product of the corresponding linear
|
||||
/// maps).
|
||||
#[must_use]
|
||||
pub fn kronecker<R2: Dim, C2: Dim, SB>(
|
||||
&self,
|
||||
rhs: &Matrix<T, R2, C2, SB>,
|
||||
|
@ -20,6 +20,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(mat.len(), 12);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
let (nrows, ncols) = self.shape();
|
||||
nrows * ncols
|
||||
@ -35,12 +36,14 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert!(!mat.is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Indicates if this is a square matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_square(&self) -> bool {
|
||||
let (nrows, ncols) = self.shape();
|
||||
nrows == ncols
|
||||
@ -52,6 +55,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// If the matrix is diagonal, this checks that diagonal elements (i.e. at coordinates `(i, i)`
|
||||
/// for i from `0` to `min(R, C)`) are equal one; and that all other elements are zero.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_identity(&self, eps: T::Epsilon) -> bool
|
||||
where
|
||||
T: Zero + One + RelativeEq,
|
||||
@ -112,6 +116,7 @@ impl<T: ComplexField, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// In this definition `Id` is approximately equal to the identity matrix with a relative error
|
||||
/// equal to `eps`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_orthogonal(&self, eps: T::Epsilon) -> bool
|
||||
where
|
||||
T: Zero + One + ClosedAdd + ClosedMul + RelativeEq,
|
||||
@ -129,6 +134,7 @@ where
|
||||
{
|
||||
/// Checks that this matrix is orthogonal and has a determinant equal to 1.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_special_orthogonal(&self, eps: T) -> bool
|
||||
where
|
||||
D: DimMin<D, Output = D>,
|
||||
@ -139,6 +145,7 @@ where
|
||||
|
||||
/// Returns `true` if this matrix is invertible.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_invertible(&self) -> bool {
|
||||
// TODO: improve this?
|
||||
self.clone_owned().try_inverse().is_some()
|
||||
|
@ -9,6 +9,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Returns a row vector where each element is the result of the application of `f` on the
|
||||
/// corresponding column of the original matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn compress_rows(
|
||||
&self,
|
||||
f: impl Fn(VectorSlice<T, R, S::RStride, S::CStride>) -> T,
|
||||
@ -35,6 +36,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
///
|
||||
/// This is the same as `self.compress_rows(f).transpose()`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn compress_rows_tr(
|
||||
&self,
|
||||
f: impl Fn(VectorSlice<T, R, S::RStride, S::CStride>) -> T,
|
||||
@ -58,6 +60,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
|
||||
/// Returns a column vector resulting from the folding of `f` on each column of this matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn compress_columns(
|
||||
&self,
|
||||
init: OVector<T, R>,
|
||||
@ -95,6 +98,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(m.sum(), 21.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sum(&self) -> T
|
||||
where
|
||||
T: ClosedAdd + Zero,
|
||||
@ -120,6 +124,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(mint.row_sum(), RowVector2::new(9,12));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row_sum(&self) -> RowOVector<T, C>
|
||||
where
|
||||
T: ClosedAdd + Zero,
|
||||
@ -144,6 +149,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(mint.row_sum_tr(), Vector2::new(9,12));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row_sum_tr(&self) -> OVector<T, C>
|
||||
where
|
||||
T: ClosedAdd + Zero,
|
||||
@ -168,6 +174,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(mint.column_sum(), Vector3::new(3,7,11));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn column_sum(&self) -> OVector<T, R>
|
||||
where
|
||||
T: ClosedAdd + Zero,
|
||||
@ -197,6 +204,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_relative_eq!(m.variance(), 35.0 / 12.0, epsilon = 1.0e-8);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn variance(&self) -> T
|
||||
where
|
||||
T: Field + SupersetOf<f64>,
|
||||
@ -226,6 +234,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(m.row_variance(), RowVector3::new(2.25, 2.25, 2.25));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row_variance(&self) -> RowOVector<T, C>
|
||||
where
|
||||
T: Field + SupersetOf<f64>,
|
||||
@ -246,6 +255,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(m.row_variance_tr(), Vector3::new(2.25, 2.25, 2.25));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row_variance_tr(&self) -> OVector<T, C>
|
||||
where
|
||||
T: Field + SupersetOf<f64>,
|
||||
@ -267,6 +277,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_relative_eq!(m.column_variance(), Vector2::new(2.0 / 3.0, 2.0 / 3.0), epsilon = 1.0e-8);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn column_variance(&self) -> OVector<T, R>
|
||||
where
|
||||
T: Field + SupersetOf<f64>,
|
||||
@ -306,6 +317,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(m.mean(), 3.5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn mean(&self) -> T
|
||||
where
|
||||
T: Field + SupersetOf<f64>,
|
||||
@ -331,6 +343,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(m.row_mean(), RowVector3::new(2.5, 3.5, 4.5));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row_mean(&self) -> RowOVector<T, C>
|
||||
where
|
||||
T: Field + SupersetOf<f64>,
|
||||
@ -351,6 +364,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(m.row_mean_tr(), Vector3::new(2.5, 3.5, 4.5));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn row_mean_tr(&self) -> OVector<T, C>
|
||||
where
|
||||
T: Field + SupersetOf<f64>,
|
||||
@ -371,6 +385,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// assert_eq!(m.column_mean(), Vector2::new(2.0, 5.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn column_mean(&self) -> OVector<T, R>
|
||||
where
|
||||
T: Field + SupersetOf<f64>,
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Abstract definition of a matrix data storage.
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
use crate::base::allocator::{Allocator, SameShapeC, SameShapeR};
|
||||
use crate::base::default_allocator::DefaultAllocator;
|
||||
@ -58,7 +58,7 @@ pub unsafe trait Storage<T: Scalar, R: Dim, C: Dim = U1>: Debug + Sized {
|
||||
/// Compute the index corresponding to the irow-th row and icol-th column of this matrix. The
|
||||
/// index must be such that the following holds:
|
||||
///
|
||||
/// ```.ignore
|
||||
/// ```ignore
|
||||
/// let lindex = self.linear_index(irow, icol);
|
||||
/// assert!(*self.get_unchecked(irow, icol) == *self.get_unchecked_linear(lindex))
|
||||
/// ```
|
||||
@ -70,36 +70,57 @@ pub unsafe trait Storage<T: Scalar, R: Dim, C: Dim = U1>: Debug + Sized {
|
||||
}
|
||||
|
||||
/// Gets the address of the i-th matrix component without performing bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the index is out of bounds, dereferencing the result will cause undefined behavior.
|
||||
#[inline]
|
||||
unsafe fn get_address_unchecked_linear(&self, i: usize) -> *const T {
|
||||
fn get_address_unchecked_linear(&self, i: usize) -> *const T {
|
||||
self.ptr().wrapping_add(i)
|
||||
}
|
||||
|
||||
/// Gets the address of the i-th matrix component without performing bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the index is out of bounds, dereferencing the result will cause undefined behavior.
|
||||
#[inline]
|
||||
unsafe fn get_address_unchecked(&self, irow: usize, icol: usize) -> *const T {
|
||||
fn get_address_unchecked(&self, irow: usize, icol: usize) -> *const T {
|
||||
self.get_address_unchecked_linear(self.linear_index(irow, icol))
|
||||
}
|
||||
|
||||
/// Retrieves a reference to the i-th element without bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the index is out of bounds, the method will cause undefined behavior.
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_linear(&self, i: usize) -> &T {
|
||||
&*self.get_address_unchecked_linear(i)
|
||||
}
|
||||
|
||||
/// Retrieves a reference to the i-th element without bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the index is out of bounds, the method will cause undefined behavior.
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(&self, irow: usize, icol: usize) -> &T {
|
||||
self.get_unchecked_linear(self.linear_index(irow, icol))
|
||||
}
|
||||
|
||||
/// Indicates whether this data buffer stores its elements contiguously.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function must not return `true` if the underlying storage is not contiguous,
|
||||
/// or undefined behaviour will occur.
|
||||
fn is_contiguous(&self) -> bool;
|
||||
|
||||
/// Retrieves the data buffer as a contiguous slice.
|
||||
///
|
||||
/// # Safety
|
||||
/// The matrix components may not be stored in a contiguous way, depending on the strides.
|
||||
fn as_slice(&self) -> &[T];
|
||||
/// This method is unsafe because this can yield to invalid aliasing when called on some pairs
|
||||
/// of matrix slices originating from the same matrix with strides.
|
||||
///
|
||||
/// Call the safe alternative `matrix.as_slice()` instead.
|
||||
unsafe fn as_slice_unchecked(&self) -> &[T];
|
||||
|
||||
/// Builds a matrix data storage that does not contain any reference.
|
||||
fn into_owned(self) -> Owned<T, R, C>
|
||||
@ -122,39 +143,57 @@ pub unsafe trait StorageMut<T: Scalar, R: Dim, C: Dim = U1>: Storage<T, R, C> {
|
||||
fn ptr_mut(&mut self) -> *mut T;
|
||||
|
||||
/// Gets the mutable address of the i-th matrix component without performing bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the index is out of bounds, dereferencing the result will cause undefined behavior.
|
||||
#[inline]
|
||||
unsafe fn get_address_unchecked_linear_mut(&mut self, i: usize) -> *mut T {
|
||||
fn get_address_unchecked_linear_mut(&mut self, i: usize) -> *mut T {
|
||||
self.ptr_mut().wrapping_add(i)
|
||||
}
|
||||
|
||||
/// Gets the mutable address of the i-th matrix component without performing bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the index is out of bounds, dereferencing the result will cause undefined behavior.
|
||||
#[inline]
|
||||
unsafe fn get_address_unchecked_mut(&mut self, irow: usize, icol: usize) -> *mut T {
|
||||
fn get_address_unchecked_mut(&mut self, irow: usize, icol: usize) -> *mut T {
|
||||
let lid = self.linear_index(irow, icol);
|
||||
self.get_address_unchecked_linear_mut(lid)
|
||||
}
|
||||
|
||||
/// Retrieves a mutable reference to the i-th element without bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the index is out of bounds, the method will cause undefined behavior.
|
||||
unsafe fn get_unchecked_linear_mut(&mut self, i: usize) -> &mut T {
|
||||
&mut *self.get_address_unchecked_linear_mut(i)
|
||||
}
|
||||
|
||||
/// Retrieves a mutable reference to the element at `(irow, icol)` without bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the index is out of bounds, the method will cause undefined behavior.
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(&mut self, irow: usize, icol: usize) -> &mut T {
|
||||
&mut *self.get_address_unchecked_mut(irow, icol)
|
||||
}
|
||||
|
||||
/// Swaps two elements using their linear index without bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the indices are out of bounds, the method will cause undefined behavior.
|
||||
#[inline]
|
||||
unsafe fn swap_unchecked_linear(&mut self, i1: usize, i2: usize) {
|
||||
let a = self.get_address_unchecked_linear_mut(i1);
|
||||
let b = self.get_address_unchecked_linear_mut(i2);
|
||||
|
||||
mem::swap(&mut *a, &mut *b);
|
||||
ptr::swap(a, b);
|
||||
}
|
||||
|
||||
/// Swaps two elements without bound-checking.
|
||||
///
|
||||
/// # Safety
|
||||
/// If the indices are out of bounds, the method will cause undefined behavior.
|
||||
#[inline]
|
||||
unsafe fn swap_unchecked(&mut self, row_col1: (usize, usize), row_col2: (usize, usize)) {
|
||||
let lid1 = self.linear_index(row_col1.0, row_col1.1);
|
||||
@ -165,8 +204,13 @@ pub unsafe trait StorageMut<T: Scalar, R: Dim, C: Dim = U1>: Storage<T, R, C> {
|
||||
|
||||
/// Retrieves the mutable data buffer as a contiguous slice.
|
||||
///
|
||||
/// Matrix components may not be contiguous, depending on its strides.
|
||||
fn as_mut_slice(&mut self) -> &mut [T];
|
||||
/// Matrix components may not be contiguous, depending on its strides.
|
||||
///
|
||||
/// # Safety
|
||||
/// The matrix components may not be stored in a contiguous way, depending on the strides.
|
||||
/// This method is unsafe because this can yield to invalid aliasing when called on some pairs
|
||||
/// of matrix slices originating from the same matrix with strides.
|
||||
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T];
|
||||
}
|
||||
|
||||
/// A matrix storage that is stored contiguously in memory.
|
||||
@ -177,6 +221,12 @@ pub unsafe trait StorageMut<T: Scalar, R: Dim, C: Dim = U1>: Storage<T, R, C> {
|
||||
pub unsafe trait ContiguousStorage<T: Scalar, R: Dim, C: Dim = U1>:
|
||||
Storage<T, R, C>
|
||||
{
|
||||
/// Converts this data storage to a contiguous slice.
|
||||
fn as_slice(&self) -> &[T] {
|
||||
// SAFETY: this is safe because this trait guarantees the fact
|
||||
// that the data is stored contiguously.
|
||||
unsafe { self.as_slice_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
/// A mutable matrix storage that is stored contiguously in memory.
|
||||
@ -187,6 +237,12 @@ pub unsafe trait ContiguousStorage<T: Scalar, R: Dim, C: Dim = U1>:
|
||||
pub unsafe trait ContiguousStorageMut<T: Scalar, R: Dim, C: Dim = U1>:
|
||||
ContiguousStorage<T, R, C> + StorageMut<T, R, C>
|
||||
{
|
||||
/// Converts this data storage to a contiguous mutable slice.
|
||||
fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
// SAFETY: this is safe because this trait guarantees the fact
|
||||
// that the data is stored contiguously.
|
||||
unsafe { self.as_mut_slice_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
/// A matrix storage that can be reshaped in-place.
|
||||
|
@ -8,6 +8,7 @@ macro_rules! impl_swizzle {
|
||||
$(
|
||||
/// Builds a new vector from components of `self`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn $name(&self) -> $Result<T>
|
||||
where D::Typenum: Cmp<typenum::$BaseDim, Output=Greater> {
|
||||
$Result::new($(self[$i].inlined_clone()),*)
|
||||
|
@ -1,6 +1,5 @@
|
||||
#[cfg(feature = "abomonation-serialize")]
|
||||
use std::io::{Result as IOResult, Write};
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[cfg(feature = "serde-serialize-no-std")]
|
||||
@ -96,7 +95,7 @@ mod rkyv_impl {
|
||||
|
||||
impl<T: Serialize<S>, S: Fallible + ?Sized> Serialize<S> for Unit<T> {
|
||||
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
|
||||
Ok(self.value.serialize(serializer)?)
|
||||
self.value.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,14 +221,14 @@ impl<T: Normed> Unit<T> {
|
||||
impl<T> Unit<T> {
|
||||
/// Wraps the given value, assuming it is already normalized.
|
||||
#[inline]
|
||||
pub fn new_unchecked(value: T) -> Self {
|
||||
pub const fn new_unchecked(value: T) -> Self {
|
||||
Unit { value }
|
||||
}
|
||||
|
||||
/// Wraps the given reference, assuming it is already normalized.
|
||||
#[inline]
|
||||
pub fn from_ref_unchecked<'a>(value: &'a T) -> &'a Self {
|
||||
unsafe { mem::transmute(value) }
|
||||
pub fn from_ref_unchecked(value: &T) -> &Self {
|
||||
unsafe { &*(value as *const T as *const Self) }
|
||||
}
|
||||
|
||||
/// Retrieves the underlying value.
|
||||
@ -332,7 +331,7 @@ impl<T> Deref for Unit<T> {
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { mem::transmute(self) }
|
||||
unsafe { &*(self as *const Self as *const T) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,12 +95,14 @@ impl<T, R: Dim, C: Dim> VecStorage<T, R, C> {
|
||||
|
||||
/// The underlying data storage.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_vec(&self) -> &Vec<T> {
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// The underlying mutable data storage.
|
||||
///
|
||||
/// # Safety
|
||||
/// This is unsafe because this may cause UB if the size of the vector is changed
|
||||
/// by the user.
|
||||
#[inline]
|
||||
@ -110,6 +112,7 @@ impl<T, R: Dim, C: Dim> VecStorage<T, R, C> {
|
||||
|
||||
/// Resizes the underlying mutable data storage and unwraps it.
|
||||
///
|
||||
/// # Safety
|
||||
/// If `sz` is larger than the current size, additional elements are uninitialized.
|
||||
/// If `sz` is smaller than the current size, additional elements are truncated.
|
||||
#[inline]
|
||||
@ -129,20 +132,22 @@ impl<T, R: Dim, C: Dim> VecStorage<T, R, C> {
|
||||
|
||||
/// The number of elements on the underlying vector.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
/// Returns true if the underlying vector contains no elements.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R: Dim, C: Dim> Into<Vec<T>> for VecStorage<T, R, C> {
|
||||
fn into(self) -> Vec<T> {
|
||||
self.data
|
||||
impl<T, R: Dim, C: Dim> From<VecStorage<T, R, C>> for Vec<T> {
|
||||
fn from(vec: VecStorage<T, R, C>) -> Self {
|
||||
vec.data
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +201,7 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[T] {
|
||||
unsafe fn as_slice_unchecked(&self) -> &[T] {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
@ -245,7 +250,7 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[T] {
|
||||
unsafe fn as_slice_unchecked(&self) -> &[T] {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
@ -265,7 +270,7 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
|
||||
&mut self.data[..]
|
||||
}
|
||||
}
|
||||
@ -326,7 +331,7 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
|
||||
&mut self.data[..]
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
// The macros break if the references are taken out, for some reason.
|
||||
#![allow(clippy::op_ref)]
|
||||
|
||||
use crate::{
|
||||
Isometry3, Matrix4, Normed, OVector, Point3, Quaternion, Scalar, SimdRealField, Translation3,
|
||||
Unit, UnitQuaternion, Vector3, Zero, U8,
|
||||
@ -35,14 +38,23 @@ use simba::scalar::{ClosedNeg, RealField};
|
||||
/// If a feature that you need is missing, feel free to open an issue or a PR.
|
||||
/// See https://github.com/dimforge/nalgebra/issues/487
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub struct DualQuaternion<T: Scalar> {
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct DualQuaternion<T> {
|
||||
/// The real component of the quaternion
|
||||
pub real: Quaternion<T>,
|
||||
/// The dual component of the quaternion
|
||||
pub dual: Quaternion<T>,
|
||||
}
|
||||
|
||||
impl<T: Scalar + Eq> Eq for DualQuaternion<T> {}
|
||||
|
||||
impl<T: Scalar> PartialEq for DualQuaternion<T> {
|
||||
#[inline]
|
||||
fn eq(&self, right: &Self) -> bool {
|
||||
self.real == right.real && self.dual == right.dual
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Zero> Default for DualQuaternion<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@ -232,6 +244,7 @@ where
|
||||
/// ));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lerp(&self, other: &Self, t: T) -> Self {
|
||||
self * (T::one() - t) + other * t
|
||||
}
|
||||
@ -271,8 +284,8 @@ where
|
||||
}
|
||||
|
||||
impl<T: RealField> DualQuaternion<T> {
|
||||
fn to_vector(&self) -> OVector<T, U8> {
|
||||
self.as_ref().clone().into()
|
||||
fn to_vector(self) -> OVector<T, U8> {
|
||||
(*self.as_ref()).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,6 +394,7 @@ where
|
||||
/// ));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn dual_quaternion(&self) -> &DualQuaternion<T> {
|
||||
self.as_ref()
|
||||
}
|
||||
@ -463,7 +477,6 @@ where
|
||||
/// assert_relative_eq!(inv * unit, UnitDualQuaternion::identity(), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use = "Did you mean to use inverse_mut()?"]
|
||||
pub fn inverse_mut(&mut self) {
|
||||
let quat = self.as_mut_unchecked();
|
||||
quat.real = Unit::new_unchecked(quat.real).inverse().into_inner();
|
||||
@ -486,6 +499,7 @@ where
|
||||
/// assert_relative_eq!(dq_to * dq1, dq2, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn isometry_to(&self, other: &Self) -> Self {
|
||||
other / self
|
||||
}
|
||||
@ -518,6 +532,7 @@ where
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lerp(&self, other: &Self, t: T) -> DualQuaternion<T> {
|
||||
self.as_ref().lerp(other.as_ref(), t)
|
||||
}
|
||||
@ -546,6 +561,7 @@ where
|
||||
/// ), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nlerp(&self, other: &Self, t: T) -> Self {
|
||||
let mut res = self.lerp(other, t);
|
||||
let _ = res.normalize_mut();
|
||||
@ -581,6 +597,7 @@ where
|
||||
/// );
|
||||
/// assert_relative_eq!(dq.translation().vector.y, 3.0, epsilon = 1.0e-6);
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sclerp(&self, other: &Self, t: T) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -600,6 +617,7 @@ where
|
||||
/// * `epsilon`: the value below which the sinus of the angle separating both quaternion
|
||||
/// must be to return `None`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn try_sclerp(&self, other: &Self, t: T, epsilon: T) -> Option<Self>
|
||||
where
|
||||
T: RealField,
|
||||
@ -612,9 +630,9 @@ where
|
||||
let other = {
|
||||
let dot_product = self.as_ref().real.coords.dot(&other.as_ref().real.coords);
|
||||
if dot_product < T::zero() {
|
||||
-other.clone()
|
||||
-*other
|
||||
} else {
|
||||
other.clone()
|
||||
*other
|
||||
}
|
||||
};
|
||||
|
||||
@ -667,6 +685,7 @@ where
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn rotation(&self) -> UnitQuaternion<T> {
|
||||
Unit::new_unchecked(self.as_ref().real)
|
||||
}
|
||||
@ -686,6 +705,7 @@ where
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn translation(&self) -> Translation3<T> {
|
||||
let two = T::one() + T::one();
|
||||
Translation3::from(
|
||||
@ -712,7 +732,8 @@ where
|
||||
/// assert_relative_eq!(iso.translation.vector, translation, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_isometry(&self) -> Isometry3<T> {
|
||||
#[must_use]
|
||||
pub fn to_isometry(self) -> Isometry3<T> {
|
||||
Isometry3::from_parts(self.translation(), self.rotation())
|
||||
}
|
||||
|
||||
@ -735,6 +756,7 @@ where
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, pt: &Point3<T>) -> Point3<T> {
|
||||
self * pt
|
||||
}
|
||||
@ -758,6 +780,7 @@ where
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, v: &Vector3<T>) -> Vector3<T> {
|
||||
self * v
|
||||
}
|
||||
@ -781,6 +804,7 @@ where
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_point(&self, pt: &Point3<T>) -> Point3<T> {
|
||||
self.inverse() * pt
|
||||
}
|
||||
@ -805,6 +829,7 @@ where
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_vector(&self, v: &Vector3<T>) -> Vector3<T> {
|
||||
self.inverse() * v
|
||||
}
|
||||
@ -830,6 +855,7 @@ where
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector3<T>>) -> Unit<Vector3<T>> {
|
||||
self.inverse() * v
|
||||
}
|
||||
@ -857,7 +883,8 @@ where
|
||||
/// assert_relative_eq!(dq.to_homogeneous(), expected, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_homogeneous(&self) -> Matrix4<T> {
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(self) -> Matrix4<T> {
|
||||
self.to_isometry().to_homogeneous()
|
||||
}
|
||||
}
|
||||
|
@ -186,9 +186,9 @@ where
|
||||
pub fn from_parts(translation: Translation3<T>, rotation: UnitQuaternion<T>) -> Self {
|
||||
let half: T = crate::convert(0.5f64);
|
||||
UnitDualQuaternion::new_unchecked(DualQuaternion {
|
||||
real: rotation.clone().into_inner(),
|
||||
real: rotation.into_inner(),
|
||||
dual: Quaternion::from_parts(T::zero(), translation.vector)
|
||||
* rotation.clone().into_inner()
|
||||
* rotation.into_inner()
|
||||
* half,
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
// The macros break if the references are taken out, for some reason.
|
||||
#![allow(clippy::op_ref)]
|
||||
|
||||
/*
|
||||
* This file provides:
|
||||
*
|
||||
@ -49,7 +52,6 @@ use crate::{
|
||||
DualQuaternion, Isometry3, Point, Point3, Quaternion, SimdRealField, Translation3, Unit,
|
||||
UnitDualQuaternion, UnitQuaternion, Vector, Vector3, U3,
|
||||
};
|
||||
use std::mem;
|
||||
use std::ops::{
|
||||
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
||||
};
|
||||
@ -57,14 +59,14 @@ use std::ops::{
|
||||
impl<T: SimdRealField> AsRef<[T; 8]> for DualQuaternion<T> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[T; 8] {
|
||||
unsafe { mem::transmute(self) }
|
||||
unsafe { &*(self as *const Self as *const [T; 8]) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SimdRealField> AsMut<[T; 8]> for DualQuaternion<T> {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [T; 8] {
|
||||
unsafe { mem::transmute(self) }
|
||||
unsafe { &mut *(self as *mut Self as *mut [T; 8]) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,7 +566,7 @@ dual_quaternion_op_impl!(
|
||||
(U4, U1), (U3, U1);
|
||||
self: &'a UnitDualQuaternion<T>, rhs: &'b Translation3<T>,
|
||||
Output = UnitDualQuaternion<T> => U3, U1;
|
||||
self * UnitDualQuaternion::<T>::from_parts(rhs.clone(), UnitQuaternion::identity());
|
||||
self * UnitDualQuaternion::<T>::from_parts(*rhs, UnitQuaternion::identity());
|
||||
'a, 'b);
|
||||
|
||||
dual_quaternion_op_impl!(
|
||||
@ -580,7 +582,7 @@ dual_quaternion_op_impl!(
|
||||
(U4, U1), (U3, U3);
|
||||
self: UnitDualQuaternion<T>, rhs: &'b Translation3<T>,
|
||||
Output = UnitDualQuaternion<T> => U3, U1;
|
||||
self * UnitDualQuaternion::<T>::from_parts(rhs.clone(), UnitQuaternion::identity());
|
||||
self * UnitDualQuaternion::<T>::from_parts(*rhs, UnitQuaternion::identity());
|
||||
'b);
|
||||
|
||||
dual_quaternion_op_impl!(
|
||||
@ -632,7 +634,7 @@ dual_quaternion_op_impl!(
|
||||
(U3, U1), (U4, U1);
|
||||
self: &'b Translation3<T>, rhs: &'a UnitDualQuaternion<T>,
|
||||
Output = UnitDualQuaternion<T> => U3, U1;
|
||||
UnitDualQuaternion::<T>::from_parts(self.clone(), UnitQuaternion::identity()) * rhs;
|
||||
UnitDualQuaternion::<T>::from_parts(*self, UnitQuaternion::identity()) * rhs;
|
||||
'a, 'b);
|
||||
|
||||
dual_quaternion_op_impl!(
|
||||
@ -640,7 +642,7 @@ dual_quaternion_op_impl!(
|
||||
(U3, U1), (U4, U1);
|
||||
self: &'a Translation3<T>, rhs: UnitDualQuaternion<T>,
|
||||
Output = UnitDualQuaternion<T> => U3, U1;
|
||||
UnitDualQuaternion::<T>::from_parts(self.clone(), UnitQuaternion::identity()) * rhs;
|
||||
UnitDualQuaternion::<T>::from_parts(*self, UnitQuaternion::identity()) * rhs;
|
||||
'a);
|
||||
|
||||
dual_quaternion_op_impl!(
|
||||
@ -664,7 +666,7 @@ dual_quaternion_op_impl!(
|
||||
(U3, U1), (U4, U1);
|
||||
self: &'b Translation3<T>, rhs: &'a UnitDualQuaternion<T>,
|
||||
Output = UnitDualQuaternion<T> => U3, U1;
|
||||
UnitDualQuaternion::<T>::from_parts(self.clone(), UnitQuaternion::identity()) / rhs;
|
||||
UnitDualQuaternion::<T>::from_parts(*self, UnitQuaternion::identity()) / rhs;
|
||||
'a, 'b);
|
||||
|
||||
dual_quaternion_op_impl!(
|
||||
@ -672,7 +674,7 @@ dual_quaternion_op_impl!(
|
||||
(U3, U1), (U4, U1);
|
||||
self: &'a Translation3<T>, rhs: UnitDualQuaternion<T>,
|
||||
Output = UnitDualQuaternion<T> => U3, U1;
|
||||
UnitDualQuaternion::<T>::from_parts(self.clone(), UnitQuaternion::identity()) / rhs;
|
||||
UnitDualQuaternion::<T>::from_parts(*self, UnitQuaternion::identity()) / rhs;
|
||||
'a);
|
||||
|
||||
dual_quaternion_op_impl!(
|
||||
@ -860,7 +862,7 @@ dual_quaternion_op_impl!(
|
||||
Output = Point3<T> => U3, U1;
|
||||
{
|
||||
let two: T = crate::convert(2.0f64);
|
||||
let q_point = Quaternion::from_parts(T::zero(), rhs.coords.clone());
|
||||
let q_point = Quaternion::from_parts(T::zero(), rhs.coords);
|
||||
Point::from(
|
||||
((self.as_ref().real * q_point + self.as_ref().dual * two) * self.as_ref().real.conjugate())
|
||||
.vector()
|
||||
@ -1115,7 +1117,7 @@ dual_quaternion_op_impl!(
|
||||
MulAssign, mul_assign;
|
||||
(U4, U1), (U4, U1);
|
||||
self: UnitDualQuaternion<T>, rhs: &'b UnitQuaternion<T>;
|
||||
*self *= rhs.clone(); 'b);
|
||||
*self *= *rhs; 'b);
|
||||
|
||||
// UnitDualQuaternion ÷= UnitQuaternion
|
||||
dual_quaternion_op_impl!(
|
||||
@ -1151,7 +1153,7 @@ dual_quaternion_op_impl!(
|
||||
MulAssign, mul_assign;
|
||||
(U4, U1), (U4, U1);
|
||||
self: UnitDualQuaternion<T>, rhs: &'b Translation3<T>;
|
||||
*self *= rhs.clone(); 'b);
|
||||
*self *= *rhs; 'b);
|
||||
|
||||
// UnitDualQuaternion ÷= Translation3
|
||||
dual_quaternion_op_impl!(
|
||||
|
@ -60,15 +60,17 @@ use crate::geometry::{AbstractRotation, Point, Translation};
|
||||
feature = "serde-serialize-no-std",
|
||||
serde(bound(serialize = "R: Serialize,
|
||||
DefaultAllocator: Allocator<T, Const<D>>,
|
||||
Owned<T, Const<D>>: Serialize"))
|
||||
Owned<T, Const<D>>: Serialize,
|
||||
T: Scalar"))
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde-serialize-no-std",
|
||||
serde(bound(deserialize = "R: Deserialize<'de>,
|
||||
DefaultAllocator: Allocator<T, Const<D>>,
|
||||
Owned<T, Const<D>>: Deserialize<'de>"))
|
||||
Owned<T, Const<D>>: Deserialize<'de>,
|
||||
T: Scalar"))
|
||||
)]
|
||||
pub struct Isometry<T: Scalar, R, const D: usize> {
|
||||
pub struct Isometry<T, R, const D: usize> {
|
||||
/// The pure rotational part of this isometry.
|
||||
pub rotation: R,
|
||||
/// The pure translational part of this isometry.
|
||||
@ -267,9 +269,10 @@ where
|
||||
/// assert_eq!(iso1.inverse() * iso2, iso1.inv_mul(&iso2));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inv_mul(&self, rhs: &Isometry<T, R, D>) -> Self {
|
||||
let inv_rot1 = self.rotation.inverse();
|
||||
let tr_12 = rhs.translation.vector.clone() - self.translation.vector.clone();
|
||||
let tr_12 = rhs.translation.vector - self.translation.vector;
|
||||
Isometry::from_parts(
|
||||
inv_rot1.transform_vector(&tr_12).into(),
|
||||
inv_rot1 * rhs.rotation.clone(),
|
||||
@ -384,6 +387,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, 2.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
self * pt
|
||||
}
|
||||
@ -407,6 +411,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
|
||||
self * v
|
||||
}
|
||||
@ -429,9 +434,10 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point3::new(0.0, 2.0, 1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
self.rotation
|
||||
.inverse_transform_point(&(pt - &self.translation.vector))
|
||||
.inverse_transform_point(&(pt - self.translation.vector))
|
||||
}
|
||||
|
||||
/// Transform the given vector by the inverse of this isometry, ignoring the
|
||||
@ -453,6 +459,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
|
||||
self.rotation.inverse_transform_vector(v)
|
||||
}
|
||||
@ -476,6 +483,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, -Vector3::y_axis(), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_unit_vector(&self, v: &Unit<SVector<T, D>>) -> Unit<SVector<T, D>> {
|
||||
self.rotation.inverse_transform_unit_vector(v)
|
||||
}
|
||||
@ -505,6 +513,7 @@ impl<T: SimdRealField, R, const D: usize> Isometry<T, R, D> {
|
||||
/// assert_relative_eq!(iso.to_homogeneous(), expected, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(&self) -> OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>
|
||||
where
|
||||
Const<D>: DimNameAdd<U1>,
|
||||
@ -536,6 +545,7 @@ impl<T: SimdRealField, R, const D: usize> Isometry<T, R, D> {
|
||||
/// assert_relative_eq!(iso.to_matrix(), expected, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_matrix(&self) -> OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>
|
||||
where
|
||||
Const<D>: DimNameAdd<U1>,
|
||||
|
@ -86,7 +86,7 @@ where
|
||||
Standard: Distribution<T> + Distribution<R>,
|
||||
{
|
||||
#[inline]
|
||||
fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Isometry<T, R, D> {
|
||||
fn sample<G: Rng + ?Sized>(&self, rng: &mut G) -> Isometry<T, R, D> {
|
||||
Isometry::from_parts(rng.gen(), rng.gen())
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn from(arr: [Isometry<T::Element, R::Element, D>; 2]) -> Self {
|
||||
let tra = Translation::from([arr[0].translation.clone(), arr[1].translation.clone()]);
|
||||
let tra = Translation::from([arr[0].translation, arr[1].translation]);
|
||||
let rot = R::from([arr[0].rotation, arr[0].rotation]);
|
||||
|
||||
Self::from_parts(tra, rot)
|
||||
@ -258,10 +258,10 @@ where
|
||||
#[inline]
|
||||
fn from(arr: [Isometry<T::Element, R::Element, D>; 4]) -> Self {
|
||||
let tra = Translation::from([
|
||||
arr[0].translation.clone(),
|
||||
arr[1].translation.clone(),
|
||||
arr[2].translation.clone(),
|
||||
arr[3].translation.clone(),
|
||||
arr[0].translation,
|
||||
arr[1].translation,
|
||||
arr[2].translation,
|
||||
arr[3].translation,
|
||||
]);
|
||||
let rot = R::from([
|
||||
arr[0].rotation,
|
||||
@ -286,14 +286,14 @@ where
|
||||
#[inline]
|
||||
fn from(arr: [Isometry<T::Element, R::Element, D>; 8]) -> Self {
|
||||
let tra = Translation::from([
|
||||
arr[0].translation.clone(),
|
||||
arr[1].translation.clone(),
|
||||
arr[2].translation.clone(),
|
||||
arr[3].translation.clone(),
|
||||
arr[4].translation.clone(),
|
||||
arr[5].translation.clone(),
|
||||
arr[6].translation.clone(),
|
||||
arr[7].translation.clone(),
|
||||
arr[0].translation,
|
||||
arr[1].translation,
|
||||
arr[2].translation,
|
||||
arr[3].translation,
|
||||
arr[4].translation,
|
||||
arr[5].translation,
|
||||
arr[6].translation,
|
||||
arr[7].translation,
|
||||
]);
|
||||
let rot = R::from([
|
||||
arr[0].rotation,
|
||||
@ -322,22 +322,22 @@ where
|
||||
#[inline]
|
||||
fn from(arr: [Isometry<T::Element, R::Element, D>; 16]) -> Self {
|
||||
let tra = Translation::from([
|
||||
arr[0].translation.clone(),
|
||||
arr[1].translation.clone(),
|
||||
arr[2].translation.clone(),
|
||||
arr[3].translation.clone(),
|
||||
arr[4].translation.clone(),
|
||||
arr[5].translation.clone(),
|
||||
arr[6].translation.clone(),
|
||||
arr[7].translation.clone(),
|
||||
arr[8].translation.clone(),
|
||||
arr[9].translation.clone(),
|
||||
arr[10].translation.clone(),
|
||||
arr[11].translation.clone(),
|
||||
arr[12].translation.clone(),
|
||||
arr[13].translation.clone(),
|
||||
arr[14].translation.clone(),
|
||||
arr[15].translation.clone(),
|
||||
arr[0].translation,
|
||||
arr[1].translation,
|
||||
arr[2].translation,
|
||||
arr[3].translation,
|
||||
arr[4].translation,
|
||||
arr[5].translation,
|
||||
arr[6].translation,
|
||||
arr[7].translation,
|
||||
arr[8].translation,
|
||||
arr[9].translation,
|
||||
arr[10].translation,
|
||||
arr[11].translation,
|
||||
arr[12].translation,
|
||||
arr[13].translation,
|
||||
arr[14].translation,
|
||||
arr[15].translation,
|
||||
]);
|
||||
let rot = R::from([
|
||||
arr[0].rotation,
|
||||
|
@ -26,6 +26,7 @@ impl<T: SimdRealField> Isometry3<T> {
|
||||
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lerp_slerp(&self, other: &Self, t: T) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -59,6 +60,7 @@ impl<T: SimdRealField> Isometry3<T> {
|
||||
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn try_lerp_slerp(&self, other: &Self, t: T, epsilon: T) -> Option<Self>
|
||||
where
|
||||
T: RealField,
|
||||
@ -94,6 +96,7 @@ impl<T: SimdRealField> IsometryMatrix3<T> {
|
||||
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lerp_slerp(&self, other: &Self, t: T) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -127,6 +130,7 @@ impl<T: SimdRealField> IsometryMatrix3<T> {
|
||||
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn try_lerp_slerp(&self, other: &Self, t: T, epsilon: T) -> Option<Self>
|
||||
where
|
||||
T: RealField,
|
||||
@ -163,6 +167,7 @@ impl<T: SimdRealField> Isometry2<T> {
|
||||
/// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lerp_slerp(&self, other: &Self, t: T) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -199,6 +204,7 @@ impl<T: SimdRealField> IsometryMatrix2<T> {
|
||||
/// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lerp_slerp(&self, other: &Self, t: T) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
|
@ -1,3 +1,6 @@
|
||||
// The macros break if the references are taken out, for some reason.
|
||||
#![allow(clippy::op_ref)]
|
||||
|
||||
use num::{One, Zero};
|
||||
use std::ops::{Div, DivAssign, Mul, MulAssign};
|
||||
|
||||
@ -198,7 +201,7 @@ md_assign_impl_all!(
|
||||
const D; for; where;
|
||||
self: Isometry<T, Rotation<T, D>, D>, rhs: Rotation<T, D>;
|
||||
[val] => self.rotation *= rhs;
|
||||
[ref] => self.rotation *= rhs.clone();
|
||||
[ref] => self.rotation *= *rhs;
|
||||
);
|
||||
|
||||
md_assign_impl_all!(
|
||||
@ -365,9 +368,9 @@ isometry_from_composition_impl_all!(
|
||||
D;
|
||||
self: Rotation<T, D>, right: Translation<T, D>, Output = Isometry<T, Rotation<T, D>, D>;
|
||||
[val val] => Isometry::from_parts(Translation::from(&self * right.vector), self);
|
||||
[ref val] => Isometry::from_parts(Translation::from(self * right.vector), self.clone());
|
||||
[ref val] => Isometry::from_parts(Translation::from(self * right.vector), *self);
|
||||
[val ref] => Isometry::from_parts(Translation::from(&self * &right.vector), self);
|
||||
[ref ref] => Isometry::from_parts(Translation::from(self * &right.vector), self.clone());
|
||||
[ref ref] => Isometry::from_parts(Translation::from(self * &right.vector), *self);
|
||||
);
|
||||
|
||||
// UnitQuaternion × Translation
|
||||
@ -389,9 +392,9 @@ isometry_from_composition_impl_all!(
|
||||
self: Isometry<T, Rotation<T, D>, D>, rhs: Rotation<T, D>,
|
||||
Output = Isometry<T, Rotation<T, D>, D>;
|
||||
[val val] => Isometry::from_parts(self.translation, self.rotation * rhs);
|
||||
[ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs); // TODO: do not clone.
|
||||
[val ref] => Isometry::from_parts(self.translation, self.rotation * rhs.clone());
|
||||
[ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs.clone());
|
||||
[ref val] => Isometry::from_parts(self.translation, self.rotation * rhs);
|
||||
[val ref] => Isometry::from_parts(self.translation, self.rotation * *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation, self.rotation * *rhs);
|
||||
);
|
||||
|
||||
// Rotation × Isometry
|
||||
@ -416,9 +419,9 @@ isometry_from_composition_impl_all!(
|
||||
self: Isometry<T, Rotation<T, D>, D>, rhs: Rotation<T, D>,
|
||||
Output = Isometry<T, Rotation<T, D>, D>;
|
||||
[val val] => Isometry::from_parts(self.translation, self.rotation / rhs);
|
||||
[ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs); // TODO: do not clone.
|
||||
[val ref] => Isometry::from_parts(self.translation, self.rotation / rhs.clone());
|
||||
[ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs.clone());
|
||||
[ref val] => Isometry::from_parts(self.translation, self.rotation / rhs);
|
||||
[val ref] => Isometry::from_parts(self.translation, self.rotation / *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation, self.rotation / *rhs);
|
||||
);
|
||||
|
||||
// Rotation ÷ Isometry
|
||||
@ -441,9 +444,9 @@ isometry_from_composition_impl_all!(
|
||||
self: Isometry<T, UnitQuaternion<T>, 3>, rhs: UnitQuaternion<T>,
|
||||
Output = Isometry<T, UnitQuaternion<T>, 3>;
|
||||
[val val] => Isometry::from_parts(self.translation, self.rotation * rhs);
|
||||
[ref val] => Isometry::from_parts(self.translation.clone(), self.rotation * rhs); // TODO: do not clone.
|
||||
[ref val] => Isometry::from_parts(self.translation, self.rotation * rhs);
|
||||
[val ref] => Isometry::from_parts(self.translation, self.rotation * *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation * *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation, self.rotation * *rhs);
|
||||
);
|
||||
|
||||
// UnitQuaternion × Isometry
|
||||
@ -468,9 +471,9 @@ isometry_from_composition_impl_all!(
|
||||
self: Isometry<T, UnitQuaternion<T>, 3>, rhs: UnitQuaternion<T>,
|
||||
Output = Isometry<T, UnitQuaternion<T>, 3>;
|
||||
[val val] => Isometry::from_parts(self.translation, self.rotation / rhs);
|
||||
[ref val] => Isometry::from_parts(self.translation.clone(), self.rotation / rhs); // TODO: do not clone.
|
||||
[ref val] => Isometry::from_parts(self.translation, self.rotation / rhs);
|
||||
[val ref] => Isometry::from_parts(self.translation, self.rotation / *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation / *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation, self.rotation / *rhs);
|
||||
);
|
||||
|
||||
// UnitQuaternion ÷ Isometry
|
||||
@ -492,9 +495,9 @@ isometry_from_composition_impl_all!(
|
||||
D;
|
||||
self: Translation<T, D>, right: Rotation<T, D>, Output = Isometry<T, Rotation<T, D>, D>;
|
||||
[val val] => Isometry::from_parts(self, right);
|
||||
[ref val] => Isometry::from_parts(self.clone(), right);
|
||||
[val ref] => Isometry::from_parts(self, right.clone());
|
||||
[ref ref] => Isometry::from_parts(self.clone(), right.clone());
|
||||
[ref val] => Isometry::from_parts(*self, right);
|
||||
[val ref] => Isometry::from_parts(self, *right);
|
||||
[ref ref] => Isometry::from_parts(*self, *right);
|
||||
);
|
||||
|
||||
// Translation × UnitQuaternion
|
||||
@ -503,9 +506,9 @@ isometry_from_composition_impl_all!(
|
||||
;
|
||||
self: Translation<T, 3>, right: UnitQuaternion<T>, Output = Isometry<T, UnitQuaternion<T>, 3>;
|
||||
[val val] => Isometry::from_parts(self, right);
|
||||
[ref val] => Isometry::from_parts(self.clone(), right);
|
||||
[ref val] => Isometry::from_parts(*self, right);
|
||||
[val ref] => Isometry::from_parts(self, *right);
|
||||
[ref ref] => Isometry::from_parts(self.clone(), *right);
|
||||
[ref ref] => Isometry::from_parts(*self, *right);
|
||||
);
|
||||
|
||||
// Isometry × UnitComplex
|
||||
@ -515,9 +518,9 @@ isometry_from_composition_impl_all!(
|
||||
self: Isometry<T, UnitComplex<T>, 2>, rhs: UnitComplex<T>,
|
||||
Output = Isometry<T, UnitComplex<T>, 2>;
|
||||
[val val] => Isometry::from_parts(self.translation, self.rotation * rhs);
|
||||
[ref val] => Isometry::from_parts(self.translation.clone(), self.rotation * rhs); // TODO: do not clone.
|
||||
[ref val] => Isometry::from_parts(self.translation, self.rotation * rhs);
|
||||
[val ref] => Isometry::from_parts(self.translation, self.rotation * *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation * *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation, self.rotation * *rhs);
|
||||
);
|
||||
|
||||
// Isometry ÷ UnitComplex
|
||||
@ -527,7 +530,7 @@ isometry_from_composition_impl_all!(
|
||||
self: Isometry<T, UnitComplex<T>, 2>, rhs: UnitComplex<T>,
|
||||
Output = Isometry<T, UnitComplex<T>, 2>;
|
||||
[val val] => Isometry::from_parts(self.translation, self.rotation / rhs);
|
||||
[ref val] => Isometry::from_parts(self.translation.clone(), self.rotation / rhs); // TODO: do not clone.
|
||||
[ref val] => Isometry::from_parts(self.translation, self.rotation / rhs);
|
||||
[val ref] => Isometry::from_parts(self.translation, self.rotation / *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation / *rhs);
|
||||
[ref ref] => Isometry::from_parts(self.translation, self.rotation / *rhs);
|
||||
);
|
||||
|
@ -8,7 +8,6 @@ use rand::{
|
||||
#[cfg(feature = "serde-serialize-no-std")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
use simba::scalar::RealField;
|
||||
|
||||
@ -19,7 +18,7 @@ use crate::base::{Matrix4, Vector, Vector3};
|
||||
use crate::geometry::{Point3, Projective3};
|
||||
|
||||
/// A 3D orthographic projection stored as a homogeneous 4x4 matrix.
|
||||
pub struct Orthographic3<T: RealField> {
|
||||
pub struct Orthographic3<T> {
|
||||
matrix: Matrix4<T>,
|
||||
}
|
||||
|
||||
@ -188,6 +187,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.as_matrix() * inv, Matrix4::identity());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse(&self) -> Matrix4<T> {
|
||||
let mut res = self.to_homogeneous();
|
||||
|
||||
@ -221,7 +221,8 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_eq!(proj.to_homogeneous(), expected);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_homogeneous(&self) -> Matrix4<T> {
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(self) -> Matrix4<T> {
|
||||
self.matrix
|
||||
}
|
||||
|
||||
@ -240,6 +241,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_eq!(*proj.as_matrix(), expected);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_matrix(&self) -> &Matrix4<T> {
|
||||
&self.matrix
|
||||
}
|
||||
@ -253,8 +255,9 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_eq!(proj.as_projective().to_homogeneous(), proj.to_homogeneous());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_projective(&self) -> &Projective3<T> {
|
||||
unsafe { mem::transmute(self) }
|
||||
unsafe { &*(self as *const Orthographic3<T> as *const Projective3<T>) }
|
||||
}
|
||||
|
||||
/// This transformation seen as a `Projective3`.
|
||||
@ -266,7 +269,8 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_eq!(proj.to_projective().to_homogeneous(), proj.to_homogeneous());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_projective(&self) -> Projective3<T> {
|
||||
#[must_use]
|
||||
pub fn to_projective(self) -> Projective3<T> {
|
||||
Projective3::from_matrix_unchecked(self.matrix)
|
||||
}
|
||||
|
||||
@ -310,6 +314,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.left(), 10.0, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn left(&self) -> T {
|
||||
(-T::one() - self.matrix[(0, 3)]) / self.matrix[(0, 0)]
|
||||
}
|
||||
@ -326,6 +331,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.right(), 1.0, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn right(&self) -> T {
|
||||
(T::one() - self.matrix[(0, 3)]) / self.matrix[(0, 0)]
|
||||
}
|
||||
@ -342,6 +348,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.bottom(), 20.0, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn bottom(&self) -> T {
|
||||
(-T::one() - self.matrix[(1, 3)]) / self.matrix[(1, 1)]
|
||||
}
|
||||
@ -358,6 +365,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.top(), 2.0, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn top(&self) -> T {
|
||||
(T::one() - self.matrix[(1, 3)]) / self.matrix[(1, 1)]
|
||||
}
|
||||
@ -374,6 +382,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.znear(), 1000.0, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn znear(&self) -> T {
|
||||
(T::one() + self.matrix[(2, 3)]) / self.matrix[(2, 2)]
|
||||
}
|
||||
@ -390,6 +399,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.zfar(), 0.1, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn zfar(&self) -> T {
|
||||
(-T::one() + self.matrix[(2, 3)]) / self.matrix[(2, 2)]
|
||||
}
|
||||
@ -422,6 +432,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.project_point(&p8), Point3::new( 1.0, 1.0, 1.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn project_point(&self, p: &Point3<T>) -> Point3<T> {
|
||||
Point3::new(
|
||||
self.matrix[(0, 0)] * p[0] + self.matrix[(0, 3)],
|
||||
@ -457,6 +468,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.unproject_point(&p8), Point3::new(10.0, 20.0, -1000.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn unproject_point(&self, p: &Point3<T>) -> Point3<T> {
|
||||
Point3::new(
|
||||
(p[0] - self.matrix[(0, 3)]) / self.matrix[(0, 0)],
|
||||
@ -485,6 +497,7 @@ impl<T: RealField> Orthographic3<T> {
|
||||
/// assert_relative_eq!(proj.project_vector(&v3), Vector3::z() * -2.0 / 999.9);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn project_vector<SB>(&self, p: &Vector<T, U3, SB>) -> Vector3<T>
|
||||
where
|
||||
SB: Storage<T, U3>,
|
||||
|
@ -9,18 +9,17 @@ use rand::{
|
||||
#[cfg(feature = "serde-serialize-no-std")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
use simba::scalar::RealField;
|
||||
|
||||
use crate::base::dimension::U3;
|
||||
use crate::base::storage::Storage;
|
||||
use crate::base::{Matrix4, Scalar, Vector, Vector3};
|
||||
use crate::base::{Matrix4, Vector, Vector3};
|
||||
|
||||
use crate::geometry::{Point3, Projective3};
|
||||
|
||||
/// A 3D perspective projection stored as a homogeneous 4x4 matrix.
|
||||
pub struct Perspective3<T: Scalar> {
|
||||
pub struct Perspective3<T> {
|
||||
matrix: Matrix4<T>,
|
||||
}
|
||||
|
||||
@ -104,6 +103,7 @@ impl<T: RealField> Perspective3<T> {
|
||||
|
||||
/// Retrieves the inverse of the underlying homogeneous matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse(&self) -> Matrix4<T> {
|
||||
let mut res = self.to_homogeneous();
|
||||
|
||||
@ -123,25 +123,29 @@ impl<T: RealField> Perspective3<T> {
|
||||
|
||||
/// Computes the corresponding homogeneous matrix.
|
||||
#[inline]
|
||||
pub fn to_homogeneous(&self) -> Matrix4<T> {
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(self) -> Matrix4<T> {
|
||||
self.matrix.clone_owned()
|
||||
}
|
||||
|
||||
/// A reference to the underlying homogeneous transformation matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_matrix(&self) -> &Matrix4<T> {
|
||||
&self.matrix
|
||||
}
|
||||
|
||||
/// A reference to this transformation seen as a `Projective3`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_projective(&self) -> &Projective3<T> {
|
||||
unsafe { mem::transmute(self) }
|
||||
unsafe { &*(self as *const Perspective3<T> as *const Projective3<T>) }
|
||||
}
|
||||
|
||||
/// This transformation seen as a `Projective3`.
|
||||
#[inline]
|
||||
pub fn to_projective(&self) -> Projective3<T> {
|
||||
#[must_use]
|
||||
pub fn to_projective(self) -> Projective3<T> {
|
||||
Projective3::from_matrix_unchecked(self.matrix)
|
||||
}
|
||||
|
||||
@ -161,18 +165,21 @@ impl<T: RealField> Perspective3<T> {
|
||||
|
||||
/// Gets the `width / height` aspect ratio of the view frustum.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn aspect(&self) -> T {
|
||||
self.matrix[(1, 1)] / self.matrix[(0, 0)]
|
||||
}
|
||||
|
||||
/// Gets the y field of view of the view frustum.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn fovy(&self) -> T {
|
||||
(T::one() / self.matrix[(1, 1)]).atan() * crate::convert(2.0)
|
||||
}
|
||||
|
||||
/// Gets the near plane offset of the view frustum.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn znear(&self) -> T {
|
||||
let ratio = (-self.matrix[(2, 2)] + T::one()) / (-self.matrix[(2, 2)] - T::one());
|
||||
|
||||
@ -182,6 +189,7 @@ impl<T: RealField> Perspective3<T> {
|
||||
|
||||
/// Gets the far plane offset of the view frustum.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn zfar(&self) -> T {
|
||||
let ratio = (-self.matrix[(2, 2)] + T::one()) / (-self.matrix[(2, 2)] - T::one());
|
||||
|
||||
@ -193,6 +201,7 @@ impl<T: RealField> Perspective3<T> {
|
||||
// TODO: when we get specialization, specialize the Mul impl instead.
|
||||
/// Projects a point. Faster than matrix multiplication.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn project_point(&self, p: &Point3<T>) -> Point3<T> {
|
||||
let inverse_denom = -T::one() / p[2];
|
||||
Point3::new(
|
||||
@ -204,6 +213,7 @@ impl<T: RealField> Perspective3<T> {
|
||||
|
||||
/// Un-projects a point. Faster than multiplication by the matrix inverse.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn unproject_point(&self, p: &Point3<T>) -> Point3<T> {
|
||||
let inverse_denom = self.matrix[(2, 3)] / (p[2] + self.matrix[(2, 2)]);
|
||||
|
||||
@ -217,6 +227,7 @@ impl<T: RealField> Perspective3<T> {
|
||||
// TODO: when we get specialization, specialize the Mul impl instead.
|
||||
/// Projects a vector. Faster than matrix multiplication.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn project_vector<SB>(&self, p: &Vector<T, U3, SB>) -> Vector3<T>
|
||||
where
|
||||
SB: Storage<T, U3>,
|
||||
@ -244,8 +255,9 @@ impl<T: RealField> Perspective3<T> {
|
||||
#[inline]
|
||||
pub fn set_fovy(&mut self, fovy: T) {
|
||||
let old_m22 = self.matrix[(1, 1)];
|
||||
self.matrix[(1, 1)] = T::one() / (fovy / crate::convert(2.0)).tan();
|
||||
self.matrix[(0, 0)] = self.matrix[(0, 0)] * (self.matrix[(1, 1)] / old_m22);
|
||||
let new_m22 = T::one() / (fovy / crate::convert(2.0)).tan();
|
||||
self.matrix[(1, 1)] = new_m22;
|
||||
self.matrix[(0, 0)] *= new_m22 / old_m22;
|
||||
}
|
||||
|
||||
/// Updates this perspective matrix with a new near plane offset of the view frustum.
|
||||
@ -276,7 +288,7 @@ where
|
||||
Standard: Distribution<T>,
|
||||
{
|
||||
/// Generate an arbitrary random variate for testing purposes.
|
||||
fn sample<'a, R: Rng + ?Sized>(&self, r: &'a mut R) -> Perspective3<T> {
|
||||
fn sample<R: Rng + ?Sized>(&self, r: &mut R) -> Perspective3<T> {
|
||||
use crate::base::helper;
|
||||
let znear = r.gen();
|
||||
let zfar = helper::reject_rand(r, |&x: &T| !(x - znear).is_zero());
|
||||
|
@ -17,7 +17,7 @@ use simba::simd::SimdPartialOrd;
|
||||
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, SVector, Scalar};
|
||||
use crate::base::{Const, DefaultAllocator, OVector, Scalar};
|
||||
|
||||
/// A point in an euclidean space.
|
||||
///
|
||||
@ -40,35 +40,53 @@ use crate::base::{Const, DefaultAllocator, OVector, SVector, Scalar};
|
||||
/// of said transformations for details.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Point<T, const D: usize> {
|
||||
pub struct OPoint<T: Scalar, D: DimName>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
/// The coordinates of this point, i.e., the shift from the origin.
|
||||
pub coords: SVector<T, D>,
|
||||
pub coords: OVector<T, D>,
|
||||
}
|
||||
|
||||
impl<T: Scalar + hash::Hash, const D: usize> hash::Hash for Point<T, D> {
|
||||
impl<T: Scalar + hash::Hash, D: DimName> hash::Hash for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.coords.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Copy, const D: usize> Copy for Point<T, D> {}
|
||||
|
||||
#[cfg(feature = "bytemuck")]
|
||||
unsafe impl<T: Scalar, const D: usize> bytemuck::Zeroable for Point<T, D> where
|
||||
SVector<T, D>: bytemuck::Zeroable
|
||||
impl<T: Scalar + Copy, D: DimName> Copy for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
OVector<T, D>: Copy,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(feature = "bytemuck")]
|
||||
unsafe impl<T: Scalar, const D: usize> bytemuck::Pod for Point<T, D>
|
||||
unsafe impl<T: Scalar, D: DimName> bytemuck::Zeroable for OPoint<T, D>
|
||||
where
|
||||
OVector<T, D>: bytemuck::Zeroable,
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(feature = "bytemuck")]
|
||||
unsafe impl<T: Scalar, D: DimName> bytemuck::Pod for OPoint<T, D>
|
||||
where
|
||||
T: Copy,
|
||||
SVector<T, D>: bytemuck::Pod,
|
||||
OVector<T, D>: bytemuck::Pod,
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-serialize-no-std")]
|
||||
impl<T: Scalar + Serialize, const D: usize> Serialize for Point<T, D> {
|
||||
impl<T: Scalar + Serialize, D: DimName> Serialize for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
<DefaultAllocator as Allocator<T, D>>::Buffer: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@ -78,22 +96,27 @@ impl<T: Scalar + Serialize, const D: usize> Serialize for Point<T, D> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-serialize-no-std")]
|
||||
impl<'a, T: Scalar + Deserialize<'a>, const D: usize> Deserialize<'a> for Point<T, D> {
|
||||
impl<'a, T: Scalar + Deserialize<'a>, D: DimName> Deserialize<'a> for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
<DefaultAllocator as Allocator<T, D>>::Buffer: Deserialize<'a>,
|
||||
{
|
||||
fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error>
|
||||
where
|
||||
Des: Deserializer<'a>,
|
||||
{
|
||||
let coords = SVector::<T, D>::deserialize(deserializer)?;
|
||||
let coords = OVector::<T, D>::deserialize(deserializer)?;
|
||||
|
||||
Ok(Self::from(coords))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "abomonation-serialize")]
|
||||
impl<T, const D: usize> Abomonation for Point<T, D>
|
||||
impl<T, D: DimName> Abomonation for OPoint<T, D>
|
||||
where
|
||||
T: Scalar,
|
||||
SVector<T, D>: Abomonation,
|
||||
OVector<T, D>: Abomonation,
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
unsafe fn entomb<W: Write>(&self, writer: &mut W) -> IOResult<()> {
|
||||
self.coords.entomb(writer)
|
||||
@ -108,7 +131,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
impl<T: Scalar, D: DimName> OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
/// Returns a point containing the result of `f` applied to each of its entries.
|
||||
///
|
||||
/// # Example
|
||||
@ -122,7 +148,11 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
/// assert_eq!(p.map(|e| e as u32), Point3::new(1, 2, 3));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn map<T2: Scalar, F: FnMut(T) -> T2>(&self, f: F) -> Point<T2, D> {
|
||||
#[must_use]
|
||||
pub fn map<T2: Scalar, F: FnMut(T) -> T2>(&self, f: F) -> OPoint<T2, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T2, D>,
|
||||
{
|
||||
self.coords.map(f).into()
|
||||
}
|
||||
|
||||
@ -161,20 +191,22 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
/// assert_eq!(p.to_homogeneous(), Vector4::new(10.0, 20.0, 30.0, 1.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_homogeneous(&self) -> OVector<T, DimNameSum<Const<D>, U1>>
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(&self) -> OVector<T, DimNameSum<D, U1>>
|
||||
where
|
||||
T: One,
|
||||
Const<D>: DimNameAdd<U1>,
|
||||
DefaultAllocator: Allocator<T, DimNameSum<Const<D>, U1>>,
|
||||
D: DimNameAdd<U1>,
|
||||
DefaultAllocator: Allocator<T, DimNameSum<D, U1>>,
|
||||
{
|
||||
let mut res = unsafe {
|
||||
crate::unimplemented_or_uninitialized_generic!(
|
||||
<DimNameSum<Const<D>, U1> as DimName>::name(),
|
||||
<DimNameSum<D, U1> as DimName>::name(),
|
||||
Const::<1>
|
||||
)
|
||||
};
|
||||
res.fixed_slice_mut::<D, 1>(0, 0).copy_from(&self.coords);
|
||||
res[(D, 0)] = T::one();
|
||||
res.generic_slice_mut((0, 0), (D::name(), Const::<1>))
|
||||
.copy_from(&self.coords);
|
||||
res[(D::dim(), 0)] = T::one();
|
||||
|
||||
res
|
||||
}
|
||||
@ -182,7 +214,7 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
/// Creates a new point with the given coordinates.
|
||||
#[deprecated(note = "Use Point::from(vector) instead.")]
|
||||
#[inline]
|
||||
pub fn from_coordinates(coords: SVector<T, D>) -> Self {
|
||||
pub fn from_coordinates(coords: OVector<T, D>) -> Self {
|
||||
Self { coords }
|
||||
}
|
||||
|
||||
@ -199,6 +231,7 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
/// assert_eq!(p.len(), 3);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
self.coords.len()
|
||||
}
|
||||
@ -212,6 +245,7 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
/// assert!(!p.is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
@ -239,13 +273,13 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
#[inline]
|
||||
pub fn iter(
|
||||
&self,
|
||||
) -> MatrixIter<T, Const<D>, Const<1>, <DefaultAllocator as Allocator<T, Const<D>>>::Buffer>
|
||||
{
|
||||
) -> MatrixIter<T, D, Const<1>, <DefaultAllocator as Allocator<T, D>>::Buffer> {
|
||||
self.coords.iter()
|
||||
}
|
||||
|
||||
/// Gets a reference to i-th element of this point without bound-checking.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub unsafe fn get_unchecked(&self, i: usize) -> &T {
|
||||
self.coords.vget_unchecked(i)
|
||||
}
|
||||
@ -265,13 +299,13 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
#[inline]
|
||||
pub fn iter_mut(
|
||||
&mut self,
|
||||
) -> MatrixIterMut<T, Const<D>, Const<1>, <DefaultAllocator as Allocator<T, Const<D>>>::Buffer>
|
||||
{
|
||||
) -> MatrixIterMut<T, D, Const<1>, <DefaultAllocator as Allocator<T, D>>::Buffer> {
|
||||
self.coords.iter_mut()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to i-th element of this point without bound-checking.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub unsafe fn get_unchecked_mut(&mut self, i: usize) -> &mut T {
|
||||
self.coords.vget_unchecked_mut(i)
|
||||
}
|
||||
@ -283,9 +317,10 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + AbsDiffEq, const D: usize> AbsDiffEq for Point<T, D>
|
||||
impl<T: Scalar + AbsDiffEq, D: DimName> AbsDiffEq for OPoint<T, D>
|
||||
where
|
||||
T::Epsilon: Copy,
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
type Epsilon = T::Epsilon;
|
||||
|
||||
@ -300,9 +335,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + RelativeEq, const D: usize> RelativeEq for Point<T, D>
|
||||
impl<T: Scalar + RelativeEq, D: DimName> RelativeEq for OPoint<T, D>
|
||||
where
|
||||
T::Epsilon: Copy,
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn default_max_relative() -> Self::Epsilon {
|
||||
@ -321,9 +357,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + UlpsEq, const D: usize> UlpsEq for Point<T, D>
|
||||
impl<T: Scalar + UlpsEq, D: DimName> UlpsEq for OPoint<T, D>
|
||||
where
|
||||
T::Epsilon: Copy,
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn default_max_ulps() -> u32 {
|
||||
@ -336,16 +373,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Eq, const D: usize> Eq for Point<T, D> {}
|
||||
impl<T: Scalar + Eq, D: DimName> Eq for OPoint<T, D> where DefaultAllocator: Allocator<T, D> {}
|
||||
|
||||
impl<T: Scalar, const D: usize> PartialEq for Point<T, D> {
|
||||
impl<T: Scalar, D: DimName> PartialEq for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, right: &Self) -> bool {
|
||||
self.coords == right.coords
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + PartialOrd, const D: usize> PartialOrd for Point<T, D> {
|
||||
impl<T: Scalar + PartialOrd, D: DimName> PartialOrd for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.coords.partial_cmp(&other.coords)
|
||||
@ -375,22 +418,28 @@ impl<T: Scalar + PartialOrd, const D: usize> PartialOrd for Point<T, D> {
|
||||
/*
|
||||
* inf/sup
|
||||
*/
|
||||
impl<T: Scalar + SimdPartialOrd, const D: usize> Point<T, D> {
|
||||
impl<T: Scalar + SimdPartialOrd, D: DimName> OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
/// Computes the infimum (aka. componentwise min) of two points.
|
||||
#[inline]
|
||||
pub fn inf(&self, other: &Self) -> Point<T, D> {
|
||||
#[must_use]
|
||||
pub fn inf(&self, other: &Self) -> OPoint<T, D> {
|
||||
self.coords.inf(&other.coords).into()
|
||||
}
|
||||
|
||||
/// Computes the supremum (aka. componentwise max) of two points.
|
||||
#[inline]
|
||||
pub fn sup(&self, other: &Self) -> Point<T, D> {
|
||||
#[must_use]
|
||||
pub fn sup(&self, other: &Self) -> OPoint<T, D> {
|
||||
self.coords.sup(&other.coords).into()
|
||||
}
|
||||
|
||||
/// Computes the (infimum, supremum) of two points.
|
||||
#[inline]
|
||||
pub fn inf_sup(&self, other: &Self) -> (Point<T, D>, Point<T, D>) {
|
||||
#[must_use]
|
||||
pub fn inf_sup(&self, other: &Self) -> (OPoint<T, D>, OPoint<T, D>) {
|
||||
let (inf, sup) = self.coords.inf_sup(&other.coords);
|
||||
(inf.into(), sup.into())
|
||||
}
|
||||
@ -401,7 +450,10 @@ impl<T: Scalar + SimdPartialOrd, const D: usize> Point<T, D> {
|
||||
* Display
|
||||
*
|
||||
*/
|
||||
impl<T: Scalar + fmt::Display, const D: usize> fmt::Display for Point<T, D> {
|
||||
impl<T: Scalar + fmt::Display, D: DimName> fmt::Display for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{{")?;
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
use crate::geometry::Point;
|
||||
use crate::geometry::OPoint;
|
||||
use crate::Const;
|
||||
|
||||
/// A point with `D` elements.
|
||||
pub type Point<T, const D: usize> = OPoint<T, Const<D>>;
|
||||
|
||||
/// A statically sized 1-dimensional column point.
|
||||
///
|
||||
|
@ -10,22 +10,26 @@ use rand::{
|
||||
|
||||
use crate::base::allocator::Allocator;
|
||||
use crate::base::dimension::{DimNameAdd, DimNameSum, U1};
|
||||
use crate::base::{DefaultAllocator, SVector, Scalar};
|
||||
use crate::base::{DefaultAllocator, Scalar};
|
||||
use crate::{
|
||||
Const, OVector, Point1, Point2, Point3, Point4, Point5, Point6, Vector1, Vector2, Vector3,
|
||||
Vector4, Vector5, Vector6,
|
||||
Const, DimName, OPoint, OVector, Point1, Point2, Point3, Point4, Point5, Point6, Vector1,
|
||||
Vector2, Vector3, Vector4, Vector5, Vector6,
|
||||
};
|
||||
use simba::scalar::{ClosedDiv, SupersetOf};
|
||||
|
||||
use crate::geometry::Point;
|
||||
|
||||
/// # Other construction methods
|
||||
impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
impl<T: Scalar, D: DimName> OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
/// Creates a new point with uninitialized coordinates.
|
||||
#[inline]
|
||||
pub unsafe fn new_uninitialized() -> Self {
|
||||
Self::from(crate::unimplemented_or_uninitialized_generic!(
|
||||
Const::<D>, Const::<1>
|
||||
D::name(),
|
||||
Const::<1>
|
||||
))
|
||||
}
|
||||
|
||||
@ -49,7 +53,7 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
where
|
||||
T: Zero,
|
||||
{
|
||||
Self::from(SVector::from_element(T::zero()))
|
||||
Self::from(OVector::from_element(T::zero()))
|
||||
}
|
||||
|
||||
/// Creates a new point from a slice.
|
||||
@ -68,7 +72,7 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_slice(components: &[T]) -> Self {
|
||||
Self::from(SVector::from_row_slice(components))
|
||||
Self::from(OVector::from_row_slice(components))
|
||||
}
|
||||
|
||||
/// Creates a new point from its homogeneous vector representation.
|
||||
@ -102,14 +106,15 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
/// assert_eq!(pt, Some(Point2::new(1.0, 2.0)));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_homogeneous(v: OVector<T, DimNameSum<Const<D>, U1>>) -> Option<Self>
|
||||
pub fn from_homogeneous(v: OVector<T, DimNameSum<D, U1>>) -> Option<Self>
|
||||
where
|
||||
T: Scalar + Zero + One + ClosedDiv,
|
||||
Const<D>: DimNameAdd<U1>,
|
||||
DefaultAllocator: Allocator<T, DimNameSum<Const<D>, U1>>,
|
||||
D: DimNameAdd<U1>,
|
||||
DefaultAllocator: Allocator<T, DimNameSum<D, U1>>,
|
||||
{
|
||||
if !v[D].is_zero() {
|
||||
let coords = v.fixed_slice::<D, 1>(0, 0) / v[D].inlined_clone();
|
||||
if !v[D::dim()].is_zero() {
|
||||
let coords =
|
||||
v.generic_slice((0, 0), (D::name(), Const::<1>)) / v[D::dim()].inlined_clone();
|
||||
Some(Self::from(coords))
|
||||
} else {
|
||||
None
|
||||
@ -125,9 +130,10 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
/// let pt2 = pt.cast::<f32>();
|
||||
/// assert_eq!(pt2, Point2::new(1.0f32, 2.0));
|
||||
/// ```
|
||||
pub fn cast<To: Scalar>(self) -> Point<To, D>
|
||||
pub fn cast<To: Scalar>(self) -> OPoint<To, D>
|
||||
where
|
||||
Point<To, D>: SupersetOf<Self>,
|
||||
OPoint<To, D>: SupersetOf<Self>,
|
||||
DefaultAllocator: Allocator<To, D>,
|
||||
{
|
||||
crate::convert(self)
|
||||
}
|
||||
@ -138,38 +144,43 @@ impl<T: Scalar, const D: usize> Point<T, D> {
|
||||
* Traits that build points.
|
||||
*
|
||||
*/
|
||||
impl<T: Scalar + Bounded, const D: usize> Bounded for Point<T, D> {
|
||||
impl<T: Scalar + Bounded, D: DimName> Bounded for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn max_value() -> Self {
|
||||
Self::from(SVector::max_value())
|
||||
Self::from(OVector::max_value())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min_value() -> Self {
|
||||
Self::from(SVector::min_value())
|
||||
Self::from(OVector::min_value())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand-no-std")]
|
||||
impl<T: Scalar, const D: usize> Distribution<Point<T, D>> for Standard
|
||||
impl<T: Scalar, D: DimName> Distribution<OPoint<T, D>> for Standard
|
||||
where
|
||||
Standard: Distribution<T>,
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
/// Generate a `Point` where each coordinate is an independent variate from `[0, 1)`.
|
||||
#[inline]
|
||||
fn sample<'a, G: Rng + ?Sized>(&self, rng: &mut G) -> Point<T, D> {
|
||||
Point::from(rng.gen::<SVector<T, D>>())
|
||||
fn sample<'a, G: Rng + ?Sized>(&self, rng: &mut G) -> OPoint<T, D> {
|
||||
OPoint::from(rng.gen::<OVector<T, D>>())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<T: Scalar + Arbitrary + Send, const D: usize> Arbitrary for Point<T, D>
|
||||
impl<T: Scalar + Arbitrary + Send, D: DimName> Arbitrary for OPoint<T, D>
|
||||
where
|
||||
<DefaultAllocator as Allocator<T, Const<D>>>::Buffer: Send,
|
||||
<DefaultAllocator as Allocator<T, D>>::Buffer: Send,
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn arbitrary(g: &mut Gen) -> Self {
|
||||
Self::from(SVector::arbitrary(g))
|
||||
Self::from(OVector::arbitrary(g))
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +192,7 @@ where
|
||||
// NOTE: the impl for Point1 is not with the others so that we
|
||||
// can add a section with the impl block comment.
|
||||
/// # Construction from individual components
|
||||
impl<T> Point1<T> {
|
||||
impl<T: Scalar> Point1<T> {
|
||||
/// Initializes this point from its components.
|
||||
///
|
||||
/// # Example
|
||||
@ -192,7 +203,7 @@ impl<T> Point1<T> {
|
||||
/// assert_eq!(p.x, 1.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn new(x: T) -> Self {
|
||||
pub fn new(x: T) -> Self {
|
||||
Point {
|
||||
coords: Vector1::new(x),
|
||||
}
|
||||
@ -200,13 +211,13 @@ impl<T> Point1<T> {
|
||||
}
|
||||
macro_rules! componentwise_constructors_impl(
|
||||
($($doc: expr; $Point: ident, $Vector: ident, $($args: ident:$irow: expr),*);* $(;)*) => {$(
|
||||
impl<T> $Point<T> {
|
||||
impl<T: Scalar> $Point<T> {
|
||||
#[doc = "Initializes this point from its components."]
|
||||
#[doc = "# Example\n```"]
|
||||
#[doc = $doc]
|
||||
#[doc = "```"]
|
||||
#[inline]
|
||||
pub const fn new($($args: T),*) -> Self {
|
||||
pub fn new($($args: T),*) -> Self {
|
||||
Point { coords: $Vector::new($($args),*) }
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use crate::base::dimension::{DimNameAdd, DimNameSum, U1};
|
||||
use crate::base::{Const, DefaultAllocator, Matrix, OVector, Scalar};
|
||||
|
||||
use crate::geometry::Point;
|
||||
use crate::{DimName, OPoint};
|
||||
|
||||
/*
|
||||
* This file provides the following conversions:
|
||||
@ -16,67 +17,69 @@ use crate::geometry::Point;
|
||||
* Point -> Vector (homogeneous)
|
||||
*/
|
||||
|
||||
impl<T1, T2, const D: usize> SubsetOf<Point<T2, D>> for Point<T1, D>
|
||||
impl<T1, T2, D: DimName> SubsetOf<OPoint<T2, D>> for OPoint<T1, D>
|
||||
where
|
||||
T1: Scalar,
|
||||
T2: Scalar + SupersetOf<T1>,
|
||||
DefaultAllocator: Allocator<T1, D> + Allocator<T2, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_superset(&self) -> Point<T2, D> {
|
||||
Point::from(self.coords.to_superset())
|
||||
fn to_superset(&self) -> OPoint<T2, D> {
|
||||
OPoint::from(self.coords.to_superset())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_in_subset(m: &Point<T2, D>) -> bool {
|
||||
fn is_in_subset(m: &OPoint<T2, D>) -> bool {
|
||||
// TODO: is there a way to reuse the `.is_in_subset` from the matrix implementation of
|
||||
// SubsetOf?
|
||||
m.iter().all(|e| e.is_in_subset())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_superset_unchecked(m: &Point<T2, D>) -> Self {
|
||||
fn from_superset_unchecked(m: &OPoint<T2, D>) -> Self {
|
||||
Self::from(Matrix::from_superset_unchecked(&m.coords))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1, T2, const D: usize> SubsetOf<OVector<T2, DimNameSum<Const<D>, U1>>> for Point<T1, D>
|
||||
impl<T1, T2, D> SubsetOf<OVector<T2, DimNameSum<D, U1>>> for OPoint<T1, D>
|
||||
where
|
||||
Const<D>: DimNameAdd<U1>,
|
||||
D: DimNameAdd<U1>,
|
||||
T1: Scalar,
|
||||
T2: Scalar + Zero + One + ClosedDiv + SupersetOf<T1>,
|
||||
DefaultAllocator:
|
||||
Allocator<T1, DimNameSum<Const<D>, U1>> + Allocator<T2, DimNameSum<Const<D>, U1>>,
|
||||
DefaultAllocator: Allocator<T1, D>
|
||||
+ Allocator<T2, D>
|
||||
+ Allocator<T1, DimNameSum<D, U1>>
|
||||
+ Allocator<T2, DimNameSum<D, U1>>,
|
||||
// + Allocator<T1, D>
|
||||
// + Allocator<T2, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_superset(&self) -> OVector<T2, DimNameSum<Const<D>, U1>> {
|
||||
let p: Point<T2, D> = self.to_superset();
|
||||
fn to_superset(&self) -> OVector<T2, DimNameSum<D, U1>> {
|
||||
let p: OPoint<T2, D> = self.to_superset();
|
||||
p.to_homogeneous()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_in_subset(v: &OVector<T2, DimNameSum<Const<D>, U1>>) -> bool {
|
||||
crate::is_convertible::<_, OVector<T1, DimNameSum<Const<D>, U1>>>(v) && !v[D].is_zero()
|
||||
fn is_in_subset(v: &OVector<T2, DimNameSum<D, U1>>) -> bool {
|
||||
crate::is_convertible::<_, OVector<T1, DimNameSum<D, U1>>>(v) && !v[D::dim()].is_zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_superset_unchecked(v: &OVector<T2, DimNameSum<Const<D>, U1>>) -> Self {
|
||||
let coords = v.fixed_slice::<D, 1>(0, 0) / v[D].inlined_clone();
|
||||
fn from_superset_unchecked(v: &OVector<T2, DimNameSum<D, U1>>) -> Self {
|
||||
let coords = v.generic_slice((0, 0), (D::name(), Const::<1>)) / v[D::dim()].inlined_clone();
|
||||
Self {
|
||||
coords: crate::convert_unchecked(coords),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Zero + One, const D: usize> From<Point<T, D>>
|
||||
for OVector<T, DimNameSum<Const<D>, U1>>
|
||||
impl<T: Scalar + Zero + One, D: DimName> From<OPoint<T, D>> for OVector<T, DimNameSum<D, U1>>
|
||||
where
|
||||
Const<D>: DimNameAdd<U1>,
|
||||
DefaultAllocator: Allocator<T, DimNameSum<Const<D>, U1>>,
|
||||
D: DimNameAdd<U1>,
|
||||
DefaultAllocator: Allocator<T, DimNameSum<D, U1>> + Allocator<T, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn from(t: Point<T, D>) -> Self {
|
||||
fn from(t: OPoint<T, D>) -> Self {
|
||||
t.to_homogeneous()
|
||||
}
|
||||
}
|
||||
@ -90,17 +93,20 @@ impl<T: Scalar, const D: usize> From<[T; D]> for Point<T, D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, const D: usize> Into<[T; D]> for Point<T, D> {
|
||||
impl<T: Scalar, const D: usize> From<Point<T, D>> for [T; D] {
|
||||
#[inline]
|
||||
fn into(self) -> [T; D] {
|
||||
self.coords.into()
|
||||
fn from(p: Point<T, D>) -> Self {
|
||||
p.coords.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, const D: usize> From<OVector<T, Const<D>>> for Point<T, D> {
|
||||
impl<T: Scalar, D: DimName> From<OVector<T, D>> for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn from(coords: OVector<T, Const<D>>) -> Self {
|
||||
Point { coords }
|
||||
fn from(coords: OVector<T, D>) -> Self {
|
||||
OPoint { coords }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB};
|
||||
use crate::base::Scalar;
|
||||
use crate::base::{Scalar, U1, U2, U3, U4, U5, U6};
|
||||
|
||||
use crate::geometry::Point;
|
||||
use crate::geometry::OPoint;
|
||||
|
||||
/*
|
||||
*
|
||||
@ -12,8 +12,8 @@ use crate::geometry::Point;
|
||||
*/
|
||||
|
||||
macro_rules! deref_impl(
|
||||
($D: expr, $Target: ident $(, $comps: ident)*) => {
|
||||
impl<T: Scalar> Deref for Point<T, $D>
|
||||
($D: ty, $Target: ident $(, $comps: ident)*) => {
|
||||
impl<T: Scalar> Deref for OPoint<T, $D>
|
||||
{
|
||||
type Target = $Target<T>;
|
||||
|
||||
@ -23,7 +23,7 @@ macro_rules! deref_impl(
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar> DerefMut for Point<T, $D>
|
||||
impl<T: Scalar> DerefMut for OPoint<T, $D>
|
||||
{
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
@ -33,9 +33,9 @@ macro_rules! deref_impl(
|
||||
}
|
||||
);
|
||||
|
||||
deref_impl!(1, X, x);
|
||||
deref_impl!(2, XY, x, y);
|
||||
deref_impl!(3, XYZ, x, y, z);
|
||||
deref_impl!(4, XYZW, x, y, z, w);
|
||||
deref_impl!(5, XYZWA, x, y, z, w, a);
|
||||
deref_impl!(6, XYZWAB, x, y, z, w, a, b);
|
||||
deref_impl!(U1, X, x);
|
||||
deref_impl!(U2, XY, x, y);
|
||||
deref_impl!(U3, XYZ, x, y, z);
|
||||
deref_impl!(U4, XYZW, x, y, z, w);
|
||||
deref_impl!(U5, XYZWA, x, y, z, w, a);
|
||||
deref_impl!(U6, XYZWAB, x, y, z, w, a, b);
|
||||
|
@ -8,18 +8,23 @@ use simba::scalar::{ClosedAdd, ClosedDiv, ClosedMul, ClosedNeg, ClosedSub};
|
||||
use crate::base::constraint::{
|
||||
AreMultipliable, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint,
|
||||
};
|
||||
use crate::base::dimension::{Dim, U1};
|
||||
use crate::base::dimension::{Dim, DimName, U1};
|
||||
use crate::base::storage::Storage;
|
||||
use crate::base::{Const, Matrix, SVector, Scalar, Vector};
|
||||
use crate::base::{Const, Matrix, OVector, Scalar, Vector};
|
||||
|
||||
use crate::geometry::Point;
|
||||
use crate::allocator::Allocator;
|
||||
use crate::geometry::{OPoint, Point};
|
||||
use crate::DefaultAllocator;
|
||||
|
||||
/*
|
||||
*
|
||||
* Indexing.
|
||||
*
|
||||
*/
|
||||
impl<T: Scalar, const D: usize> Index<usize> for Point<T, D> {
|
||||
impl<T: Scalar, D: DimName> Index<usize> for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
type Output = T;
|
||||
|
||||
#[inline]
|
||||
@ -28,7 +33,10 @@ impl<T: Scalar, const D: usize> Index<usize> for Point<T, D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, const D: usize> IndexMut<usize> for Point<T, D> {
|
||||
impl<T: Scalar, D: DimName> IndexMut<usize> for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
#[inline]
|
||||
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
|
||||
&mut self.coords[i]
|
||||
@ -40,7 +48,10 @@ impl<T: Scalar, const D: usize> IndexMut<usize> for Point<T, D> {
|
||||
* Neg.
|
||||
*
|
||||
*/
|
||||
impl<T: Scalar + ClosedNeg, const D: usize> Neg for Point<T, D> {
|
||||
impl<T: Scalar + ClosedNeg, D: DimName> Neg for OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
@ -49,8 +60,11 @@ impl<T: Scalar + ClosedNeg, const D: usize> Neg for Point<T, D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Scalar + ClosedNeg, const D: usize> Neg for &'a Point<T, D> {
|
||||
type Output = Point<T, D>;
|
||||
impl<'a, T: Scalar + ClosedNeg, D: DimName> Neg for &'a OPoint<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D>,
|
||||
{
|
||||
type Output = OPoint<T, D>;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
@ -66,102 +80,103 @@ impl<'a, T: Scalar + ClosedNeg, const D: usize> Neg for &'a Point<T, D> {
|
||||
|
||||
// Point - Point
|
||||
add_sub_impl!(Sub, sub, ClosedSub;
|
||||
(Const<D>, U1), (Const<D>, U1) -> (Const<D>, U1)
|
||||
const D; for; where;
|
||||
self: &'a Point<T, D>, right: &'b Point<T, D>, Output = SVector<T, D>;
|
||||
(D, U1), (D, U1) -> (D, U1)
|
||||
const; for D; where D: DimName, DefaultAllocator: Allocator<T, D>;
|
||||
self: &'a OPoint<T, D>, right: &'b OPoint<T, D>, Output = OVector<T, D>;
|
||||
&self.coords - &right.coords; 'a, 'b);
|
||||
|
||||
add_sub_impl!(Sub, sub, ClosedSub;
|
||||
(Const<D>, U1), (Const<D>, U1) -> (Const<D>, U1)
|
||||
const D; for; where;
|
||||
self: &'a Point<T, D>, right: Point<T, D>, Output = SVector<T, D>;
|
||||
(D, U1), (D, U1) -> (D, U1)
|
||||
const; for D; where D: DimName, DefaultAllocator: Allocator<T, D>;
|
||||
self: &'a OPoint<T, D>, right: OPoint<T, D>, Output = OVector<T, D>;
|
||||
&self.coords - right.coords; 'a);
|
||||
|
||||
add_sub_impl!(Sub, sub, ClosedSub;
|
||||
(Const<D>, U1), (Const<D>, U1) -> (Const<D>, U1)
|
||||
const D; for; where;
|
||||
self: Point<T, D>, right: &'b Point<T, D>, Output = SVector<T, D>;
|
||||
(D, U1), (D, U1) -> (D, U1)
|
||||
const; for D; where D: DimName, DefaultAllocator: Allocator<T, D>;
|
||||
self: OPoint<T, D>, right: &'b OPoint<T, D>, Output = OVector<T, D>;
|
||||
self.coords - &right.coords; 'b);
|
||||
|
||||
add_sub_impl!(Sub, sub, ClosedSub;
|
||||
(Const<D>, U1), (Const<D>, U1) -> (Const<D>, U1)
|
||||
const D; for; where;
|
||||
self: Point<T, D>, right: Point<T, D>, Output = SVector<T, D>;
|
||||
(D, U1), (D, U1) -> (D, U1)
|
||||
const; for D; where D: DimName, DefaultAllocator: Allocator<T, D>;
|
||||
self: OPoint<T, D>, right: OPoint<T, D>, Output = OVector<T, D>;
|
||||
self.coords - right.coords; );
|
||||
|
||||
// Point - Vector
|
||||
add_sub_impl!(Sub, sub, ClosedSub;
|
||||
(Const<D1>, U1), (D2, U1) -> (Const<D1>, U1)
|
||||
const D1;
|
||||
for D2, SB;
|
||||
where D2: Dim, SB: Storage<T, D2>;
|
||||
self: &'a Point<T, D1>, right: &'b Vector<T, D2, SB>, Output = Point<T, D1>;
|
||||
(D1, U1), (D2, U1) -> (D1, U1)
|
||||
const;
|
||||
for D1, D2, SB;
|
||||
where D1: DimName, D2: Dim, SB: Storage<T, D2>, DefaultAllocator: Allocator<T, D1>;
|
||||
self: &'a OPoint<T, D1>, right: &'b Vector<T, D2, SB>, Output = OPoint<T, D1>;
|
||||
Self::Output::from(&self.coords - right); 'a, 'b);
|
||||
|
||||
add_sub_impl!(Sub, sub, ClosedSub;
|
||||
(Const<D1>, U1), (D2, U1) -> (Const<D1>, U1)
|
||||
const D1;
|
||||
for D2, SB;
|
||||
where D2: Dim, SB: Storage<T, D2>;
|
||||
self: &'a Point<T, D1>, right: Vector<T, D2, SB>, Output = Point<T, D1>;
|
||||
(D1, U1), (D2, U1) -> (D1, U1)
|
||||
const;
|
||||
for D1, D2, SB;
|
||||
where D1: DimName, D2: Dim, SB: Storage<T, D2>, DefaultAllocator: Allocator<T, D1>;
|
||||
self: &'a OPoint<T, D1>, right: Vector<T, D2, SB>, Output = OPoint<T, D1>;
|
||||
Self::Output::from(&self.coords - &right); 'a); // TODO: should not be a ref to `right`.
|
||||
|
||||
add_sub_impl!(Sub, sub, ClosedSub;
|
||||
(Const<D1>, U1), (D2, U1) -> (Const<D1>, U1)
|
||||
const D1;
|
||||
for D2, SB;
|
||||
where D2: Dim, SB: Storage<T, D2>;
|
||||
self: Point<T, D1>, right: &'b Vector<T, D2, SB>, Output = Point<T, D1>;
|
||||
(D1, U1), (D2, U1) -> (D1, U1)
|
||||
const;
|
||||
for D1, D2, SB;
|
||||
where D1: DimName, D2: Dim, SB: Storage<T, D2>, DefaultAllocator: Allocator<T, D1>;
|
||||
self: OPoint<T, D1>, right: &'b Vector<T, D2, SB>, Output = OPoint<T, D1>;
|
||||
Self::Output::from(self.coords - right); 'b);
|
||||
|
||||
add_sub_impl!(Sub, sub, ClosedSub;
|
||||
(Const<D1>, U1), (D2, U1) -> (Const<D1>, U1)
|
||||
const D1;
|
||||
for D2, SB;
|
||||
where D2: Dim, SB: Storage<T, D2>;
|
||||
self: Point<T, D1>, right: Vector<T, D2, SB>, Output = Point<T, D1>;
|
||||
(D1, U1), (D2, U1) -> (D1, U1)
|
||||
const;
|
||||
for D1, D2, SB;
|
||||
where D1: DimName, D2: Dim, SB: Storage<T, D2>, DefaultAllocator: Allocator<T, D1>;
|
||||
self: OPoint<T, D1>, right: Vector<T, D2, SB>, Output = OPoint<T, D1>;
|
||||
Self::Output::from(self.coords - right); );
|
||||
|
||||
// Point + Vector
|
||||
add_sub_impl!(Add, add, ClosedAdd;
|
||||
(Const<D1>, U1), (D2, U1) -> (Const<D1>, U1)
|
||||
const D1;
|
||||
for D2, SB;
|
||||
where D2: Dim, SB: Storage<T, D2>;
|
||||
self: &'a Point<T, D1>, right: &'b Vector<T, D2, SB>, Output = Point<T, D1>;
|
||||
(D1, U1), (D2, U1) -> (D1, U1)
|
||||
const;
|
||||
for D1, D2, SB;
|
||||
where D1: DimName, D2: Dim, SB: Storage<T, D2>, DefaultAllocator: Allocator<T, D1>;
|
||||
self: &'a OPoint<T, D1>, right: &'b Vector<T, D2, SB>, Output = OPoint<T, D1>;
|
||||
Self::Output::from(&self.coords + right); 'a, 'b);
|
||||
|
||||
add_sub_impl!(Add, add, ClosedAdd;
|
||||
(Const<D1>, U1), (D2, U1) -> (Const<D1>, U1)
|
||||
const D1;
|
||||
for D2, SB;
|
||||
where D2: Dim, SB: Storage<T, D2>;
|
||||
self: &'a Point<T, D1>, right: Vector<T, D2, SB>, Output = Point<T, D1>;
|
||||
(D1, U1), (D2, U1) -> (D1, U1)
|
||||
const;
|
||||
for D1, D2, SB;
|
||||
where D1: DimName, D2: Dim, SB: Storage<T, D2>, DefaultAllocator: Allocator<T, D1>;
|
||||
self: &'a OPoint<T, D1>, right: Vector<T, D2, SB>, Output = OPoint<T, D1>;
|
||||
Self::Output::from(&self.coords + &right); 'a); // TODO: should not be a ref to `right`.
|
||||
|
||||
add_sub_impl!(Add, add, ClosedAdd;
|
||||
(Const<D1>, U1), (D2, U1) -> (Const<D1>, U1)
|
||||
const D1;
|
||||
for D2, SB;
|
||||
where D2: Dim, SB: Storage<T, D2>;
|
||||
self: Point<T, D1>, right: &'b Vector<T, D2, SB>, Output = Point<T, D1>;
|
||||
(D1, U1), (D2, U1) -> (D1, U1)
|
||||
const;
|
||||
for D1, D2, SB;
|
||||
where D1: DimName, D2: Dim, SB: Storage<T, D2>, DefaultAllocator: Allocator<T, D1>;
|
||||
self: OPoint<T, D1>, right: &'b Vector<T, D2, SB>, Output = OPoint<T, D1>;
|
||||
Self::Output::from(self.coords + right); 'b);
|
||||
|
||||
add_sub_impl!(Add, add, ClosedAdd;
|
||||
(Const<D1>, U1), (D2, U1) -> (Const<D1>, U1)
|
||||
const D1;
|
||||
for D2, SB;
|
||||
where D2: Dim, SB: Storage<T, D2>;
|
||||
self: Point<T, D1>, right: Vector<T, D2, SB>, Output = Point<T, D1>;
|
||||
(D1, U1), (D2, U1) -> (D1, U1)
|
||||
const;
|
||||
for D1, D2, SB;
|
||||
where D1: DimName, D2: Dim, SB: Storage<T, D2>, DefaultAllocator: Allocator<T, D1>;
|
||||
self: OPoint<T, D1>, right: Vector<T, D2, SB>, Output = OPoint<T, D1>;
|
||||
Self::Output::from(self.coords + right); );
|
||||
|
||||
// TODO: replace by the shared macro: add_sub_assign_impl?
|
||||
macro_rules! op_assign_impl(
|
||||
($($TraitAssign: ident, $method_assign: ident, $bound: ident);* $(;)*) => {$(
|
||||
impl<'b, T, D2: Dim, SB, const D1: usize> $TraitAssign<&'b Vector<T, D2, SB>> for Point<T, D1>
|
||||
impl<'b, T, D1: DimName, D2: Dim, SB> $TraitAssign<&'b Vector<T, D2, SB>> for OPoint<T, D1>
|
||||
where T: Scalar + $bound,
|
||||
SB: Storage<T, D2>,
|
||||
ShapeConstraint: SameNumberOfRows<Const<D1>, D2> {
|
||||
ShapeConstraint: SameNumberOfRows<D1, D2>,
|
||||
DefaultAllocator: Allocator<T, D1> {
|
||||
|
||||
#[inline]
|
||||
fn $method_assign(&mut self, right: &'b Vector<T, D2, SB>) {
|
||||
@ -169,10 +184,11 @@ macro_rules! op_assign_impl(
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D2: Dim, SB, const D1: usize> $TraitAssign<Vector<T, D2, SB>> for Point<T, D1>
|
||||
impl<T, D1: DimName, D2: Dim, SB> $TraitAssign<Vector<T, D2, SB>> for OPoint<T, D1>
|
||||
where T: Scalar + $bound,
|
||||
SB: Storage<T, D2>,
|
||||
ShapeConstraint: SameNumberOfRows<Const<D1>, D2> {
|
||||
ShapeConstraint: SameNumberOfRows<D1, D2>,
|
||||
DefaultAllocator: Allocator<T, D1> {
|
||||
|
||||
#[inline]
|
||||
fn $method_assign(&mut self, right: Vector<T, D2, SB>) {
|
||||
@ -214,28 +230,30 @@ md_impl_all!(
|
||||
macro_rules! componentwise_scalarop_impl(
|
||||
($Trait: ident, $method: ident, $bound: ident;
|
||||
$TraitAssign: ident, $method_assign: ident) => {
|
||||
impl<T: Scalar + $bound, const D: usize> $Trait<T> for Point<T, D>
|
||||
impl<T: Scalar + $bound, D: DimName> $Trait<T> for OPoint<T, D>
|
||||
where DefaultAllocator: Allocator<T, D>
|
||||
{
|
||||
type Output = Point<T, D>;
|
||||
type Output = OPoint<T, D>;
|
||||
|
||||
#[inline]
|
||||
fn $method(self, right: T) -> Self::Output {
|
||||
Point::from(self.coords.$method(right))
|
||||
OPoint::from(self.coords.$method(right))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Scalar + $bound, const D: usize> $Trait<T> for &'a Point<T, D>
|
||||
impl<'a, T: Scalar + $bound, D: DimName> $Trait<T> for &'a OPoint<T, D>
|
||||
where DefaultAllocator: Allocator<T, D>
|
||||
{
|
||||
type Output = Point<T, D>;
|
||||
type Output = OPoint<T, D>;
|
||||
|
||||
#[inline]
|
||||
fn $method(self, right: T) -> Self::Output {
|
||||
Point::from((&self.coords).$method(right))
|
||||
OPoint::from((&self.coords).$method(right))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + $bound, const D: usize> $TraitAssign<T> for Point<T, D>
|
||||
/* where DefaultAllocator: Allocator<T, D> */
|
||||
impl<T: Scalar + $bound, D: DimName> $TraitAssign<T> for OPoint<T, D>
|
||||
where DefaultAllocator: Allocator<T, D>
|
||||
{
|
||||
#[inline]
|
||||
fn $method_assign(&mut self, right: T) {
|
||||
@ -250,23 +268,25 @@ componentwise_scalarop_impl!(Div, div, ClosedDiv; DivAssign, div_assign);
|
||||
|
||||
macro_rules! left_scalar_mul_impl(
|
||||
($($T: ty),* $(,)*) => {$(
|
||||
impl<const D: usize> Mul<Point<$T, D>> for $T
|
||||
impl<D: DimName> Mul<OPoint<$T, D>> for $T
|
||||
where DefaultAllocator: Allocator<$T, D>
|
||||
{
|
||||
type Output = Point<$T, D>;
|
||||
type Output = OPoint<$T, D>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, right: Point<$T, D>) -> Self::Output {
|
||||
Point::from(self * right.coords)
|
||||
fn mul(self, right: OPoint<$T, D>) -> Self::Output {
|
||||
OPoint::from(self * right.coords)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, const D: usize> Mul<&'b Point<$T, D>> for $T
|
||||
impl<'b, D: DimName> Mul<&'b OPoint<$T, D>> for $T
|
||||
where DefaultAllocator: Allocator<$T, D>
|
||||
{
|
||||
type Output = Point<$T, D>;
|
||||
type Output = OPoint<$T, D>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, right: &'b Point<$T, D>) -> Self::Output {
|
||||
Point::from(self * &right.coords)
|
||||
fn mul(self, right: &'b OPoint<$T, D>) -> Self::Output {
|
||||
OPoint::from(self * &right.coords)
|
||||
}
|
||||
}
|
||||
)*}
|
||||
|
@ -139,7 +139,7 @@ mod rkyv_impl {
|
||||
|
||||
impl<T: Serialize<S>, S: Fallible + ?Sized> Serialize<S> for Quaternion<T> {
|
||||
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
|
||||
Ok(self.coords.serialize(serializer)?)
|
||||
self.coords.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,6 +191,7 @@ where
|
||||
|
||||
/// The imaginary part of this quaternion.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn imag(&self) -> Vector3<T> {
|
||||
self.coords.xyz()
|
||||
}
|
||||
@ -223,6 +224,7 @@ where
|
||||
/// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(1.9, 3.8, 5.7, 7.6));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lerp(&self, other: &Self, t: T) -> Self {
|
||||
self * (T::one() - t) + other * t
|
||||
}
|
||||
@ -238,6 +240,7 @@ where
|
||||
/// assert_eq!(q.vector()[2], 4.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn vector(&self) -> MatrixSlice<T, U3, U1, RStride<T, U4, U1>, CStride<T, U4, U1>> {
|
||||
self.coords.fixed_rows::<3>(0)
|
||||
}
|
||||
@ -251,6 +254,7 @@ where
|
||||
/// assert_eq!(q.scalar(), 1.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn scalar(&self) -> T {
|
||||
self.coords[3]
|
||||
}
|
||||
@ -266,6 +270,7 @@ where
|
||||
/// assert_eq!(*q.as_vector(), Vector4::new(2.0, 3.0, 4.0, 1.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_vector(&self) -> &Vector4<T> {
|
||||
&self.coords
|
||||
}
|
||||
@ -280,6 +285,7 @@ where
|
||||
/// assert_relative_eq!(q.norm(), 5.47722557, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn norm(&self) -> T {
|
||||
self.coords.norm()
|
||||
}
|
||||
@ -297,6 +303,7 @@ where
|
||||
/// assert_relative_eq!(q.magnitude(), 5.47722557, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn magnitude(&self) -> T {
|
||||
self.norm()
|
||||
}
|
||||
@ -310,6 +317,7 @@ where
|
||||
/// assert_eq!(q.magnitude_squared(), 30.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn norm_squared(&self) -> T {
|
||||
self.coords.norm_squared()
|
||||
}
|
||||
@ -326,6 +334,7 @@ where
|
||||
/// assert_eq!(q.magnitude_squared(), 30.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn magnitude_squared(&self) -> T {
|
||||
self.norm_squared()
|
||||
}
|
||||
@ -340,6 +349,7 @@ where
|
||||
/// assert_eq!(q1.dot(&q2), 70.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn dot(&self, rhs: &Self) -> T {
|
||||
self.coords.dot(&rhs.coords)
|
||||
}
|
||||
@ -409,6 +419,7 @@ where
|
||||
/// let result = a.inner(&b);
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-5);
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inner(&self, other: &Self) -> Self {
|
||||
(self * other + other * self).half()
|
||||
}
|
||||
@ -428,6 +439,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn outer(&self, other: &Self) -> Self {
|
||||
#[allow(clippy::eq_op)]
|
||||
(self * other - other * self).half()
|
||||
@ -448,6 +460,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn project(&self, other: &Self) -> Option<Self>
|
||||
where
|
||||
T: RealField,
|
||||
@ -470,6 +483,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn reject(&self, other: &Self) -> Option<Self>
|
||||
where
|
||||
T: RealField,
|
||||
@ -492,6 +506,7 @@ where
|
||||
/// assert_eq!(half_ang, f32::consts::FRAC_PI_2);
|
||||
/// assert_eq!(axis, Some(Vector3::x_axis()));
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn polar_decomposition(&self) -> (T, T, Option<Unit<Vector3<T>>>)
|
||||
where
|
||||
T: RealField,
|
||||
@ -519,6 +534,7 @@ where
|
||||
/// assert_relative_eq!(q.ln(), Quaternion::new(1.683647, 1.190289, 0.0, 0.0), epsilon = 1.0e-6)
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ln(&self) -> Self {
|
||||
let n = self.norm();
|
||||
let v = self.vector();
|
||||
@ -537,6 +553,7 @@ where
|
||||
/// assert_relative_eq!(q.exp(), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5)
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn exp(&self) -> Self {
|
||||
self.exp_eps(T::simd_default_epsilon())
|
||||
}
|
||||
@ -556,6 +573,7 @@ where
|
||||
/// assert_eq!(q.exp_eps(1.0e-6), Quaternion::identity());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn exp_eps(&self, eps: T) -> Self {
|
||||
let v = self.vector();
|
||||
let nn = v.norm_squared();
|
||||
@ -579,6 +597,7 @@ where
|
||||
/// assert_relative_eq!(q.powf(1.5), Quaternion::new( -6.2576659, 4.1549037, 6.2323556, 8.3098075), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn powf(&self, n: T) -> Self {
|
||||
(self.ln() * n).exp()
|
||||
}
|
||||
@ -674,18 +693,21 @@ where
|
||||
|
||||
/// Calculates square of a quaternion.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn squared(&self) -> Self {
|
||||
self * self
|
||||
}
|
||||
|
||||
/// Divides quaternion into two.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn half(&self) -> Self {
|
||||
self / crate::convert(2.0f64)
|
||||
}
|
||||
|
||||
/// Calculates square root.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sqrt(&self) -> Self {
|
||||
self.powf(crate::convert(0.5))
|
||||
}
|
||||
@ -694,12 +716,14 @@ where
|
||||
///
|
||||
/// A quaternion is pure if it has no real part (`self.w == 0.0`).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_pure(&self) -> bool {
|
||||
self.w.is_zero()
|
||||
}
|
||||
|
||||
/// Convert quaternion to pure quaternion.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pure(&self) -> Self {
|
||||
Self::from_imag(self.imag())
|
||||
}
|
||||
@ -708,6 +732,7 @@ where
|
||||
///
|
||||
/// Calculates B<sup>-1</sup> * A where A = self, B = other.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn left_div(&self, other: &Self) -> Option<Self>
|
||||
where
|
||||
T: RealField,
|
||||
@ -730,6 +755,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn right_div(&self, other: &Self) -> Option<Self>
|
||||
where
|
||||
T: RealField,
|
||||
@ -749,6 +775,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cos(&self) -> Self {
|
||||
let z = self.imag().magnitude();
|
||||
let w = -self.w.simd_sin() * z.simd_sinhc();
|
||||
@ -766,6 +793,7 @@ where
|
||||
/// assert_relative_eq!(input, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn acos(&self) -> Self {
|
||||
let u = Self::from_imag(self.imag().normalize());
|
||||
let identity = Self::identity();
|
||||
@ -787,6 +815,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sin(&self) -> Self {
|
||||
let z = self.imag().magnitude();
|
||||
let w = self.w.simd_cos() * z.simd_sinhc();
|
||||
@ -804,6 +833,7 @@ where
|
||||
/// assert_relative_eq!(input, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn asin(&self) -> Self {
|
||||
let u = Self::from_imag(self.imag().normalize());
|
||||
let identity = Self::identity();
|
||||
@ -825,6 +855,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn tan(&self) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -843,6 +874,7 @@ where
|
||||
/// assert_relative_eq!(input, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn atan(&self) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -867,6 +899,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sinh(&self) -> Self {
|
||||
(self.exp() - (-self).exp()).half()
|
||||
}
|
||||
@ -883,6 +916,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn asinh(&self) -> Self {
|
||||
let identity = Self::identity();
|
||||
(self + (identity + self.squared()).sqrt()).ln()
|
||||
@ -900,6 +934,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cosh(&self) -> Self {
|
||||
(self.exp() + (-self).exp()).half()
|
||||
}
|
||||
@ -916,6 +951,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn acosh(&self) -> Self {
|
||||
let identity = Self::identity();
|
||||
(self + (self + identity).sqrt() * (self - identity).sqrt()).ln()
|
||||
@ -933,6 +969,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn tanh(&self) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -952,6 +989,7 @@ where
|
||||
/// assert_relative_eq!(expected, result, epsilon = 1.0e-7);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn atanh(&self) -> Self {
|
||||
let identity = Self::identity();
|
||||
((identity + self).ln() - (identity - self).ln()).half()
|
||||
@ -1069,6 +1107,7 @@ where
|
||||
/// assert_eq!(rot.angle(), 1.78);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn angle(&self) -> T {
|
||||
let w = self.quaternion().scalar().simd_abs();
|
||||
self.quaternion().imag().norm().simd_atan2(w) * crate::convert(2.0f64)
|
||||
@ -1085,6 +1124,7 @@ where
|
||||
/// assert_eq!(*axis.quaternion(), Quaternion::new(1.0, 0.0, 0.0, 0.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn quaternion(&self) -> &Quaternion<T> {
|
||||
self.as_ref()
|
||||
}
|
||||
@ -1133,6 +1173,7 @@ where
|
||||
/// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn angle_to(&self, other: &Self) -> T {
|
||||
let delta = self.rotation_to(other);
|
||||
delta.angle()
|
||||
@ -1152,6 +1193,7 @@ where
|
||||
/// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn rotation_to(&self, other: &Self) -> Self {
|
||||
other / self
|
||||
}
|
||||
@ -1168,6 +1210,7 @@ where
|
||||
/// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(0.9, 0.1, 0.0, 0.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn lerp(&self, other: &Self, t: T) -> Quaternion<T> {
|
||||
self.as_ref().lerp(other.as_ref(), t)
|
||||
}
|
||||
@ -1184,6 +1227,7 @@ where
|
||||
/// assert_eq!(q1.nlerp(&q2, 0.1), UnitQuaternion::new_normalize(Quaternion::new(0.9, 0.1, 0.0, 0.0)));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn nlerp(&self, other: &Self, t: T) -> Self {
|
||||
let mut res = self.lerp(other, t);
|
||||
let _ = res.normalize_mut();
|
||||
@ -1209,6 +1253,7 @@ where
|
||||
/// assert_eq!(q.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn slerp(&self, other: &Self, t: T) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -1228,6 +1273,7 @@ where
|
||||
/// * `epsilon`: the value below which the sinus of the angle separating both quaternion
|
||||
/// must be to return `None`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn try_slerp(&self, other: &Self, t: T, epsilon: T) -> Option<Self>
|
||||
where
|
||||
T: RealField,
|
||||
@ -1287,6 +1333,7 @@ where
|
||||
/// assert!(rot.axis().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn axis(&self) -> Option<Unit<Vector3<T>>>
|
||||
where
|
||||
T: RealField,
|
||||
@ -1311,6 +1358,7 @@ where
|
||||
/// assert_relative_eq!(rot.scaled_axis(), axisangle, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn scaled_axis(&self) -> Vector3<T>
|
||||
where
|
||||
T: RealField,
|
||||
@ -1339,6 +1387,7 @@ where
|
||||
/// assert!(rot.axis_angle().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn axis_angle(&self) -> Option<(Unit<Vector3<T>>, T)>
|
||||
where
|
||||
T: RealField,
|
||||
@ -1350,6 +1399,7 @@ where
|
||||
///
|
||||
/// Note that this function yields a `Quaternion<T>` because it loses the unit property.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn exp(&self) -> Quaternion<T> {
|
||||
self.as_ref().exp()
|
||||
}
|
||||
@ -1369,6 +1419,7 @@ where
|
||||
/// assert_relative_eq!(q.ln().vector().into_owned(), axisangle, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ln(&self) -> Quaternion<T>
|
||||
where
|
||||
T: RealField,
|
||||
@ -1397,6 +1448,7 @@ where
|
||||
/// assert_eq!(pow.angle(), 2.4);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn powf(&self, n: T) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -1425,7 +1477,8 @@ where
|
||||
/// assert_relative_eq!(*rot.matrix(), expected, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_rotation_matrix(&self) -> Rotation<T, 3> {
|
||||
#[must_use]
|
||||
pub fn to_rotation_matrix(self) -> Rotation<T, 3> {
|
||||
let i = self.as_ref()[0];
|
||||
let j = self.as_ref()[1];
|
||||
let k = self.as_ref()[2];
|
||||
@ -1460,7 +1513,7 @@ where
|
||||
/// The angles are produced in the form (roll, pitch, yaw).
|
||||
#[inline]
|
||||
#[deprecated(note = "This is renamed to use `.euler_angles()`.")]
|
||||
pub fn to_euler_angles(&self) -> (T, T, T)
|
||||
pub fn to_euler_angles(self) -> (T, T, T)
|
||||
where
|
||||
T: RealField,
|
||||
{
|
||||
@ -1482,6 +1535,7 @@ where
|
||||
/// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn euler_angles(&self) -> (T, T, T)
|
||||
where
|
||||
T: RealField,
|
||||
@ -1506,7 +1560,8 @@ where
|
||||
/// assert_relative_eq!(rot.to_homogeneous(), expected, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_homogeneous(&self) -> Matrix4<T> {
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(self) -> Matrix4<T> {
|
||||
self.to_rotation_matrix().to_homogeneous()
|
||||
}
|
||||
|
||||
@ -1526,6 +1581,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, pt: &Point3<T>) -> Point3<T> {
|
||||
self * pt
|
||||
}
|
||||
@ -1546,6 +1602,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, v: &Vector3<T>) -> Vector3<T> {
|
||||
self * v
|
||||
}
|
||||
@ -1566,6 +1623,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[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
|
||||
// the inverse transformation explicitly here) ?
|
||||
@ -1588,6 +1646,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_vector(&self, v: &Vector3<T>) -> Vector3<T> {
|
||||
self.inverse() * v
|
||||
}
|
||||
@ -1608,6 +1667,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector3<T>>) -> Unit<Vector3<T>> {
|
||||
self.inverse() * v
|
||||
}
|
||||
@ -1616,6 +1676,7 @@ where
|
||||
///
|
||||
/// This is faster, but approximate, way to compute `UnitQuaternion::new(axisangle) * self`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn append_axisangle_linearized(&self, axisangle: &Vector3<T>) -> Self {
|
||||
let half: T = crate::convert(0.5);
|
||||
let q1 = self.into_inner();
|
||||
|
@ -171,7 +171,7 @@ where
|
||||
Standard: Distribution<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn sample<'a, R: Rng + ?Sized>(&self, rng: &'a mut R) -> Quaternion<T> {
|
||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Quaternion<T> {
|
||||
Quaternion::new(rng.gen(), rng.gen(), rng.gen(), rng.gen())
|
||||
}
|
||||
}
|
||||
@ -535,10 +535,10 @@ where
|
||||
SC: Storage<T, U3>,
|
||||
{
|
||||
// TODO: code duplication with Rotation.
|
||||
let c = na.cross(&nb);
|
||||
let c = na.cross(nb);
|
||||
|
||||
if let Some(axis) = Unit::try_new(c, T::default_epsilon()) {
|
||||
let cos = na.dot(&nb);
|
||||
let cos = na.dot(nb);
|
||||
|
||||
// The cosinus may be out of [-1, 1] because of inaccuracies.
|
||||
if cos <= -T::one() {
|
||||
@ -548,7 +548,7 @@ where
|
||||
} else {
|
||||
Some(Self::from_axis_angle(&axis, cos.acos() * s))
|
||||
}
|
||||
} else if na.dot(&nb) < T::zero() {
|
||||
} else if na.dot(nb) < T::zero() {
|
||||
// PI
|
||||
//
|
||||
// The rotation axis is undefined but the angle not zero. This is not a
|
||||
@ -860,7 +860,7 @@ where
|
||||
{
|
||||
/// Generate a uniformly distributed random rotation quaternion.
|
||||
#[inline]
|
||||
fn sample<'a, R: Rng + ?Sized>(&self, rng: &'a mut R) -> UnitQuaternion<T> {
|
||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> UnitQuaternion<T> {
|
||||
// Ken Shoemake's Subgroup Algorithm
|
||||
// Uniform random rotations.
|
||||
// In D. Kirk, editor, Graphics Gems III, pages 124-132. Academic, New York, 1992.
|
||||
|
@ -1,4 +1,3 @@
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use simba::simd::SimdValue;
|
||||
@ -13,13 +12,13 @@ impl<T: Scalar + SimdValue> Deref for Quaternion<T> {
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { mem::transmute(self) }
|
||||
unsafe { &*(self as *const Self as *const Self::Target) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + SimdValue> DerefMut for Quaternion<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { mem::transmute(self) }
|
||||
unsafe { &mut *(self as *mut Self as *mut Self::Target) }
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
// The macros break if the references are taken out, for some reason.
|
||||
#![allow(clippy::op_ref)]
|
||||
|
||||
/*
|
||||
* This file provides:
|
||||
* ===================
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::base::constraint::{AreMultipliable, DimEq, SameNumberOfRows, ShapeConstraint};
|
||||
use crate::base::{Const, Matrix, Scalar, Unit, Vector};
|
||||
use crate::base::{Const, Matrix, Unit, Vector};
|
||||
use crate::dimension::{Dim, U1};
|
||||
use crate::storage::{Storage, StorageMut};
|
||||
use simba::scalar::ComplexField;
|
||||
@ -7,7 +7,7 @@ use simba::scalar::ComplexField;
|
||||
use crate::geometry::Point;
|
||||
|
||||
/// A reflection wrt. a plane.
|
||||
pub struct Reflection<T: Scalar, D: Dim, S: Storage<T, D>> {
|
||||
pub struct Reflection<T, D, S> {
|
||||
axis: Vector<T, D, S>,
|
||||
bias: T,
|
||||
}
|
||||
@ -34,6 +34,7 @@ impl<T: ComplexField, D: Dim, S: Storage<T, D>> Reflection<T, D, S> {
|
||||
}
|
||||
|
||||
/// The reflexion axis.
|
||||
#[must_use]
|
||||
pub fn axis(&self) -> &Vector<T, D, S> {
|
||||
&self.axis
|
||||
}
|
||||
@ -89,7 +90,7 @@ impl<T: ComplexField, D: Dim, S: Storage<T, D>> Reflection<T, D, S> {
|
||||
}
|
||||
|
||||
let m_two: T = crate::convert(-2.0f64);
|
||||
lhs.gerc(m_two, &work, &self.axis, T::one());
|
||||
lhs.gerc(m_two, work, &self.axis, T::one());
|
||||
}
|
||||
|
||||
/// Applies the reflection to the rows of `lhs`.
|
||||
@ -110,6 +111,6 @@ impl<T: ComplexField, D: Dim, S: Storage<T, D>> Reflection<T, D, S> {
|
||||
}
|
||||
|
||||
let m_two = sign.scale(crate::convert(-2.0f64));
|
||||
lhs.gerc(m_two, &work, &self.axis, sign);
|
||||
lhs.gerc(m_two, work, &self.axis, sign);
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ use crate::geometry::Point;
|
||||
///
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct Rotation<T: Scalar, const D: usize> {
|
||||
pub struct Rotation<T, const D: usize> {
|
||||
matrix: SMatrix<T, D, D>,
|
||||
}
|
||||
|
||||
@ -185,6 +185,7 @@ impl<T: Scalar, const D: usize> Rotation<T, D> {
|
||||
/// assert_eq!(*rot.matrix(), expected);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn matrix(&self) -> &SMatrix<T, D, D> {
|
||||
&self.matrix
|
||||
}
|
||||
@ -198,9 +199,9 @@ impl<T: Scalar, const D: usize> Rotation<T, D> {
|
||||
|
||||
/// A mutable reference to the underlying matrix representation of this rotation.
|
||||
///
|
||||
/// This is suffixed by "_unchecked" because this allows the user to replace the matrix by another one that is
|
||||
/// non-square, non-inversible, or non-orthonormal. If one of those properties is broken,
|
||||
/// subsequent method calls may be UB.
|
||||
/// This is suffixed by "_unchecked" because this allows the user to replace the
|
||||
/// matrix by another one that is non-inversible or non-orthonormal. If one of
|
||||
/// those properties is broken, subsequent method calls may return bogus results.
|
||||
#[inline]
|
||||
pub fn matrix_mut_unchecked(&mut self) -> &mut SMatrix<T, D, D> {
|
||||
&mut self.matrix
|
||||
@ -262,6 +263,7 @@ impl<T: Scalar, const D: usize> Rotation<T, D> {
|
||||
/// assert_eq!(rot.to_homogeneous(), expected);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(&self) -> OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>
|
||||
where
|
||||
T: Zero + One,
|
||||
@ -403,6 +405,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
self * pt
|
||||
}
|
||||
@ -422,6 +425,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
|
||||
self * v
|
||||
}
|
||||
@ -441,6 +445,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
Point::from(self.inverse_transform_vector(&pt.coords))
|
||||
}
|
||||
@ -460,6 +465,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
|
||||
self.matrix().tr_mul(v)
|
||||
}
|
||||
@ -479,6 +485,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_unit_vector(&self, v: &Unit<SVector<T, D>>) -> Unit<SVector<T, D>> {
|
||||
Unit::new_unchecked(self.inverse_transform_vector(&**v))
|
||||
}
|
||||
|
@ -267,10 +267,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn from(arr: [Rotation<T::Element, D>; 2]) -> Self {
|
||||
Self::from_matrix_unchecked(OMatrix::from([
|
||||
arr[0].clone().into_inner(),
|
||||
arr[1].clone().into_inner(),
|
||||
]))
|
||||
Self::from_matrix_unchecked(OMatrix::from([arr[0].into_inner(), arr[1].into_inner()]))
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,10 +280,10 @@ where
|
||||
#[inline]
|
||||
fn from(arr: [Rotation<T::Element, D>; 4]) -> Self {
|
||||
Self::from_matrix_unchecked(OMatrix::from([
|
||||
arr[0].clone().into_inner(),
|
||||
arr[1].clone().into_inner(),
|
||||
arr[2].clone().into_inner(),
|
||||
arr[3].clone().into_inner(),
|
||||
arr[0].into_inner(),
|
||||
arr[1].into_inner(),
|
||||
arr[2].into_inner(),
|
||||
arr[3].into_inner(),
|
||||
]))
|
||||
}
|
||||
}
|
||||
@ -300,14 +297,14 @@ where
|
||||
#[inline]
|
||||
fn from(arr: [Rotation<T::Element, D>; 8]) -> Self {
|
||||
Self::from_matrix_unchecked(OMatrix::from([
|
||||
arr[0].clone().into_inner(),
|
||||
arr[1].clone().into_inner(),
|
||||
arr[2].clone().into_inner(),
|
||||
arr[3].clone().into_inner(),
|
||||
arr[4].clone().into_inner(),
|
||||
arr[5].clone().into_inner(),
|
||||
arr[6].clone().into_inner(),
|
||||
arr[7].clone().into_inner(),
|
||||
arr[0].into_inner(),
|
||||
arr[1].into_inner(),
|
||||
arr[2].into_inner(),
|
||||
arr[3].into_inner(),
|
||||
arr[4].into_inner(),
|
||||
arr[5].into_inner(),
|
||||
arr[6].into_inner(),
|
||||
arr[7].into_inner(),
|
||||
]))
|
||||
}
|
||||
}
|
||||
@ -321,22 +318,22 @@ where
|
||||
#[inline]
|
||||
fn from(arr: [Rotation<T::Element, D>; 16]) -> Self {
|
||||
Self::from_matrix_unchecked(OMatrix::from([
|
||||
arr[0].clone().into_inner(),
|
||||
arr[1].clone().into_inner(),
|
||||
arr[2].clone().into_inner(),
|
||||
arr[3].clone().into_inner(),
|
||||
arr[4].clone().into_inner(),
|
||||
arr[5].clone().into_inner(),
|
||||
arr[6].clone().into_inner(),
|
||||
arr[7].clone().into_inner(),
|
||||
arr[8].clone().into_inner(),
|
||||
arr[9].clone().into_inner(),
|
||||
arr[10].clone().into_inner(),
|
||||
arr[11].clone().into_inner(),
|
||||
arr[12].clone().into_inner(),
|
||||
arr[13].clone().into_inner(),
|
||||
arr[14].clone().into_inner(),
|
||||
arr[15].clone().into_inner(),
|
||||
arr[0].into_inner(),
|
||||
arr[1].into_inner(),
|
||||
arr[2].into_inner(),
|
||||
arr[3].into_inner(),
|
||||
arr[4].into_inner(),
|
||||
arr[5].into_inner(),
|
||||
arr[6].into_inner(),
|
||||
arr[7].into_inner(),
|
||||
arr[8].into_inner(),
|
||||
arr[9].into_inner(),
|
||||
arr[10].into_inner(),
|
||||
arr[11].into_inner(),
|
||||
arr[12].into_inner(),
|
||||
arr[13].into_inner(),
|
||||
arr[14].into_inner(),
|
||||
arr[15].into_inner(),
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ impl<T: SimdRealField> Rotation2<T> {
|
||||
/// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn slerp(&self, other: &Self, t: T) -> Self
|
||||
where
|
||||
T::Element: SimdRealField,
|
||||
@ -47,6 +48,7 @@ impl<T: SimdRealField> Rotation3<T> {
|
||||
/// assert_eq!(q.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn slerp(&self, other: &Self, t: T) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -67,12 +69,13 @@ impl<T: SimdRealField> Rotation3<T> {
|
||||
/// * `epsilon`: the value below which the sinus of the angle separating both rotations
|
||||
/// must be to return `None`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn try_slerp(&self, other: &Self, t: T, epsilon: T) -> Option<Self>
|
||||
where
|
||||
T: RealField,
|
||||
{
|
||||
let q1 = Rotation3::from(*self);
|
||||
let q2 = Rotation3::from(*other);
|
||||
let q1 = UnitQuaternion::from(*self);
|
||||
let q2 = UnitQuaternion::from(*other);
|
||||
q1.try_slerp(&q2, t, epsilon).map(|q| q.into())
|
||||
}
|
||||
}
|
||||
|
@ -186,6 +186,7 @@ impl<T: SimdRealField> Rotation2<T> {
|
||||
/// assert_relative_eq!(rot_to.inverse() * rot2, rot1);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn rotation_to(&self, other: &Self) -> Self {
|
||||
other * self.inverse()
|
||||
}
|
||||
@ -215,6 +216,7 @@ impl<T: SimdRealField> Rotation2<T> {
|
||||
/// assert_relative_eq!(pow.angle(), 2.0 * 0.78);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn powf(&self, n: T) -> Self {
|
||||
Self::new(self.angle() * n)
|
||||
}
|
||||
@ -232,6 +234,7 @@ impl<T: SimdRealField> Rotation2<T> {
|
||||
/// assert_relative_eq!(rot.angle(), 1.78);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn angle(&self) -> T {
|
||||
self.matrix()[(1, 0)].simd_atan2(self.matrix()[(0, 0)])
|
||||
}
|
||||
@ -247,6 +250,7 @@ impl<T: SimdRealField> Rotation2<T> {
|
||||
/// assert_relative_eq!(rot1.angle_to(&rot2), 1.6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn angle_to(&self, other: &Self) -> T {
|
||||
self.rotation_to(other).angle()
|
||||
}
|
||||
@ -256,6 +260,7 @@ impl<T: SimdRealField> Rotation2<T> {
|
||||
/// This is generally used in the context of generic programming. Using
|
||||
/// the `.angle()` method instead is more common.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn scaled_axis(&self) -> SVector<T, 1> {
|
||||
Vector1::new(self.angle())
|
||||
}
|
||||
@ -269,7 +274,7 @@ where
|
||||
{
|
||||
/// Generate a uniformly distributed random rotation.
|
||||
#[inline]
|
||||
fn sample<'a, R: Rng + ?Sized>(&self, rng: &'a mut R) -> Rotation2<T> {
|
||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Rotation2<T> {
|
||||
let twopi = Uniform::new(T::zero(), T::simd_two_pi());
|
||||
Rotation2::new(rng.sample(twopi))
|
||||
}
|
||||
@ -640,6 +645,7 @@ where
|
||||
/// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn rotation_to(&self, other: &Self) -> Self {
|
||||
other * self.inverse()
|
||||
}
|
||||
@ -659,6 +665,7 @@ where
|
||||
/// assert_eq!(pow.angle(), 2.4);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn powf(&self, n: T) -> Self
|
||||
where
|
||||
T: RealField,
|
||||
@ -765,6 +772,7 @@ impl<T: SimdRealField> Rotation3<T> {
|
||||
/// assert_relative_eq!(rot.angle(), 1.78);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn angle(&self) -> T {
|
||||
((self.matrix()[(0, 0)] + self.matrix()[(1, 1)] + self.matrix()[(2, 2)] - T::one())
|
||||
/ crate::convert(2.0))
|
||||
@ -787,6 +795,7 @@ impl<T: SimdRealField> Rotation3<T> {
|
||||
/// assert!(rot.axis().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn axis(&self) -> Option<Unit<Vector3<T>>>
|
||||
where
|
||||
T: RealField,
|
||||
@ -811,6 +820,7 @@ impl<T: SimdRealField> Rotation3<T> {
|
||||
/// assert_relative_eq!(rot.scaled_axis(), axisangle, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn scaled_axis(&self) -> Vector3<T>
|
||||
where
|
||||
T: RealField,
|
||||
@ -842,15 +852,12 @@ impl<T: SimdRealField> Rotation3<T> {
|
||||
/// assert!(rot.axis_angle().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn axis_angle(&self) -> Option<(Unit<Vector3<T>>, T)>
|
||||
where
|
||||
T: RealField,
|
||||
{
|
||||
if let Some(axis) = self.axis() {
|
||||
Some((axis, self.angle()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
self.axis().map(|axis| (axis, self.angle()))
|
||||
}
|
||||
|
||||
/// The rotation angle needed to make `self` and `other` coincide.
|
||||
@ -864,6 +871,7 @@ impl<T: SimdRealField> Rotation3<T> {
|
||||
/// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn angle_to(&self, other: &Self) -> T
|
||||
where
|
||||
T::Element: SimdRealField,
|
||||
@ -875,7 +883,7 @@ impl<T: SimdRealField> Rotation3<T> {
|
||||
///
|
||||
/// The angles are produced in the form (roll, pitch, yaw).
|
||||
#[deprecated(note = "This is renamed to use `.euler_angles()`.")]
|
||||
pub fn to_euler_angles(&self) -> (T, T, T)
|
||||
pub fn to_euler_angles(self) -> (T, T, T)
|
||||
where
|
||||
T: RealField,
|
||||
{
|
||||
@ -896,6 +904,7 @@ impl<T: SimdRealField> Rotation3<T> {
|
||||
/// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6);
|
||||
/// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn euler_angles(&self) -> (T, T, T)
|
||||
where
|
||||
T: RealField,
|
||||
|
@ -27,19 +27,19 @@ use crate::geometry::{AbstractRotation, Isometry, Point, Translation};
|
||||
#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde-serialize-no-std",
|
||||
serde(bound(serialize = "T: Serialize,
|
||||
serde(bound(serialize = "T: Scalar + Serialize,
|
||||
R: Serialize,
|
||||
DefaultAllocator: Allocator<T, Const<D>>,
|
||||
Owned<T, Const<D>>: Serialize"))
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde-serialize-no-std",
|
||||
serde(bound(deserialize = "T: Deserialize<'de>,
|
||||
serde(bound(deserialize = "T: Scalar + Deserialize<'de>,
|
||||
R: Deserialize<'de>,
|
||||
DefaultAllocator: Allocator<T, Const<D>>,
|
||||
Owned<T, Const<D>>: Deserialize<'de>"))
|
||||
)]
|
||||
pub struct Similarity<T: Scalar, R, const D: usize> {
|
||||
pub struct Similarity<T, R, const D: usize> {
|
||||
/// The part of this similarity that does not include the scaling factor.
|
||||
pub isometry: Isometry<T, R, D>,
|
||||
scaling: T,
|
||||
@ -122,6 +122,7 @@ where
|
||||
impl<T: Scalar, R, const D: usize> Similarity<T, R, D> {
|
||||
/// The scaling factor of this similarity transformation.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn scaling(&self) -> T {
|
||||
self.scaling.inlined_clone()
|
||||
}
|
||||
@ -177,7 +178,7 @@ where
|
||||
);
|
||||
|
||||
Self::from_parts(
|
||||
Translation::from(&self.isometry.translation.vector * scaling),
|
||||
Translation::from(self.isometry.translation.vector * scaling),
|
||||
self.isometry.rotation.clone(),
|
||||
self.scaling * scaling,
|
||||
)
|
||||
@ -248,6 +249,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point3::new(19.0, 17.0, -9.0), epsilon = 1.0e-5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
self * pt
|
||||
}
|
||||
@ -269,6 +271,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, Vector3::new(18.0, 15.0, -12.0), epsilon = 1.0e-5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
|
||||
self * v
|
||||
}
|
||||
@ -289,6 +292,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point3::new(-1.5, 1.5, 1.5), epsilon = 1.0e-5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
self.isometry.inverse_transform_point(pt) / self.scaling()
|
||||
}
|
||||
@ -309,6 +313,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, Vector3::new(-3.0, 2.5, 2.0), epsilon = 1.0e-5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
|
||||
self.isometry.inverse_transform_vector(v) / self.scaling()
|
||||
}
|
||||
@ -321,6 +326,7 @@ where
|
||||
impl<T: SimdRealField, R, const D: usize> Similarity<T, R, D> {
|
||||
/// Converts this similarity into its equivalent homogeneous transformation matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(&self) -> OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>
|
||||
where
|
||||
Const<D>: DimNameAdd<U1>,
|
||||
|
@ -197,7 +197,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn from(arr: [Similarity<T::Element, R::Element, D>; 2]) -> Self {
|
||||
let iso = Isometry::from([arr[0].isometry.clone(), arr[1].isometry.clone()]);
|
||||
let iso = Isometry::from([arr[0].isometry, arr[1].isometry]);
|
||||
let scale = T::from([arr[0].scaling(), arr[1].scaling()]);
|
||||
|
||||
Self::from_isometry(iso, scale)
|
||||
@ -216,10 +216,10 @@ where
|
||||
#[inline]
|
||||
fn from(arr: [Similarity<T::Element, R::Element, D>; 4]) -> Self {
|
||||
let iso = Isometry::from([
|
||||
arr[0].isometry.clone(),
|
||||
arr[1].isometry.clone(),
|
||||
arr[2].isometry.clone(),
|
||||
arr[3].isometry.clone(),
|
||||
arr[0].isometry,
|
||||
arr[1].isometry,
|
||||
arr[2].isometry,
|
||||
arr[3].isometry,
|
||||
]);
|
||||
let scale = T::from([
|
||||
arr[0].scaling(),
|
||||
@ -244,14 +244,14 @@ where
|
||||
#[inline]
|
||||
fn from(arr: [Similarity<T::Element, R::Element, D>; 8]) -> Self {
|
||||
let iso = Isometry::from([
|
||||
arr[0].isometry.clone(),
|
||||
arr[1].isometry.clone(),
|
||||
arr[2].isometry.clone(),
|
||||
arr[3].isometry.clone(),
|
||||
arr[4].isometry.clone(),
|
||||
arr[5].isometry.clone(),
|
||||
arr[6].isometry.clone(),
|
||||
arr[7].isometry.clone(),
|
||||
arr[0].isometry,
|
||||
arr[1].isometry,
|
||||
arr[2].isometry,
|
||||
arr[3].isometry,
|
||||
arr[4].isometry,
|
||||
arr[5].isometry,
|
||||
arr[6].isometry,
|
||||
arr[7].isometry,
|
||||
]);
|
||||
let scale = T::from([
|
||||
arr[0].scaling(),
|
||||
@ -280,22 +280,22 @@ where
|
||||
#[inline]
|
||||
fn from(arr: [Similarity<T::Element, R::Element, D>; 16]) -> Self {
|
||||
let iso = Isometry::from([
|
||||
arr[0].isometry.clone(),
|
||||
arr[1].isometry.clone(),
|
||||
arr[2].isometry.clone(),
|
||||
arr[3].isometry.clone(),
|
||||
arr[4].isometry.clone(),
|
||||
arr[5].isometry.clone(),
|
||||
arr[6].isometry.clone(),
|
||||
arr[7].isometry.clone(),
|
||||
arr[8].isometry.clone(),
|
||||
arr[9].isometry.clone(),
|
||||
arr[10].isometry.clone(),
|
||||
arr[11].isometry.clone(),
|
||||
arr[12].isometry.clone(),
|
||||
arr[13].isometry.clone(),
|
||||
arr[14].isometry.clone(),
|
||||
arr[15].isometry.clone(),
|
||||
arr[0].isometry,
|
||||
arr[1].isometry,
|
||||
arr[2].isometry,
|
||||
arr[3].isometry,
|
||||
arr[4].isometry,
|
||||
arr[5].isometry,
|
||||
arr[6].isometry,
|
||||
arr[7].isometry,
|
||||
arr[8].isometry,
|
||||
arr[9].isometry,
|
||||
arr[10].isometry,
|
||||
arr[11].isometry,
|
||||
arr[12].isometry,
|
||||
arr[13].isometry,
|
||||
arr[14].isometry,
|
||||
arr[15].isometry,
|
||||
]);
|
||||
let scale = T::from([
|
||||
arr[0].scaling(),
|
||||
|
@ -1,3 +1,6 @@
|
||||
// The macros break if the references are taken out, for some reason.
|
||||
#![allow(clippy::op_ref)]
|
||||
|
||||
use num::{One, Zero};
|
||||
use std::ops::{Div, DivAssign, Mul, MulAssign};
|
||||
|
||||
@ -219,7 +222,7 @@ md_assign_impl_all!(
|
||||
const D; for; where;
|
||||
self: Similarity<T, Rotation<T, D>, D>, rhs: Rotation<T, D>;
|
||||
[val] => self.isometry.rotation *= rhs;
|
||||
[ref] => self.isometry.rotation *= rhs.clone();
|
||||
[ref] => self.isometry.rotation *= *rhs;
|
||||
);
|
||||
|
||||
md_assign_impl_all!(
|
||||
|
@ -8,6 +8,7 @@ macro_rules! impl_swizzle {
|
||||
$(
|
||||
/// Builds a new point from components of `self`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn $name(&self) -> $Result<T>
|
||||
where <Const<D> as ToTypenum>::Typenum: Cmp<typenum::$BaseDim, Output=Greater> {
|
||||
$Result::new($(self[$i].inlined_clone()),*)
|
||||
|
@ -1,6 +1,7 @@
|
||||
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::hash;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[cfg(feature = "serde-serialize-no-std")]
|
||||
@ -166,14 +167,16 @@ where
|
||||
_phantom: PhantomData<C>,
|
||||
}
|
||||
|
||||
// TODO
|
||||
// impl<T: RealField + hash::Hash, D: DimNameAdd<U1> + hash::Hash, C: TCategory> hash::Hash for Transform<T, C, D>
|
||||
// where DefaultAllocator: Allocator<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>,
|
||||
// Owned<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>: hash::Hash {
|
||||
// fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
// self.matrix.hash(state);
|
||||
// }
|
||||
// }
|
||||
impl<T: RealField + hash::Hash, C: TCategory, const D: usize> hash::Hash for Transform<T, C, D>
|
||||
where
|
||||
Const<D>: DimNameAdd<U1>,
|
||||
DefaultAllocator: Allocator<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>,
|
||||
Owned<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>: hash::Hash,
|
||||
{
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.matrix.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RealField, C: TCategory, const D: usize> Copy for Transform<T, C, D>
|
||||
where
|
||||
@ -301,6 +304,7 @@ where
|
||||
/// assert_eq!(*t.matrix(), m);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn matrix(&self) -> &OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>> {
|
||||
&self.matrix
|
||||
}
|
||||
@ -367,6 +371,7 @@ where
|
||||
/// assert_eq!(t.into_inner(), m);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(&self) -> OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>> {
|
||||
self.matrix().clone_owned()
|
||||
}
|
||||
@ -397,11 +402,9 @@ where
|
||||
#[inline]
|
||||
#[must_use = "Did you mean to use try_inverse_mut()?"]
|
||||
pub fn try_inverse(self) -> Option<Transform<T, C, D>> {
|
||||
if let Some(m) = self.matrix.try_inverse() {
|
||||
Some(Transform::from_matrix_unchecked(m))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
self.matrix
|
||||
.try_inverse()
|
||||
.map(Transform::from_matrix_unchecked)
|
||||
}
|
||||
|
||||
/// Inverts this transformation. Use `.try_inverse` if this transform has the `TGeneral`
|
||||
@ -498,6 +501,7 @@ where
|
||||
///
|
||||
/// This is the same as the multiplication `self * pt`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
self * pt
|
||||
}
|
||||
@ -507,6 +511,7 @@ where
|
||||
///
|
||||
/// This is the same as the multiplication `self * v`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
|
||||
self * v
|
||||
}
|
||||
@ -524,6 +529,7 @@ where
|
||||
/// This may be cheaper than inverting the transformation and transforming
|
||||
/// the point.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
self.clone().inverse() * pt
|
||||
}
|
||||
@ -532,6 +538,7 @@ where
|
||||
/// This may be cheaper than inverting the transformation and transforming
|
||||
/// the vector.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
|
||||
self.clone().inverse() * v
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
// The macros break if the references are taken out, for some reason.
|
||||
#![allow(clippy::op_ref)]
|
||||
|
||||
use num::{One, Zero};
|
||||
use std::ops::{Div, DivAssign, Index, IndexMut, Mul, MulAssign};
|
||||
|
||||
@ -121,7 +124,7 @@ md_impl_all!(
|
||||
|
||||
if C::has_normalizer() {
|
||||
let normalizer = self.matrix().fixed_slice::<1, D>(D, 0);
|
||||
let n = normalizer.tr_dot(&rhs);
|
||||
let n = normalizer.tr_dot(rhs);
|
||||
|
||||
if !n.is_zero() {
|
||||
return transform * (rhs / n);
|
||||
|
@ -38,7 +38,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Copy, const D: usize> Copy for Translation<T, D> where Owned<T, Const<D>>: Copy {}
|
||||
impl<T: Scalar + Copy, const D: usize> Copy for Translation<T, D> {}
|
||||
|
||||
impl<T: Scalar, const D: usize> Clone for Translation<T, D>
|
||||
where
|
||||
@ -123,7 +123,7 @@ mod rkyv_impl {
|
||||
|
||||
impl<T: Serialize<S>, S: Fallible + ?Sized, const D: usize> Serialize<S> for Translation<T, D> {
|
||||
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
|
||||
Ok(self.vector.serialize(serializer)?)
|
||||
self.vector.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,6 +190,7 @@ impl<T: Scalar, const D: usize> Translation<T, D> {
|
||||
/// assert_eq!(t.to_homogeneous(), expected);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(&self) -> OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>
|
||||
where
|
||||
T: Zero + One,
|
||||
@ -241,6 +242,7 @@ impl<T: Scalar + ClosedAdd, const D: usize> Translation<T, D> {
|
||||
/// let transformed_point = t.transform_point(&Point3::new(4.0, 5.0, 6.0));
|
||||
/// assert_eq!(transformed_point, Point3::new(5.0, 7.0, 9.0));
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
pt + &self.vector
|
||||
}
|
||||
@ -256,6 +258,7 @@ impl<T: Scalar + ClosedSub, const D: usize> Translation<T, D> {
|
||||
/// let transformed_point = t.inverse_transform_point(&Point3::new(4.0, 5.0, 6.0));
|
||||
/// assert_eq!(transformed_point, Point3::new(3.0, 3.0, 3.0));
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
|
||||
pt - &self.vector
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ where
|
||||
{
|
||||
/// Generate an arbitrary random variate for testing purposes.
|
||||
#[inline]
|
||||
fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Translation<T, D> {
|
||||
fn sample<G: Rng + ?Sized>(&self, rng: &mut G) -> Translation<T, D> {
|
||||
Translation::from(rng.gen::<SVector<T, D>>())
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn to_superset(&self) -> UnitDualQuaternion<T2> {
|
||||
let dq = UnitDualQuaternion::<T1>::from_parts(self.clone(), UnitQuaternion::identity());
|
||||
let dq = UnitDualQuaternion::<T1>::from_parts(*self, UnitQuaternion::identity());
|
||||
dq.to_superset()
|
||||
}
|
||||
|
||||
@ -212,16 +212,14 @@ impl<T: Scalar, const D: usize> From<[T; D]> for Translation<T, D> {
|
||||
impl<T: Scalar, const D: usize> From<Point<T, D>> for Translation<T, D> {
|
||||
#[inline]
|
||||
fn from(pt: Point<T, D>) -> Self {
|
||||
Translation {
|
||||
vector: pt.coords.into(),
|
||||
}
|
||||
Translation { vector: pt.coords }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, const D: usize> Into<[T; D]> for Translation<T, D> {
|
||||
impl<T: Scalar, const D: usize> From<Translation<T, D>> for [T; D] {
|
||||
#[inline]
|
||||
fn into(self) -> [T; D] {
|
||||
self.vector.into()
|
||||
fn from(t: Translation<T, D>) -> Self {
|
||||
t.vector.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB};
|
||||
@ -19,15 +18,14 @@ macro_rules! deref_impl(
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { mem::transmute(self) }
|
||||
unsafe { &*(self as *const Translation<T, $D> as *const Self::Target) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar> DerefMut for Translation<T, $D>
|
||||
{
|
||||
impl<T: Scalar> DerefMut for Translation<T, $D> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { mem::transmute(self) }
|
||||
unsafe { &mut *(self as *mut Translation<T, $D> as *mut Self::Target) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ where
|
||||
/// assert_eq!(rot.angle(), 1.78);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn angle(&self) -> T {
|
||||
self.im.simd_atan2(self.re)
|
||||
}
|
||||
@ -98,6 +99,7 @@ where
|
||||
/// assert_eq!(rot.sin_angle(), angle.sin());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sin_angle(&self) -> T {
|
||||
self.im
|
||||
}
|
||||
@ -112,6 +114,7 @@ where
|
||||
/// assert_eq!(rot.cos_angle(),angle.cos());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cos_angle(&self) -> T {
|
||||
self.re
|
||||
}
|
||||
@ -121,6 +124,7 @@ where
|
||||
/// This is generally used in the context of generic programming. Using
|
||||
/// the `.angle()` method instead is more common.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn scaled_axis(&self) -> Vector1<T> {
|
||||
Vector1::new(self.angle())
|
||||
}
|
||||
@ -131,6 +135,7 @@ where
|
||||
/// the `.angle()` method instead is more common.
|
||||
/// Returns `None` if the angle is zero.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn axis_angle(&self) -> Option<(Unit<Vector1<T>>, T)>
|
||||
where
|
||||
T: RealField,
|
||||
@ -157,6 +162,7 @@ where
|
||||
/// assert_relative_eq!(rot1.angle_to(&rot2), 1.6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn angle_to(&self, other: &Self) -> T {
|
||||
let delta = self.rotation_to(other);
|
||||
delta.angle()
|
||||
@ -254,7 +260,8 @@ where
|
||||
/// assert_eq!(rot.to_rotation_matrix(), expected);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_rotation_matrix(&self) -> Rotation2<T> {
|
||||
#[must_use]
|
||||
pub fn to_rotation_matrix(self) -> Rotation2<T> {
|
||||
let r = self.re;
|
||||
let i = self.im;
|
||||
|
||||
@ -274,7 +281,8 @@ where
|
||||
/// assert_eq!(rot.to_homogeneous(), expected);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_homogeneous(&self) -> Matrix3<T> {
|
||||
#[must_use]
|
||||
pub fn to_homogeneous(self) -> Matrix3<T> {
|
||||
self.to_rotation_matrix().to_homogeneous()
|
||||
}
|
||||
}
|
||||
@ -298,6 +306,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point2::new(-2.0, 1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, pt: &Point2<T>) -> Point2<T> {
|
||||
self * pt
|
||||
}
|
||||
@ -316,6 +325,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, Vector2::new(-2.0, 1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, v: &Vector2<T>) -> Vector2<T> {
|
||||
self * v
|
||||
}
|
||||
@ -332,6 +342,7 @@ where
|
||||
/// assert_relative_eq!(transformed_point, Point2::new(2.0, -1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[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
|
||||
// the inverse transformation explicitly here) ?
|
||||
@ -350,6 +361,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, Vector2::new(2.0, -1.0), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_vector(&self, v: &Vector2<T>) -> Vector2<T> {
|
||||
self.inverse() * v
|
||||
}
|
||||
@ -366,6 +378,7 @@ where
|
||||
/// assert_relative_eq!(transformed_vector, -Vector2::y_axis(), epsilon = 1.0e-6);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector2<T>>) -> Unit<Vector2<T>> {
|
||||
self.inverse() * v
|
||||
}
|
||||
@ -392,6 +405,7 @@ where
|
||||
/// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn slerp(&self, other: &Self, t: T) -> Self {
|
||||
Self::new(self.angle() * (T::one() - t) + other.angle() * t)
|
||||
}
|
||||
|
@ -148,6 +148,7 @@ where
|
||||
/// assert_eq!(*rot.complex(), Complex::new(angle.cos(), angle.sin()));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn complex(&self) -> &Complex<T> {
|
||||
self.as_ref()
|
||||
}
|
||||
@ -244,6 +245,7 @@ where
|
||||
/// assert_relative_eq!(rot_to.inverse() * rot2, rot1);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn rotation_to(&self, other: &Self) -> Self {
|
||||
other / self
|
||||
}
|
||||
@ -262,6 +264,7 @@ where
|
||||
/// assert_relative_eq!(pow.angle(), 2.0 * 0.78);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn powf(&self, n: T) -> Self {
|
||||
Self::from_angle(self.angle() * n)
|
||||
}
|
||||
@ -380,8 +383,8 @@ where
|
||||
SB: Storage<T, U2>,
|
||||
SC: Storage<T, U2>,
|
||||
{
|
||||
let sang = na.perp(&nb);
|
||||
let cang = na.dot(&nb);
|
||||
let sang = na.perp(nb);
|
||||
let cang = na.dot(nb);
|
||||
|
||||
Self::from_angle(sang.simd_atan2(cang) * s)
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
// The macros break if the references are taken out, for some reason.
|
||||
#![allow(clippy::op_ref)]
|
||||
|
||||
use std::ops::{Div, DivAssign, Mul, MulAssign};
|
||||
|
||||
use crate::base::storage::Storage;
|
||||
@ -314,10 +317,10 @@ complex_op_impl_all!(
|
||||
;
|
||||
self: Translation<T, 2>, right: UnitComplex<T>,
|
||||
Output = Isometry<T, UnitComplex<T>, 2>;
|
||||
[val val] => Isometry::from_parts(self, right);
|
||||
[ref val] => Isometry::from_parts(self.clone(), right);
|
||||
[val ref] => Isometry::from_parts(self, *right);
|
||||
[ref ref] => Isometry::from_parts(self.clone(), *right);
|
||||
[val val] => Isometry::from_parts(self, right);
|
||||
[ref val] => Isometry::from_parts(*self, right);
|
||||
[val ref] => Isometry::from_parts(self, *right);
|
||||
[ref ref] => Isometry::from_parts(*self, *right);
|
||||
);
|
||||
|
||||
// UnitComplex ×= UnitComplex
|
||||
@ -327,7 +330,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: UnitComplex<T>) {
|
||||
*self = &*self * rhs
|
||||
*self = *self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +340,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: &'b UnitComplex<T>) {
|
||||
*self = &*self * rhs
|
||||
*self = *self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,7 +351,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn div_assign(&mut self, rhs: UnitComplex<T>) {
|
||||
*self = &*self / rhs
|
||||
*self = *self / rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,7 +361,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn div_assign(&mut self, rhs: &'b UnitComplex<T>) {
|
||||
*self = &*self / rhs
|
||||
*self = *self / rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,7 +372,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Rotation<T, 2>) {
|
||||
*self = &*self * rhs
|
||||
*self = *self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,7 +382,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: &'b Rotation<T, 2>) {
|
||||
*self = &*self * rhs
|
||||
*self = *self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,7 +393,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn div_assign(&mut self, rhs: Rotation<T, 2>) {
|
||||
*self = &*self / rhs
|
||||
*self = *self / rhs
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,7 +403,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn div_assign(&mut self, rhs: &'b Rotation<T, 2>) {
|
||||
*self = &*self / rhs
|
||||
*self = *self / rhs
|
||||
}
|
||||
}
|
||||
|
||||
|
23
src/lib.rs
23
src/lib.rs
@ -1,3 +1,4 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
/*!
|
||||
# nalgebra
|
||||
|
||||
@ -13,7 +14,7 @@ and the official package manager: [cargo](https://github.com/rust-lang/cargo).
|
||||
|
||||
Simply add the following to your `Cargo.toml` file:
|
||||
|
||||
```.ignore
|
||||
```ignore
|
||||
[dependencies]
|
||||
// TODO: replace the * by the latest version.
|
||||
nalgebra = "*"
|
||||
@ -25,7 +26,7 @@ Most useful functionalities of **nalgebra** are grouped in the root module `nalg
|
||||
However, the recommended way to use **nalgebra** is to import types and traits
|
||||
explicitly, and call free-functions using the `na::` prefix:
|
||||
|
||||
```.rust
|
||||
```
|
||||
#[macro_use]
|
||||
extern crate approx; // For the macro relative_eq!
|
||||
extern crate nalgebra as na;
|
||||
@ -86,7 +87,6 @@ an optimized set of tools for computer graphics and physics. Those features incl
|
||||
html_root_url = "https://docs.rs/nalgebra/0.25.0"
|
||||
)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))]
|
||||
#![cfg_attr(feature = "no_unsound_assume_init", allow(unreachable_code))]
|
||||
|
||||
#[cfg(feature = "rand-no-std")]
|
||||
@ -101,6 +101,7 @@ extern crate approx;
|
||||
extern crate num_traits as num;
|
||||
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
@ -184,6 +185,7 @@ pub fn zero<T: Zero>() -> T {
|
||||
/// Wraps `val` into the range `[min, max]` using modular arithmetics.
|
||||
///
|
||||
/// The range must not be empty.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn wrap<T>(mut val: T, min: T, max: T) -> T
|
||||
where
|
||||
@ -198,19 +200,15 @@ where
|
||||
while val < min {
|
||||
val += width
|
||||
}
|
||||
|
||||
val
|
||||
} else if val > max {
|
||||
val -= width;
|
||||
|
||||
while val > max {
|
||||
val -= width
|
||||
}
|
||||
|
||||
val
|
||||
} else {
|
||||
val
|
||||
}
|
||||
|
||||
val
|
||||
}
|
||||
|
||||
/// Returns a reference to the input value clamped to the interval `[min, max]`.
|
||||
@ -219,6 +217,7 @@ where
|
||||
/// * If `min < val < max`, this returns `val`.
|
||||
/// * If `val <= min`, this returns `min`.
|
||||
/// * If `val >= max`, this returns `max`.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn clamp<T: PartialOrd>(val: T, min: T, max: T) -> T {
|
||||
if val > min {
|
||||
@ -391,7 +390,7 @@ pub fn center<T: SimdComplexField, const D: usize>(
|
||||
p1: &Point<T, D>,
|
||||
p2: &Point<T, D>,
|
||||
) -> Point<T, D> {
|
||||
((&p1.coords + &p2.coords) * convert::<_, T>(0.5)).into()
|
||||
((p1.coords + p2.coords) * convert::<_, T>(0.5)).into()
|
||||
}
|
||||
|
||||
/// The distance between two points.
|
||||
@ -405,7 +404,7 @@ pub fn distance<T: SimdComplexField, const D: usize>(
|
||||
p1: &Point<T, D>,
|
||||
p2: &Point<T, D>,
|
||||
) -> T::SimdRealField {
|
||||
(&p2.coords - &p1.coords).norm()
|
||||
(p2.coords - p1.coords).norm()
|
||||
}
|
||||
|
||||
/// The squared distance between two points.
|
||||
@ -419,7 +418,7 @@ pub fn distance_squared<T: SimdComplexField, const D: usize>(
|
||||
p1: &Point<T, D>,
|
||||
p2: &Point<T, D>,
|
||||
) -> T::SimdRealField {
|
||||
(&p2.coords - &p1.coords).norm_squared()
|
||||
(p2.coords - p1.coords).norm_squared()
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -8,17 +8,17 @@ use crate::base::dimension::Dim;
|
||||
use crate::base::storage::Storage;
|
||||
use crate::base::{Const, DefaultAllocator, OMatrix, OVector};
|
||||
|
||||
/// Applies in-place a modified Parlett and Reinsch matrix balancing with 2-norm to the matrix `m` and returns
|
||||
/// Applies in-place a modified Parlett and Reinsch matrix balancing with 2-norm to the matrix and returns
|
||||
/// the corresponding diagonal transformation.
|
||||
///
|
||||
/// See https://arxiv.org/pdf/1401.5766.pdf
|
||||
pub fn balance_parlett_reinsch<T: RealField, D: Dim>(m: &mut OMatrix<T, D, D>) -> OVector<T, D>
|
||||
pub fn balance_parlett_reinsch<T: RealField, D: Dim>(matrix: &mut OMatrix<T, D, D>) -> OVector<T, D>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D, D> + Allocator<T, D>,
|
||||
{
|
||||
assert!(m.is_square(), "Unable to balance a non-square matrix.");
|
||||
assert!(matrix.is_square(), "Unable to balance a non-square matrix.");
|
||||
|
||||
let dim = m.data.shape().0;
|
||||
let dim = matrix.data.shape().0;
|
||||
let radix: T = crate::convert(2.0f64);
|
||||
let mut d = OVector::from_element_generic(dim, Const::<1>, T::one());
|
||||
|
||||
@ -28,36 +28,37 @@ where
|
||||
converged = true;
|
||||
|
||||
for i in 0..dim.value() {
|
||||
let mut c = m.column(i).norm_squared();
|
||||
let mut r = m.row(i).norm_squared();
|
||||
let mut n_col = matrix.column(i).norm_squared();
|
||||
let mut n_row = matrix.row(i).norm_squared();
|
||||
let mut f = T::one();
|
||||
|
||||
let s = c + r;
|
||||
c = c.sqrt();
|
||||
r = r.sqrt();
|
||||
let s = n_col + n_row;
|
||||
n_col = n_col.sqrt();
|
||||
n_row = n_row.sqrt();
|
||||
|
||||
if c.is_zero() || r.is_zero() {
|
||||
if n_col.is_zero() || n_row.is_zero() {
|
||||
continue;
|
||||
}
|
||||
|
||||
while c < r / radix {
|
||||
c *= radix;
|
||||
r /= radix;
|
||||
while n_col < n_row / radix {
|
||||
n_col *= radix;
|
||||
n_row /= radix;
|
||||
f *= radix;
|
||||
}
|
||||
|
||||
while c >= r * radix {
|
||||
c /= radix;
|
||||
r *= radix;
|
||||
while n_col >= n_row * radix {
|
||||
n_col /= radix;
|
||||
n_row *= radix;
|
||||
f /= radix;
|
||||
}
|
||||
|
||||
let eps: T = crate::convert(0.95);
|
||||
if c * c + r * r < eps * s {
|
||||
#[allow(clippy::suspicious_operation_groupings)]
|
||||
if n_col * n_col + n_row * n_row < eps * s {
|
||||
converged = false;
|
||||
d[i] *= f;
|
||||
m.column_mut(i).mul_assign(f);
|
||||
m.row_mut(i).div_assign(f);
|
||||
matrix.column_mut(i).mul_assign(f);
|
||||
matrix.row_mut(i).div_assign(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +153,7 @@ where
|
||||
|
||||
/// Indicates whether this decomposition contains an upper-diagonal matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_upper_diagonal(&self) -> bool {
|
||||
self.upper_diagonal
|
||||
}
|
||||
@ -188,6 +189,7 @@ where
|
||||
|
||||
/// Retrieves the upper trapezoidal submatrix `R` of this decomposition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn d(&self) -> OMatrix<T, DimMinimum<R, C>, DimMinimum<R, C>>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, DimMinimum<R, C>, DimMinimum<R, C>>,
|
||||
@ -207,6 +209,7 @@ where
|
||||
/// Computes the orthogonal matrix `U` of this `U * D * V` decomposition.
|
||||
// TODO: code duplication with householder::assemble_q.
|
||||
// Except that we are returning a rectangular matrix here.
|
||||
#[must_use]
|
||||
pub fn u(&self) -> OMatrix<T, R, DimMinimum<R, C>>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, R, DimMinimum<R, C>>,
|
||||
@ -237,6 +240,7 @@ where
|
||||
}
|
||||
|
||||
/// Computes the orthogonal matrix `V_t` of this `U * D * V_t` decomposition.
|
||||
#[must_use]
|
||||
pub fn v_t(&self) -> OMatrix<T, DimMinimum<R, C>, C>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, DimMinimum<R, C>, C>,
|
||||
@ -274,6 +278,7 @@ where
|
||||
}
|
||||
|
||||
/// The diagonal part of this decomposed matrix.
|
||||
#[must_use]
|
||||
pub fn diagonal(&self) -> OVector<T::RealField, DimMinimum<R, C>>
|
||||
where
|
||||
DefaultAllocator: Allocator<T::RealField, DimMinimum<R, C>>,
|
||||
@ -282,6 +287,7 @@ where
|
||||
}
|
||||
|
||||
/// The off-diagonal part of this decomposed matrix.
|
||||
#[must_use]
|
||||
pub fn off_diagonal(&self) -> OVector<T::RealField, DimDiff<DimMinimum<R, C>, U1>>
|
||||
where
|
||||
DefaultAllocator: Allocator<T::RealField, DimDiff<DimMinimum<R, C>, U1>>,
|
||||
|
@ -92,6 +92,7 @@ where
|
||||
|
||||
/// Retrieves the lower-triangular factor of the Cholesky decomposition with its strictly
|
||||
/// uppen-triangular part filled with zeros.
|
||||
#[must_use]
|
||||
pub fn l(&self) -> OMatrix<T, D, D> {
|
||||
self.chol.lower_triangle()
|
||||
}
|
||||
@ -101,6 +102,7 @@ where
|
||||
///
|
||||
/// This is an allocation-less version of `self.l()`. The values of the strict upper-triangular
|
||||
/// part are garbage and should be ignored by further computations.
|
||||
#[must_use]
|
||||
pub fn l_dirty(&self) -> &OMatrix<T, D, D> {
|
||||
&self.chol
|
||||
}
|
||||
@ -119,6 +121,7 @@ where
|
||||
|
||||
/// Returns the solution of the system `self * x = b` where `self` is the decomposed matrix and
|
||||
/// `x` the unknown.
|
||||
#[must_use = "Did you mean to use solve_mut()?"]
|
||||
pub fn solve<R2: Dim, C2: Dim, S2>(&self, b: &Matrix<T, R2, C2, S2>) -> OMatrix<T, R2, C2>
|
||||
where
|
||||
S2: Storage<T, R2, C2>,
|
||||
@ -131,6 +134,7 @@ where
|
||||
}
|
||||
|
||||
/// Computes the inverse of the decomposed matrix.
|
||||
#[must_use]
|
||||
pub fn inverse(&self) -> OMatrix<T, D, D> {
|
||||
let shape = self.chol.data.shape();
|
||||
let mut res = OMatrix::identity_generic(shape.0, shape.1);
|
||||
@ -140,6 +144,7 @@ where
|
||||
}
|
||||
|
||||
/// Computes the determinant of the decomposed matrix.
|
||||
#[must_use]
|
||||
pub fn determinant(&self) -> T::SimdRealField {
|
||||
let dim = self.chol.nrows();
|
||||
let mut prod_diag = T::one();
|
||||
@ -287,6 +292,7 @@ where
|
||||
|
||||
/// Updates the decomposition such that we get the decomposition of the factored matrix with its `j`th column removed.
|
||||
/// Since the matrix is square, the `j`th row will also be removed.
|
||||
#[must_use]
|
||||
pub fn remove_column(&self, j: usize) -> Cholesky<T, DimDiff<D, U1>>
|
||||
where
|
||||
D: DimSub<U1>,
|
||||
|
@ -95,6 +95,7 @@ where
|
||||
|
||||
/// Retrieves the upper trapezoidal submatrix `R` of this decomposition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn r(&self) -> OMatrix<T, DimMinimum<R, C>, C>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, DimMinimum<R, C>, C>,
|
||||
@ -126,6 +127,7 @@ where
|
||||
}
|
||||
|
||||
/// Computes the orthogonal matrix `Q` of this decomposition.
|
||||
#[must_use]
|
||||
pub fn q(&self) -> OMatrix<T, R, DimMinimum<R, C>>
|
||||
where
|
||||
DefaultAllocator: Allocator<T, R, DimMinimum<R, C>>,
|
||||
@ -150,6 +152,7 @@ where
|
||||
}
|
||||
/// Retrieves the column permutation of this decomposition.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn p(&self) -> &PermutationSequence<DimMinimum<R, C>> {
|
||||
&self.p
|
||||
}
|
||||
@ -201,6 +204,7 @@ where
|
||||
/// Solves the linear system `self * x = b`, where `x` is the unknown to be determined.
|
||||
///
|
||||
/// Returns `None` if `self` is not invertible.
|
||||
#[must_use = "Did you mean to use solve_mut()?"]
|
||||
pub fn solve<R2: Dim, C2: Dim, S2>(
|
||||
&self,
|
||||
b: &Matrix<T, R2, C2, S2>,
|
||||
@ -283,6 +287,7 @@ where
|
||||
/// Computes the inverse of the decomposed matrix.
|
||||
///
|
||||
/// Returns `None` if the decomposed matrix is not invertible.
|
||||
#[must_use]
|
||||
pub fn try_inverse(&self) -> Option<OMatrix<T, D, D>> {
|
||||
assert!(
|
||||
self.col_piv_qr.is_square(),
|
||||
@ -301,6 +306,7 @@ where
|
||||
}
|
||||
|
||||
/// Indicates if the decomposed matrix is invertible.
|
||||
#[must_use]
|
||||
pub fn is_invertible(&self) -> bool {
|
||||
assert!(
|
||||
self.col_piv_qr.is_square(),
|
||||
@ -317,6 +323,7 @@ where
|
||||
}
|
||||
|
||||
/// Computes the determinant of the decomposed matrix.
|
||||
#[must_use]
|
||||
pub fn determinant(&self) -> T {
|
||||
let dim = self.col_piv_qr.nrows();
|
||||
assert!(
|
||||
|
@ -112,6 +112,7 @@ impl<T: RealField, D1: Dim, S1: Storage<T, D1>> Vector<T, D1, S1> {
|
||||
///
|
||||
/// # Errors
|
||||
/// Inputs must satisfy `self.len() >= kernel.len() > 0`.
|
||||
#[must_use]
|
||||
pub fn convolve_same<D2, S2>(&self, kernel: Vector<T, D2, S2>) -> OVector<T, D1>
|
||||
where
|
||||
D2: Dim,
|
||||
|
@ -12,6 +12,7 @@ impl<T: ComplexField, D: DimMin<D, Output = D>, S: Storage<T, D, D>> SquareMatri
|
||||
///
|
||||
/// If the matrix has a dimension larger than 3, an LU decomposition is used.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn determinant(&self) -> T
|
||||
where
|
||||
DefaultAllocator: Allocator<T, D, D> + Allocator<(usize, usize), D>,
|
||||
|
@ -435,37 +435,38 @@ where
|
||||
+ Allocator<T::RealField, D, D>,
|
||||
{
|
||||
/// Computes exponential of this matrix
|
||||
#[must_use]
|
||||
pub fn exp(&self) -> Self {
|
||||
// Simple case
|
||||
if self.nrows() == 1 {
|
||||
return self.map(|v| v.exp());
|
||||
}
|
||||
|
||||
let mut h = ExpmPadeHelper::new(self.clone(), true);
|
||||
let mut helper = ExpmPadeHelper::new(self.clone(), true);
|
||||
|
||||
let eta_1 = T::RealField::max(h.d4_loose(), h.d6_loose());
|
||||
if eta_1 < convert(1.495_585_217_958_292e-2) && ell(&h.a, 3) == 0 {
|
||||
let (u, v) = h.pade3();
|
||||
let eta_1 = T::RealField::max(helper.d4_loose(), helper.d6_loose());
|
||||
if eta_1 < convert(1.495_585_217_958_292e-2) && ell(&helper.a, 3) == 0 {
|
||||
let (u, v) = helper.pade3();
|
||||
return solve_p_q(u, v);
|
||||
}
|
||||
|
||||
let eta_2 = T::RealField::max(h.d4_tight(), h.d6_loose());
|
||||
if eta_2 < convert(2.539_398_330_063_230e-1) && ell(&h.a, 5) == 0 {
|
||||
let (u, v) = h.pade5();
|
||||
let eta_2 = T::RealField::max(helper.d4_tight(), helper.d6_loose());
|
||||
if eta_2 < convert(2.539_398_330_063_23e-1) && ell(&helper.a, 5) == 0 {
|
||||
let (u, v) = helper.pade5();
|
||||
return solve_p_q(u, v);
|
||||
}
|
||||
|
||||
let eta_3 = T::RealField::max(h.d6_tight(), h.d8_loose());
|
||||
if eta_3 < convert(9.504_178_996_162_932e-1) && ell(&h.a, 7) == 0 {
|
||||
let (u, v) = h.pade7();
|
||||
let eta_3 = T::RealField::max(helper.d6_tight(), helper.d8_loose());
|
||||
if eta_3 < convert(9.504_178_996_162_932e-1) && ell(&helper.a, 7) == 0 {
|
||||
let (u, v) = helper.pade7();
|
||||
return solve_p_q(u, v);
|
||||
}
|
||||
if eta_3 < convert(2.097_847_961_257_068e+0) && ell(&h.a, 9) == 0 {
|
||||
let (u, v) = h.pade9();
|
||||
if eta_3 < convert(2.097_847_961_257_068e0) && ell(&helper.a, 9) == 0 {
|
||||
let (u, v) = helper.pade9();
|
||||
return solve_p_q(u, v);
|
||||
}
|
||||
|
||||
let eta_4 = T::RealField::max(h.d8_loose(), h.d10_loose());
|
||||
let eta_4 = T::RealField::max(helper.d8_loose(), helper.d10_loose());
|
||||
let eta_5 = T::RealField::min(eta_3, eta_4);
|
||||
let theta_13 = convert(4.25);
|
||||
|
||||
@ -481,9 +482,12 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
s += ell(&(&h.a * convert::<f64, T>(2.0_f64.powf(-(s as f64)))), 13);
|
||||
s += ell(
|
||||
&(&helper.a * convert::<f64, T>(2.0_f64.powf(-(s as f64)))),
|
||||
13,
|
||||
);
|
||||
|
||||
let (u, v) = h.pade13_scaled(s);
|
||||
let (u, v) = helper.pade13_scaled(s);
|
||||
let mut x = solve_p_q(u, v);
|
||||
|
||||
for _ in 0..s {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user