nalgebra/src/lib.rs

549 lines
15 KiB
Rust
Raw Normal View History

2017-02-13 01:17:09 +08:00
/*!
# nalgebra
**nalgebra** is a linear algebra library written for Rust targeting:
* General-purpose linear algebra (still lacks a lot of features)
* Real-time computer graphics.
* Real-time computer physics.
2017-02-13 01:17:09 +08:00
## Using **nalgebra**
You will need the last stable build of the [rust compiler](https://www.rust-lang.org)
2017-02-13 01:17:09 +08:00
and the official package manager: [cargo](https://github.com/rust-lang/cargo).
Simply add the following to your `Cargo.toml` file:
2021-07-07 10:05:25 +08:00
```ignore
2017-02-13 01:17:09 +08:00
[dependencies]
2021-01-29 20:56:40 +08:00
// TODO: replace the * by the latest version.
nalgebra = "*"
2017-02-13 01:17:09 +08:00
```
Most useful functionalities of **nalgebra** are grouped in the root module `nalgebra::`.
However, the recommended way to use **nalgebra** is to import types and traits
explicitly, and call free-functions using the `na::` prefix:
2021-07-07 10:05:25 +08:00
```
2017-02-13 01:17:09 +08:00
#[macro_use]
extern crate approx; // For the macro relative_eq!
extern crate nalgebra as na;
use na::{Vector3, Rotation3};
fn main() {
let axis = Vector3::x_axis();
let angle = 1.57;
let b = Rotation3::from_axis_angle(&axis, angle);
relative_eq!(b.axis().unwrap(), axis);
relative_eq!(b.angle(), angle);
}
```
## Features
**nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra library, with
an optimized set of tools for computer graphics and physics. Those features include:
* A single parametrizable type [`Matrix`](Matrix) for vectors, (square or rectangular) matrices, and
slices with dimensions known either at compile-time (using type-level integers) or at runtime.
2017-02-13 01:17:09 +08:00
* Matrices and vectors with compile-time sizes are statically allocated while dynamic ones are
allocated on the heap.
* Convenient aliases for low-dimensional matrices and vectors: [`Vector1`](Vector1) to
[`Vector6`](Vector6) and [`Matrix1x1`](Matrix1) to [`Matrix6x6`](Matrix6), including rectangular
matrices like [`Matrix2x5`](Matrix2x5).
* Points sizes known at compile time, and convenience aliases: [`Point1`](Point1) to
[`Point6`](Point6).
* Translation (seen as a transformation that composes by multiplication):
[`Translation2`](Translation2), [`Translation3`](Translation3).
* Rotation matrices: [`Rotation2`](Rotation2), [`Rotation3`](Rotation3).
* Quaternions: [`Quaternion`](Quaternion), [`UnitQuaternion`](UnitQuaternion) (for 3D rotation).
* Unit complex numbers can be used for 2D rotation: [`UnitComplex`](UnitComplex).
* Algebraic entities with a norm equal to one: [`Unit<T>`](Unit), e.g., `Unit<Vector3<f32>>`.
* Isometries (translation rotation): [`Isometry2`](Isometry2), [`Isometry3`](Isometry3)
* Similarity transformations (translation rotation uniform scale):
[`Similarity2`](Similarity2), [`Similarity3`](Similarity3).
* Affine transformations stored as a homogeneous matrix:
[`Affine2`](Affine2), [`Affine3`](Affine3).
* Projective (i.e. invertible) transformations stored as a homogeneous matrix:
[`Projective2`](Projective2), [`Projective3`](Projective3).
2020-02-23 23:30:11 +08:00
* General transformations that does not have to be invertible, stored as a homogeneous matrix:
[`Transform2`](Transform2), [`Transform3`](Transform3).
* 3D projections for computer graphics: [`Perspective3`](Perspective3),
[`Orthographic3`](Orthographic3).
* Matrix factorizations: [`Cholesky`](Cholesky), [`QR`](QR), [`LU`](LU), [`FullPivLU`](FullPivLU),
[`SVD`](SVD), [`Schur`](Schur), [`Hessenberg`](Hessenberg), [`SymmetricEigen`](SymmetricEigen).
* Insertion and removal of rows of columns of a matrix.
2017-02-13 01:17:09 +08:00
*/
2021-07-28 07:18:29 +08:00
#![deny(
2021-08-03 23:26:56 +08:00
missing_docs,
2021-07-28 07:18:29 +08:00
nonstandard_style,
2021-09-26 03:05:31 +08:00
unused_variables,
unused_mut,
2021-07-28 07:18:29 +08:00
unused_parens,
rust_2018_idioms,
rust_2018_compatibility,
future_incompatible,
missing_copy_implementations
)]
2023-01-13 17:28:22 +08:00
#![cfg_attr(not(feature = "rkyv-serialize-no-std"), deny(unused_results))] // TODO: deny this globally once bytecheck stops generating unused results.
#![doc(
html_favicon_url = "https://nalgebra.org/img/favicon.ico",
2021-03-01 21:25:29 +08:00
html_root_url = "https://docs.rs/nalgebra/0.25.0"
)]
#![cfg_attr(not(feature = "std"), no_std)]
/// Generates an appropriate deprecation note with a suggestion for replacement.
///
/// Used for deprecating slice types in various locations throughout the library.
/// See #1076 for more information.
macro_rules! slice_deprecation_note {
($replacement:ident) => {
concat!("Use ", stringify!($replacement),
r###" instead. See [issue #1076](https://github.com/dimforge/nalgebra/issues/1076) for more information."###)
}
}
pub(crate) use slice_deprecation_note;
2021-03-02 19:25:12 +08:00
#[cfg(feature = "rand-no-std")]
extern crate rand_package as rand;
2017-07-25 23:04:12 +08:00
#[cfg(feature = "serde-serialize-no-std")]
#[macro_use]
extern crate serde;
#[macro_use]
extern crate approx;
extern crate num_traits as num;
2018-05-24 23:17:34 +08:00
#[cfg(all(feature = "alloc", not(feature = "std")))]
#[cfg_attr(test, macro_use)]
2018-05-24 23:17:34 +08:00
extern crate alloc;
#[cfg(not(feature = "std"))]
extern crate core as std;
2018-11-07 01:32:20 +08:00
#[macro_use]
#[cfg(feature = "io")]
extern crate pest_derive;
pub mod base;
#[cfg(feature = "debug")]
pub mod debug;
pub mod geometry;
2018-11-07 01:32:20 +08:00
#[cfg(feature = "io")]
pub mod io;
pub mod linalg;
#[cfg(feature = "proptest-support")]
pub mod proptest;
#[cfg(feature = "sparse")]
pub mod sparse;
mod third_party;
2019-03-23 21:29:07 +08:00
pub use crate::base::*;
pub use crate::geometry::*;
pub use crate::linalg::*;
#[cfg(feature = "sparse")]
2019-03-23 21:29:07 +08:00
pub use crate::sparse::*;
#[cfg(feature = "std")]
#[deprecated(
note = "The 'core' module is being renamed to 'base' to avoid conflicts with the 'core' crate."
)]
pub use base as core;
#[cfg(feature = "macros")]
pub use nalgebra_macros::{dmatrix, dvector, matrix, point, vector};
2020-03-21 19:16:46 +08:00
use simba::scalar::SupersetOf;
2018-02-02 19:26:35 +08:00
use std::cmp::{self, Ordering, PartialOrd};
Api change: deal with inplace/out of place methods. Before, it was too easy to use an out of place method instead of the inplace one since they name were pretty mutch the same. This kind of confusion may lead to silly bugs very hard to understand. Thus the following changes have been made when a method is available both inplace and out-of-place: * inplace version keep a short name. * out-of-place version are suffixed by `_cpy` (meaning `copy`), and are static methods. Methods applying transformations (rotation, translation or general transform) are now prefixed by `append`, and a `prepend` version is available too. Also, free functions doing in-place modifications dont really make sense. They have been removed. Here are the naming changes: * `invert` -> `inv` * `inverted` -> `Inv::inv_cpy` * `transpose` -> `transpose` * `transposed` -> `Transpose::transpose_cpy` * `transform_by` -> `append_transformation` * `transformed` -> `Transform::append_transformation_cpy` * `rotate_by` -> `apppend_rotation` * `rotated` -> `Rotation::append_rotation_cpy` * `translate_by` -> `apppend_translation` * `translate` -> `Translation::append_translation_cpy` * `normalized` -> `Norm::normalize_cpy` * `rotated_wrt_point` -> `RotationWithTranslation::append_rotation_wrt_point_cpy` * `rotated_wrt_center` -> `RotationWithTranslation::append_rotation_wrt_center_cpy` Note that using those static methods is very verbose, and using in-place methods require an explicit import of the related trait. This is a way to convince the user to use free functions most of the time.
2013-10-14 16:22:32 +08:00
2020-03-21 19:16:46 +08:00
use num::{One, Signed, Zero};
2017-02-13 01:17:09 +08:00
2020-03-21 19:16:46 +08:00
use base::allocator::Allocator;
pub use num_complex::Complex;
pub use simba::scalar::{
ClosedAdd, ClosedDiv, ClosedMul, ClosedSub, ComplexField, Field, RealField,
};
2021-02-22 21:27:18 +08:00
pub use simba::simd::{SimdBool, SimdComplexField, SimdPartialOrd, SimdRealField, SimdValue};
Api change: deal with inplace/out of place methods. Before, it was too easy to use an out of place method instead of the inplace one since they name were pretty mutch the same. This kind of confusion may lead to silly bugs very hard to understand. Thus the following changes have been made when a method is available both inplace and out-of-place: * inplace version keep a short name. * out-of-place version are suffixed by `_cpy` (meaning `copy`), and are static methods. Methods applying transformations (rotation, translation or general transform) are now prefixed by `append`, and a `prepend` version is available too. Also, free functions doing in-place modifications dont really make sense. They have been removed. Here are the naming changes: * `invert` -> `inv` * `inverted` -> `Inv::inv_cpy` * `transpose` -> `transpose` * `transposed` -> `Transpose::transpose_cpy` * `transform_by` -> `append_transformation` * `transformed` -> `Transform::append_transformation_cpy` * `rotate_by` -> `apppend_rotation` * `rotated` -> `Rotation::append_rotation_cpy` * `translate_by` -> `apppend_translation` * `translate` -> `Translation::append_translation_cpy` * `normalized` -> `Norm::normalize_cpy` * `rotated_wrt_point` -> `RotationWithTranslation::append_rotation_wrt_point_cpy` * `rotated_wrt_center` -> `RotationWithTranslation::append_rotation_wrt_center_cpy` Note that using those static methods is very verbose, and using in-place methods require an explicit import of the related trait. This is a way to convince the user to use free functions most of the time.
2013-10-14 16:22:32 +08:00
/// Gets the multiplicative identity element.
///
/// # See also:
///
/// * [`origin`](../nalgebra/fn.origin.html)
/// * [`zero`](fn.zero.html)
#[inline]
2020-03-21 19:16:46 +08:00
pub fn one<T: One>() -> T {
T::one()
}
Api change: deal with inplace/out of place methods. Before, it was too easy to use an out of place method instead of the inplace one since they name were pretty mutch the same. This kind of confusion may lead to silly bugs very hard to understand. Thus the following changes have been made when a method is available both inplace and out-of-place: * inplace version keep a short name. * out-of-place version are suffixed by `_cpy` (meaning `copy`), and are static methods. Methods applying transformations (rotation, translation or general transform) are now prefixed by `append`, and a `prepend` version is available too. Also, free functions doing in-place modifications dont really make sense. They have been removed. Here are the naming changes: * `invert` -> `inv` * `inverted` -> `Inv::inv_cpy` * `transpose` -> `transpose` * `transposed` -> `Transpose::transpose_cpy` * `transform_by` -> `append_transformation` * `transformed` -> `Transform::append_transformation_cpy` * `rotate_by` -> `apppend_rotation` * `rotated` -> `Rotation::append_rotation_cpy` * `translate_by` -> `apppend_translation` * `translate` -> `Translation::append_translation_cpy` * `normalized` -> `Norm::normalize_cpy` * `rotated_wrt_point` -> `RotationWithTranslation::append_rotation_wrt_point_cpy` * `rotated_wrt_center` -> `RotationWithTranslation::append_rotation_wrt_center_cpy` Note that using those static methods is very verbose, and using in-place methods require an explicit import of the related trait. This is a way to convince the user to use free functions most of the time.
2013-10-14 16:22:32 +08:00
/// Gets the additive identity element.
///
/// # See also:
///
/// * [`one`](fn.one.html)
/// * [`origin`](../nalgebra/fn.origin.html)
#[inline]
2020-03-21 19:16:46 +08:00
pub fn zero<T: Zero>() -> T {
T::zero()
}
/*
*
* Ordering
*
*/
// XXX: this is very naive and could probably be optimized for specific types.
// XXX: also, we might just want to use divisions, but assuming `val` is usually not far from `min`
// or `max`, would it still be more efficient?
/// Wraps `val` into the range `[min, max]` using modular arithmetics.
///
/// The range must not be empty.
2021-06-07 22:44:59 +08:00
#[must_use]
#[inline]
pub fn wrap<T>(mut val: T, min: T, max: T) -> T
2020-08-26 02:47:07 +08:00
where
T: Copy + PartialOrd + ClosedAdd + ClosedSub,
{
assert!(min < max, "Invalid wrapping bounds.");
let width = max - min;
if val < min {
val += width;
while val < min {
val += width
}
2018-02-02 19:26:35 +08:00
} else if val > max {
val -= width;
while val > max {
val -= width
}
}
2021-06-18 15:45:37 +08:00
val
}
/// Returns a reference to the input value clamped to the interval `[min, max]`.
///
/// In particular:
/// * If `min < val < max`, this returns `val`.
2018-09-24 12:48:42 +08:00
/// * If `val <= min`, this returns `min`.
/// * If `val >= max`, this returns `max`.
2021-06-07 22:44:59 +08:00
#[must_use]
#[inline]
pub fn clamp<T: PartialOrd>(val: T, min: T, max: T) -> T {
if val > min {
if val < max {
val
2018-02-02 19:26:35 +08:00
} else {
max
}
2018-02-02 19:26:35 +08:00
} else {
min
}
}
/// Same as `cmp::max`.
#[inline]
pub fn max<T: Ord>(a: T, b: T) -> T {
cmp::max(a, b)
}
/// Same as `cmp::min`.
#[inline]
pub fn min<T: Ord>(a: T, b: T) -> T {
cmp::min(a, b)
}
/// The absolute value of `a`.
2018-12-11 04:54:54 +08:00
///
/// Deprecated: Use [`Matrix::abs`] or [`ComplexField::abs`] instead.
#[deprecated(note = "use the inherent method `Matrix::abs` or `ComplexField::abs` instead")]
#[inline]
pub fn abs<T: Signed>(a: &T) -> T {
a.abs()
}
/// Returns the infimum of `a` and `b`.
#[deprecated(note = "use the inherent method `Matrix::inf` instead")]
#[inline]
2021-04-11 17:00:38 +08:00
pub fn inf<T, R: Dim, C: Dim>(a: &OMatrix<T, R, C>, b: &OMatrix<T, R, C>) -> OMatrix<T, R, C>
where
2021-04-11 17:00:38 +08:00
T: Scalar + SimdPartialOrd,
DefaultAllocator: Allocator<T, R, C>,
{
a.inf(b)
}
/// Returns the supremum of `a` and `b`.
#[deprecated(note = "use the inherent method `Matrix::sup` instead")]
#[inline]
2021-04-11 17:00:38 +08:00
pub fn sup<T, R: Dim, C: Dim>(a: &OMatrix<T, R, C>, b: &OMatrix<T, R, C>) -> OMatrix<T, R, C>
where
2021-04-11 17:00:38 +08:00
T: Scalar + SimdPartialOrd,
DefaultAllocator: Allocator<T, R, C>,
{
a.sup(b)
}
/// Returns simultaneously the infimum and supremum of `a` and `b`.
#[deprecated(note = "use the inherent method `Matrix::inf_sup` instead")]
#[inline]
2021-04-11 17:00:38 +08:00
pub fn inf_sup<T, R: Dim, C: Dim>(
a: &OMatrix<T, R, C>,
b: &OMatrix<T, R, C>,
) -> (OMatrix<T, R, C>, OMatrix<T, R, C>)
where
2021-04-11 17:00:38 +08:00
T: Scalar + SimdPartialOrd,
DefaultAllocator: Allocator<T, R, C>,
{
a.inf_sup(b)
}
/// Compare `a` and `b` using a partial ordering relation.
#[inline]
pub fn partial_cmp<T: PartialOrd>(a: &T, b: &T) -> Option<Ordering> {
a.partial_cmp(b)
}
/// Returns `true` iff `a` and `b` are comparable and `a < b`.
#[inline]
pub fn partial_lt<T: PartialOrd>(a: &T, b: &T) -> bool {
a.lt(b)
}
/// Returns `true` iff `a` and `b` are comparable and `a <= b`.
#[inline]
pub fn partial_le<T: PartialOrd>(a: &T, b: &T) -> bool {
a.le(b)
}
/// Returns `true` iff `a` and `b` are comparable and `a > b`.
#[inline]
pub fn partial_gt<T: PartialOrd>(a: &T, b: &T) -> bool {
a.gt(b)
}
/// Returns `true` iff `a` and `b` are comparable and `a >= b`.
#[inline]
pub fn partial_ge<T: PartialOrd>(a: &T, b: &T) -> bool {
a.ge(b)
}
/// Return the minimum of `a` and `b` if they are comparable.
#[inline]
pub fn partial_min<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> Option<&'a T> {
if let Some(ord) = a.partial_cmp(b) {
match ord {
Ordering::Greater => Some(b),
2018-02-02 19:26:35 +08:00
_ => Some(a),
}
2018-02-02 19:26:35 +08:00
} else {
None
}
}
/// Return the maximum of `a` and `b` if they are comparable.
#[inline]
pub fn partial_max<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> Option<&'a T> {
if let Some(ord) = a.partial_cmp(b) {
match ord {
Ordering::Less => Some(b),
2018-02-02 19:26:35 +08:00
_ => Some(a),
}
2018-02-02 19:26:35 +08:00
} else {
None
}
}
/// Clamp `value` between `min` and `max`. Returns `None` if `value` is not comparable to
/// `min` or `max`.
#[inline]
pub fn partial_clamp<'a, T: PartialOrd>(value: &'a T, min: &'a T, max: &'a T) -> Option<&'a T> {
if let (Some(cmp_min), Some(cmp_max)) = (value.partial_cmp(min), value.partial_cmp(max)) {
if cmp_min == Ordering::Less {
Some(min)
2018-02-02 19:26:35 +08:00
} else if cmp_max == Ordering::Greater {
Some(max)
2018-02-02 19:26:35 +08:00
} else {
Some(value)
}
2018-02-02 19:26:35 +08:00
} else {
None
}
}
/// Sorts two values in increasing order using a partial ordering.
#[inline]
pub fn partial_sort2<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> Option<(&'a T, &'a T)> {
if let Some(ord) = a.partial_cmp(b) {
match ord {
Ordering::Less => Some((a, b)),
2018-02-02 19:26:35 +08:00
_ => Some((b, a)),
}
2018-02-02 19:26:35 +08:00
} else {
None
}
}
/*
*
* Point operations.
*
*/
/// The center of two points.
///
/// # See also:
///
/// * [distance](fn.distance.html)
2021-07-28 07:18:29 +08:00
/// * [`distance_squared`](fn.distance_squared.html)
#[inline]
2021-04-11 17:00:38 +08:00
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()
}
/// The distance between two points.
///
/// # See also:
///
/// * [center](fn.center.html)
2021-07-28 07:18:29 +08:00
/// * [`distance_squared`](fn.distance_squared.html)
#[inline]
2021-04-11 17:00:38 +08:00
pub fn distance<T: SimdComplexField, const D: usize>(
p1: &Point<T, D>,
p2: &Point<T, D>,
) -> T::SimdRealField {
(&p2.coords - &p1.coords).norm()
}
/// The squared distance between two points.
///
/// # See also:
///
/// * [center](fn.center.html)
/// * [distance](fn.distance.html)
#[inline]
2021-04-11 17:00:38 +08:00
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()
}
/*
* Cast
*/
/// Converts an object from one type to an equivalent or more general one.
///
/// See also [`try_convert`](fn.try_convert.html) for conversion to more specific types.
///
/// # See also:
///
2021-07-28 07:18:29 +08:00
/// * [`convert_ref`](fn.convert_ref.html)
/// * [`convert_ref_unchecked`](fn.convert_ref_unchecked.html)
/// * [`is_convertible`](../nalgebra/fn.is_convertible.html)
/// * [`try_convert`](fn.try_convert.html)
/// * [`try_convert_ref`](fn.try_convert_ref.html)
#[inline]
pub fn convert<From, To: SupersetOf<From>>(t: From) -> To {
To::from_subset(&t)
}
/// Attempts to convert an object to a more specific one.
///
/// See also [`convert`](fn.convert.html) for conversion to more general types.
///
/// # See also:
///
/// * [convert](fn.convert.html)
2021-07-28 07:18:29 +08:00
/// * [`convert_ref`](fn.convert_ref.html)
/// * [`convert_ref_unchecked`](fn.convert_ref_unchecked.html)
/// * [`is_convertible`](../nalgebra/fn.is_convertible.html)
/// * [`try_convert_ref`](fn.try_convert_ref.html)
#[inline]
pub fn try_convert<From: SupersetOf<To>, To>(t: From) -> Option<To> {
t.to_subset()
}
/// Indicates if [`try_convert`](fn.try_convert.html) will succeed without
/// actually performing the conversion.
///
/// # See also:
///
/// * [convert](fn.convert.html)
2021-07-28 07:18:29 +08:00
/// * [`convert_ref`](fn.convert_ref.html)
/// * [`convert_ref_unchecked`](fn.convert_ref_unchecked.html)
/// * [`try_convert`](fn.try_convert.html)
/// * [`try_convert_ref`](fn.try_convert_ref.html)
#[inline]
pub fn is_convertible<From: SupersetOf<To>, To>(t: &From) -> bool {
t.is_in_subset()
}
/// Use with care! Same as [`try_convert`](fn.try_convert.html) but
/// without any property checks.
///
/// # See also:
///
/// * [convert](fn.convert.html)
2021-07-28 07:18:29 +08:00
/// * [`convert_ref`](fn.convert_ref.html)
/// * [`convert_ref_unchecked`](fn.convert_ref_unchecked.html)
/// * [`is_convertible`](../nalgebra/fn.is_convertible.html)
/// * [`try_convert`](fn.try_convert.html)
/// * [`try_convert_ref`](fn.try_convert_ref.html)
#[inline]
2020-03-21 19:16:46 +08:00
pub fn convert_unchecked<From: SupersetOf<To>, To>(t: From) -> To {
t.to_subset_unchecked()
}
/// Converts an object from one type to an equivalent or more general one.
///
/// # See also:
///
/// * [convert](fn.convert.html)
2021-07-28 07:18:29 +08:00
/// * [`convert_ref_unchecked`](fn.convert_ref_unchecked.html)
/// * [`is_convertible`](../nalgebra/fn.is_convertible.html)
/// * [`try_convert`](fn.try_convert.html)
/// * [`try_convert_ref`](fn.try_convert_ref.html)
#[inline]
pub fn convert_ref<From, To: SupersetOf<From>>(t: &From) -> To {
To::from_subset(t)
}
/// Attempts to convert an object to a more specific one.
///
/// # See also:
///
/// * [convert](fn.convert.html)
2021-07-28 07:18:29 +08:00
/// * [`convert_ref`](fn.convert_ref.html)
/// * [`convert_ref_unchecked`](fn.convert_ref_unchecked.html)
/// * [`is_convertible`](../nalgebra/fn.is_convertible.html)
/// * [`try_convert`](fn.try_convert.html)
#[inline]
pub fn try_convert_ref<From: SupersetOf<To>, To>(t: &From) -> Option<To> {
t.to_subset()
}
/// Use with care! Same as [`try_convert`](fn.try_convert.html) but
/// without any property checks.
///
/// # See also:
///
/// * [convert](fn.convert.html)
2021-07-28 07:18:29 +08:00
/// * [`convert_ref`](fn.convert_ref.html)
/// * [`is_convertible`](../nalgebra/fn.is_convertible.html)
/// * [`try_convert`](fn.try_convert.html)
/// * [`try_convert_ref`](fn.try_convert_ref.html)
#[inline]
2020-03-21 19:16:46 +08:00
pub fn convert_ref_unchecked<From: SupersetOf<To>, To>(t: &From) -> To {
t.to_subset_unchecked()
}