Merge pull request #945 from dimforge/dev

Release v0.28.0
This commit is contained in:
Sébastien Crozet 2021-07-11 18:11:45 +02:00 committed by GitHub
commit 944afe24e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
129 changed files with 1732 additions and 804 deletions

View File

@ -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,7 +145,7 @@ 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.
@ -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.
@ -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

View File

@ -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."

View File

@ -4,7 +4,7 @@ version = "0.0.0"
authors = [ "You" ]
[dependencies]
nalgebra = "0.27.0"
nalgebra = "0.28.0"
[[bin]]
name = "example"

View File

@ -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 }

View File

@ -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;
```

View File

@ -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"

View File

@ -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
}

View File

@ -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() {

View File

@ -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();

View File

@ -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}
//! ```

View File

@ -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
}

View File

@ -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);

View File

@ -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>,

View File

@ -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;

View File

@ -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() {

View File

@ -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"

View File

@ -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

View File

@ -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;
//!

View File

@ -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
}

View File

@ -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,

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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();

View File

@ -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
}

View File

@ -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),

View File

@ -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()
}

View File

@ -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()];

View File

@ -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));
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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,8 +346,11 @@ 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());
@ -350,6 +358,7 @@ where
array_axc(y, a, x, c, rstride1, rstride2, x.len());
}
}
}
/// Computes `self = a * x + b * self`.
///
@ -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());
}
}

View File

@ -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);

View File

@ -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,

View File

@ -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(

View File

@ -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]

View File

@ -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

View File

@ -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) }
}
}
}

View File

@ -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;

View File

@ -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 }
}
}

View File

@ -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.

View File

@ -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>,

View File

@ -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>>,

View File

@ -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))
}
}

View File

@ -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,6 +1896,15 @@ impl_fmt!(fmt::UpperHex, "{:X}", "{:1$X}");
impl_fmt!(fmt::Binary, "{:b}", "{:.1$b}");
impl_fmt!(fmt::Pointer, "{:p}", "{:.1$p}");
#[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.);
@ -1884,6 +1919,7 @@ fn lower_exp() {
"
)
}
}
/// # Cross product
impl<T: Scalar + ClosedAdd + ClosedSub + ClosedMul, R: Dim, C: Dim, S: Storage<T, R, C>>
@ -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>,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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 {
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 {
} else {
for j in 0 .. self.ncols() {
for i in 0 .. self.nrows() {
unsafe {
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 {
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 {
} else {
for j in 0 .. rhs.ncols() {
for i in 0 .. rhs.nrows() {
unsafe {
self.get_unchecked_mut((i, j)).$method_assign(rhs.get_unchecked((i, j)).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 {
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 {
} else {
for j in 0 .. self.ncols() {
for i in 0 .. self.nrows() {
unsafe {
let r = rhs.get_unchecked_mut((i, j));
*r = self.get_unchecked((i, j)).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>,

View File

@ -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()

View File

@ -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>,

View File

@ -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);
@ -166,7 +205,12 @@ 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];
///
/// # 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.

View File

@ -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()),*)

View File

@ -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) }
}
}

View File

@ -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[..]
}
}

View File

@ -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()
}
}

View File

@ -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,
})
}

View File

@ -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!(

View File

@ -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>,

View File

@ -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())
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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);
);

View File

@ -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>,

View File

@ -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());

View File

@ -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, "{{")?;

View File

@ -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.
///

View File

@ -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),*) }
}
}

View File

@ -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 }
}
}

View File

@ -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);

View File

@ -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)
}
}
)*}

View File

@ -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();

View File

@ -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.

View File

@ -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) }
}
}

View File

@ -1,3 +1,6 @@
// The macros break if the references are taken out, for some reason.
#![allow(clippy::op_ref)]
/*
* This file provides:
* ===================

View File

@ -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);
}
}

View File

@ -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))
}

View File

@ -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(),
]))
}
}

View File

@ -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())
}
}

View File

@ -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,

View File

@ -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>,

View File

@ -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(),

View File

@ -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!(

View File

@ -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()),*)

View File

@ -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
}

View File

@ -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);

View File

@ -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
}

View File

@ -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>>())
}
}

View File

@ -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()
}
}

View File

@ -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) }
}
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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;
@ -315,9 +318,9 @@ 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);
[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);
);
// 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
}
}

View File

@ -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
}
}
/// 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()
}
/*

View File

@ -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);
}
}
}

View File

@ -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>>,

View File

@ -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>,

View File

@ -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!(

View File

@ -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,

View File

@ -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>,

View File

@ -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