commit
04e4e11154
@ -47,10 +47,10 @@ jobs:
|
|||||||
- checkout
|
- checkout
|
||||||
- run:
|
- run:
|
||||||
name: test
|
name: test
|
||||||
command: cargo test --all-features
|
command: cargo test --features arbitrary --features serde-serialize --features abomonation-serialize --features sparse --features debug --features io --features compare --features libm
|
||||||
- run:
|
- run:
|
||||||
name: test nalgebra-glm
|
name: test nalgebra-glm
|
||||||
command: cargo test -p nalgebra-glm --all-features
|
command: cargo test -p nalgebra-glm --features arbitrary --features serde-serialize --features abomonation-serialize --features sparse --features debug --features io --features compare --features libm
|
||||||
build-wasm:
|
build-wasm:
|
||||||
executor: rust-executor
|
executor: rust-executor
|
||||||
steps:
|
steps:
|
||||||
|
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@ -1,7 +1,7 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: [ "sebcrozet" ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
github: [ "dimforge" ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
patreon: sebcrozet # Replace with a single Patreon username
|
patreon: # Replace with a single Patreon username
|
||||||
open_collective: # Replace with a single Open Collective username
|
open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
26
CHANGELOG.md
26
CHANGELOG.md
@ -4,15 +4,37 @@ documented here.
|
|||||||
|
|
||||||
This project adheres to [Semantic Versioning](https://semver.org/).
|
This project adheres to [Semantic Versioning](https://semver.org/).
|
||||||
|
|
||||||
## [0.22.0] - WIP
|
## [0.23.0] - WIP
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
* The `.inverse_transform_unit_vector(v)` was added to `Rotation2/3`, `Isometry2/3`, `UnitQuaternion`, and `UnitComplex`.
|
||||||
|
It applies the corresponding rotation to a unit vector `Unit<Vector2/3>`.
|
||||||
|
* The `Point.map(f)` and `Point.apply(f)` to apply a function to each component of the point, similarly to `Vector.map(f)`
|
||||||
|
and `Vector.apply(f)`.
|
||||||
|
* The `Quaternion::from([N; 4])` conversion to build a quaternion from an array of four elements.
|
||||||
|
* The `Isometry::from(Translation)` conversion to build an isometry from a translation.
|
||||||
|
* The `Vector::ith_axis(i)` which build a unit vector, e.g., `Unit<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.
|
||||||
|
* The `Rotation2.slerp`, `Rotation3.slerp`, and `UnitQuaternion.slerp` method for
|
||||||
|
spherical interpolation.
|
||||||
|
|
||||||
|
## [0.22.0]
|
||||||
|
In this release, we are using the new version 0.2 of simba. One major change of that version is that the
|
||||||
|
use of `libm` is now opt-in when building targetting `no-std` environment. If you are using floating-point
|
||||||
|
operations with nalgebra in a `no-std` environment, you will need to enable the new `libm` feature
|
||||||
|
of nalgebra for your code to compile again.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
* The `libm` feature that enables `libm` when building for `no-std` environment.
|
||||||
|
* The `libm-force` feature that enables `libm` even when building for a not `no-std` environment.
|
||||||
* `Cholesky::new_unchecked` which build a Cholesky decomposition without checking that its input is
|
* `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 use with SIMD types.
|
||||||
* The `Default` trait is now implemented for matrices, and quaternions. They are all filled with zeros,
|
* 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.
|
except for `UnitQuaternion` which is initialized with the identity.
|
||||||
* Matrix exponential `matrix.exp()`.
|
* Matrix exponential `matrix.exp()`.
|
||||||
|
* The `Vector::ith(i, x)` that builds a vector filled with zeros except for the `i`-th component set to `x`.
|
||||||
|
|
||||||
## [0.21.0]
|
## [0.21.0]
|
||||||
In this release, we are no longer relying on traits from the __alga__ crate for our generic code.
|
In this release, we are no longer relying on traits from the __alga__ crate for our generic code.
|
||||||
|
38
Cargo.toml
38
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nalgebra"
|
name = "nalgebra"
|
||||||
version = "0.21.1"
|
version = "0.23.0"
|
||||||
authors = [ "Sébastien Crozet <developer@crozet.re>" ]
|
authors = [ "Sébastien Crozet <developer@crozet.re>" ]
|
||||||
|
|
||||||
description = "Linear algebra library with transformations and statically-sized or dynamically-sized matrices."
|
description = "Linear algebra library with transformations and statically-sized or dynamically-sized matrices."
|
||||||
@ -10,7 +10,7 @@ repository = "https://github.com/rustsim/nalgebra"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
categories = [ "science" ]
|
categories = [ "science" ]
|
||||||
keywords = [ "linear", "algebra", "matrix", "vector", "math" ]
|
keywords = [ "linear", "algebra", "matrix", "vector", "math" ]
|
||||||
license = "BSD-3-Clause"
|
license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
exclude = ["/ci/*", "/.travis.yml", "/Makefile"]
|
exclude = ["/ci/*", "/.travis.yml", "/Makefile"]
|
||||||
@ -24,32 +24,36 @@ default = [ "std" ]
|
|||||||
std = [ "matrixmultiply", "rand/std", "rand_distr", "simba/std" ]
|
std = [ "matrixmultiply", "rand/std", "rand_distr", "simba/std" ]
|
||||||
stdweb = [ "rand/stdweb" ]
|
stdweb = [ "rand/stdweb" ]
|
||||||
arbitrary = [ "quickcheck" ]
|
arbitrary = [ "quickcheck" ]
|
||||||
serde-serialize = [ "serde", "serde_derive", "num-complex/serde" ]
|
serde-serialize = [ "serde", "num-complex/serde" ]
|
||||||
abomonation-serialize = [ "abomonation" ]
|
abomonation-serialize = [ "abomonation" ]
|
||||||
sparse = [ ]
|
sparse = [ ]
|
||||||
debug = [ "approx/num-complex", "rand/std" ]
|
debug = [ "approx/num-complex", "rand/std" ]
|
||||||
alloc = [ ]
|
alloc = [ ]
|
||||||
io = [ "pest", "pest_derive" ]
|
io = [ "pest", "pest_derive" ]
|
||||||
|
compare = [ "matrixcompare-core" ]
|
||||||
|
libm = [ "simba/libm" ]
|
||||||
|
libm-force = [ "simba/libm_force" ]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
typenum = "1.11"
|
typenum = "1.12"
|
||||||
generic-array = "0.13"
|
generic-array = "0.14"
|
||||||
rand = { version = "0.7", default-features = false }
|
rand = { version = "0.7", default-features = false }
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
num-complex = { version = "0.2", default-features = false }
|
num-complex = { version = "0.3", default-features = false }
|
||||||
num-rational = { version = "0.2", default-features = false }
|
num-rational = { version = "0.3", default-features = false }
|
||||||
approx = { version = "0.3", default-features = false }
|
approx = { version = "0.4", default-features = false }
|
||||||
simba = { version = "0.1", default-features = false }
|
simba = { version = "0.3", default-features = false }
|
||||||
alga = { version = "0.9", default-features = false, optional = true }
|
alga = { version = "0.9", default-features = false, optional = true }
|
||||||
rand_distr = { version = "0.2", optional = true }
|
rand_distr = { version = "0.3", optional = true }
|
||||||
matrixmultiply = { version = "0.2", optional = true }
|
matrixmultiply = { version = "0.2", optional = true }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", features = [ "derive" ], optional = true }
|
||||||
serde_derive = { version = "1.0", optional = true }
|
|
||||||
abomonation = { version = "0.7", optional = true }
|
abomonation = { version = "0.7", optional = true }
|
||||||
mint = { version = "0.5", optional = true }
|
mint = { version = "0.5", optional = true }
|
||||||
quickcheck = { version = "0.9", optional = true }
|
quickcheck = { version = "0.9", optional = true }
|
||||||
pest = { version = "2.0", optional = true }
|
pest = { version = "2", optional = true }
|
||||||
pest_derive = { version = "2.0", optional = true }
|
pest_derive = { version = "2", optional = true }
|
||||||
|
matrixcompare-core = { version = "0.1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
@ -61,6 +65,9 @@ rand_isaac = "0.2"
|
|||||||
### https://github.com/rust-lang/cargo/issues/4866
|
### https://github.com/rust-lang/cargo/issues/4866
|
||||||
#criterion = "0.2.10"
|
#criterion = "0.2.10"
|
||||||
|
|
||||||
|
# For matrix comparison macro
|
||||||
|
matrixcompare = "0.1.3"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [ "nalgebra-lapack", "nalgebra-glm" ]
|
members = [ "nalgebra-lapack", "nalgebra-glm" ]
|
||||||
|
|
||||||
@ -71,6 +78,3 @@ path = "benches/lib.rs"
|
|||||||
|
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
#[patch.crates-io]
|
|
||||||
#simba = { path = "../simba" }
|
|
218
LICENSE
218
LICENSE
@ -1,27 +1,201 @@
|
|||||||
Copyright (c) 2013, Sébastien Crozet
|
Apache License
|
||||||
All rights reserved.
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
1. Definitions.
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
this list of conditions and the following disclaimer in the documentation
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. Neither the name of the author nor the names of its contributors may be used
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
to endorse or promote products derived from this software without specific
|
the copyright owner that is granting the License.
|
||||||
prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
other entities that control, are controlled by, or are under common
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
control with that entity. For the purposes of this definition,
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
direction or management of such entity, whether by contract or
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2020 Sébastien Crozet
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
4
Makefile
4
Makefile
@ -1,5 +1,5 @@
|
|||||||
all:
|
all:
|
||||||
cargo test --features "debug arbitrary serde-serialize abomonation-serialize"
|
cargo test --features "debug arbitrary serde-serialize abomonation-serialize compare"
|
||||||
# cargo check --features "debug arbitrary serde-serialize"
|
# cargo check --features "debug arbitrary serde-serialize"
|
||||||
|
|
||||||
doc:
|
doc:
|
||||||
@ -9,4 +9,4 @@ bench:
|
|||||||
cargo bench
|
cargo bench
|
||||||
|
|
||||||
test:
|
test:
|
||||||
cargo test --features "debug arbitrary serde-serialize abomonation-serialize"
|
cargo test --features "debug arbitrary serde-serialize abomonation-serialize compare"
|
||||||
|
16
README.md
16
README.md
@ -5,14 +5,14 @@
|
|||||||
<a href="https://discord.gg/vt9DJSW">
|
<a href="https://discord.gg/vt9DJSW">
|
||||||
<img src="https://img.shields.io/discord/507548572338880513.svg?logo=discord&colorB=7289DA">
|
<img src="https://img.shields.io/discord/507548572338880513.svg?logo=discord&colorB=7289DA">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://travis-ci.org/rustsim/nalgebra">
|
<a href="https://travis-ci.org/dimforge/nalgebra">
|
||||||
<img src="https://travis-ci.org/rustsim/nalgebra.svg?branch=master" alt="Build status">
|
<img src="https://travis-ci.org/dimforge/nalgebra.svg?branch=master" alt="Build status">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://crates.io/crates/nalgebra">
|
<a href="https://crates.io/crates/nalgebra">
|
||||||
<img src="https://meritbadge.herokuapp.com/nalgebra?style=flat-square" alt="crates.io">
|
<img src="https://meritbadge.herokuapp.com/nalgebra?style=flat-square" alt="crates.io">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://opensource.org/licenses/BSD-3-Clause">
|
<a href="https://opensource.org/licenses/Apache-2.0">
|
||||||
<img src="https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat">
|
<img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p align = "center">
|
<p align = "center">
|
||||||
@ -29,11 +29,3 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|
||||||
<p align = "center">
|
|
||||||
<i>Click this button if you wish to donate to support the development of</i> <b>nalgebra</b>:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align = "center">
|
|
||||||
<a href="https://www.patreon.com/bePatron?u=7111380" ><img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Become a Patron!" /></a>
|
|
||||||
</p>
|
|
||||||
|
82
examples/matrixcompare.rs
Normal file
82
examples/matrixcompare.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
use matrixcompare::comparators::{AbsoluteElementwiseComparator, ExactElementwiseComparator};
|
||||||
|
use matrixcompare::compare_matrices;
|
||||||
|
use na::{MatrixMN, U3, U4};
|
||||||
|
|
||||||
|
fn compare_integers_fail() {
|
||||||
|
println!("Comparing two integer matrices.");
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let a = MatrixMN::<_, U3, U4>::from_row_slice(&[
|
||||||
|
0, 1, 2, 3,
|
||||||
|
4, 5, 6, 7,
|
||||||
|
8, 9, -2, 11
|
||||||
|
]);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let b = MatrixMN::<_, U3, U4>::from_row_slice(&[
|
||||||
|
0, 1, 2, 3,
|
||||||
|
4, 5, 6, 7,
|
||||||
|
8, 9, 10, 11
|
||||||
|
]);
|
||||||
|
|
||||||
|
if let Err(err) = compare_matrices(a, b, &ExactElementwiseComparator) {
|
||||||
|
println!("{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_different_size() {
|
||||||
|
println!("Comparing matrices of different size.");
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let a = MatrixMN::<_, U3, U3>::from_row_slice(&[
|
||||||
|
0, 1, 2,
|
||||||
|
4, 5, 6,
|
||||||
|
8, 9, 10,
|
||||||
|
]);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let b = MatrixMN::<_, U3, U4>::from_row_slice(&[
|
||||||
|
0, 1, 2, 3,
|
||||||
|
4, 5, 6, 7,
|
||||||
|
8, 9, 10, 11
|
||||||
|
]);
|
||||||
|
|
||||||
|
if let Err(err) = compare_matrices(a, b, &ExactElementwiseComparator) {
|
||||||
|
println!("{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_f64_abs_tol_fail() {
|
||||||
|
println!("Comparing two f64 matrices.");
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let a = MatrixMN::<f64, U3, U3>::from_row_slice(&[
|
||||||
|
0.0, 1.0, 2.0 + 1e-10,
|
||||||
|
4.0, 5.0, 6.0,
|
||||||
|
8.0, 9.0, 10.0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let b = MatrixMN::<_, U3, U3>::from_row_slice(&[
|
||||||
|
0.0, 1.0, 2.0,
|
||||||
|
4.0, 5.0, 6.0,
|
||||||
|
8.0, 9.0, 10.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
let cmp = AbsoluteElementwiseComparator { tol: 1e-12 };
|
||||||
|
if let Err(err) = compare_matrices(a, b, &cmp) {
|
||||||
|
println!("{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// This example mostly serves the purpose of demonstrating the kind of error messages
|
||||||
|
// that are given upon comparison failure.
|
||||||
|
// The more typical use case is using `assert_matrix_eq!` in tests.
|
||||||
|
compare_integers_fail();
|
||||||
|
println!("======================================================");
|
||||||
|
compare_f64_abs_tol_fail();
|
||||||
|
println!("======================================================");
|
||||||
|
compare_different_size();
|
||||||
|
}
|
54
examples/reshaping.rs
Normal file
54
examples/reshaping.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
|
||||||
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
use na::{DMatrix, Dynamic, Matrix2x3, Matrix3x2, U2, U3};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Matrices can be reshaped in-place without moving or copying values.
|
||||||
|
let m1 = Matrix2x3::new(
|
||||||
|
1.1, 1.2, 1.3,
|
||||||
|
2.1, 2.2, 2.3
|
||||||
|
);
|
||||||
|
let m2 = Matrix3x2::new(
|
||||||
|
1.1, 2.2,
|
||||||
|
2.1, 1.3,
|
||||||
|
1.2, 2.3
|
||||||
|
);
|
||||||
|
|
||||||
|
let m3 = m1.reshape_generic(U3, U2);
|
||||||
|
assert_eq!(m3, m2);
|
||||||
|
|
||||||
|
// Note that, for statically sized matrices, invalid reshapes will not compile:
|
||||||
|
//let m4 = m3.reshape_generic(U3, U3);
|
||||||
|
|
||||||
|
// If dynamically sized matrices are used, the reshaping is checked at run-time.
|
||||||
|
let dm1 = DMatrix::from_row_slice(
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
&[
|
||||||
|
1.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0,
|
||||||
|
0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let dm2 = DMatrix::from_row_slice(
|
||||||
|
6,
|
||||||
|
2,
|
||||||
|
&[
|
||||||
|
1.0, 0.0,
|
||||||
|
0.0, 1.0,
|
||||||
|
0.0, 0.0,
|
||||||
|
0.0, 1.0,
|
||||||
|
0.0, 0.0,
|
||||||
|
0.0, 0.0,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let dm3 = dm1.reshape_generic(Dynamic::new(6), Dynamic::new(2));
|
||||||
|
assert_eq!(dm3, dm2);
|
||||||
|
|
||||||
|
// Invalid reshapings of dynamic matrices will panic at run-time.
|
||||||
|
//let dm4 = dm3.reshape_generic(Dynamic::new(6), Dynamic::new(6));
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nalgebra-glm"
|
name = "nalgebra-glm"
|
||||||
version = "0.7.0"
|
version = "0.9.0"
|
||||||
authors = ["sebcrozet <developer@crozet.re>"]
|
authors = ["sebcrozet <developer@crozet.re>"]
|
||||||
|
|
||||||
description = "A computer-graphics oriented API for nalgebra, inspired by the C++ GLM library."
|
description = "A computer-graphics oriented API for nalgebra, inspired by the C++ GLM library."
|
||||||
@ -23,6 +23,6 @@ abomonation-serialize = [ "nalgebra/abomonation-serialize" ]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
approx = { version = "0.3", default-features = false }
|
approx = { version = "0.4", default-features = false }
|
||||||
simba = { version = "0.1", default-features = false }
|
simba = { version = "0.3", default-features = false }
|
||||||
nalgebra = { path = "..", version = "0.21", default-features = false }
|
nalgebra = { path = "..", version = "0.23", default-features = false }
|
||||||
|
@ -530,7 +530,7 @@ pub fn perspective_lh_no<N: RealField>(aspect: N, fovy: N, near: N, far: N) -> T
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!relative_eq!(aspect, N::zero()),
|
!relative_eq!(aspect, N::zero()),
|
||||||
"The apsect ratio must not be zero."
|
"The aspect ratio must not be zero."
|
||||||
);
|
);
|
||||||
|
|
||||||
let one = N::one();
|
let one = N::one();
|
||||||
@ -566,7 +566,7 @@ pub fn perspective_lh_zo<N: RealField>(aspect: N, fovy: N, near: N, far: N) -> T
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!relative_eq!(aspect, N::zero()),
|
!relative_eq!(aspect, N::zero()),
|
||||||
"The apsect ratio must not be zero."
|
"The aspect ratio must not be zero."
|
||||||
);
|
);
|
||||||
|
|
||||||
let one = N::one();
|
let one = N::one();
|
||||||
@ -632,7 +632,7 @@ pub fn perspective_rh_no<N: RealField>(aspect: N, fovy: N, near: N, far: N) -> T
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!relative_eq!(aspect, N::zero()),
|
!relative_eq!(aspect, N::zero()),
|
||||||
"The apsect ratio must not be zero."
|
"The aspect ratio must not be zero."
|
||||||
);
|
);
|
||||||
|
|
||||||
let negone = -N::one();
|
let negone = -N::one();
|
||||||
@ -669,7 +669,7 @@ pub fn perspective_rh_zo<N: RealField>(aspect: N, fovy: N, near: N, far: N) -> T
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!relative_eq!(aspect, N::zero()),
|
!relative_eq!(aspect, N::zero()),
|
||||||
"The apsect ratio must not be zero."
|
"The aspect ratio must not be zero."
|
||||||
);
|
);
|
||||||
|
|
||||||
let negone = -N::one();
|
let negone = -N::one();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nalgebra-lapack"
|
name = "nalgebra-lapack"
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
authors = [ "Sébastien Crozet <developer@crozet.re>", "Andrew Straw <strawman@astraw.com>" ]
|
authors = [ "Sébastien Crozet <developer@crozet.re>", "Andrew Straw <strawman@astraw.com>" ]
|
||||||
|
|
||||||
description = "Linear algebra library with transformations and satically-sized or dynamically-sized matrices."
|
description = "Linear algebra library with transformations and satically-sized or dynamically-sized matrices."
|
||||||
@ -23,10 +23,10 @@ accelerate = ["lapack-src/accelerate"]
|
|||||||
intel-mkl = ["lapack-src/intel-mkl"]
|
intel-mkl = ["lapack-src/intel-mkl"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nalgebra = { version = "0.21", path = ".." }
|
nalgebra = { version = "0.22" } # , path = ".." }
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num-complex = { version = "0.2", default-features = false }
|
num-complex = { version = "0.2", default-features = false }
|
||||||
simba = "0.1"
|
simba = "0.2"
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
serde_derive = { version = "1.0", optional = true }
|
serde_derive = { version = "1.0", optional = true }
|
||||||
lapack = { version = "0.16", default-features = false }
|
lapack = { version = "0.16", default-features = false }
|
||||||
@ -34,7 +34,7 @@ lapack-src = { version = "0.5", default-features = false }
|
|||||||
# clippy = "*"
|
# clippy = "*"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nalgebra = { version = "0.21", path = "..", features = [ "arbitrary" ] }
|
nalgebra = { version = "0.22", features = [ "arbitrary" ] } # path = ".." }
|
||||||
quickcheck = "0.9"
|
quickcheck = "0.9"
|
||||||
approx = "0.3"
|
approx = "0.3"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
unstable_features = true
|
|
||||||
indent_style = "Block"
|
|
||||||
where_single_line = true
|
|
@ -24,7 +24,9 @@ use typenum::Prod;
|
|||||||
use crate::base::allocator::Allocator;
|
use crate::base::allocator::Allocator;
|
||||||
use crate::base::default_allocator::DefaultAllocator;
|
use crate::base::default_allocator::DefaultAllocator;
|
||||||
use crate::base::dimension::{DimName, U1};
|
use crate::base::dimension::{DimName, U1};
|
||||||
use crate::base::storage::{ContiguousStorage, ContiguousStorageMut, Owned, Storage, StorageMut};
|
use crate::base::storage::{
|
||||||
|
ContiguousStorage, ContiguousStorageMut, Owned, ReshapableStorage, Storage, StorageMut,
|
||||||
|
};
|
||||||
use crate::base::Scalar;
|
use crate::base::Scalar;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -267,6 +269,25 @@ where
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N, R1, C1, R2, C2> ReshapableStorage<N, R1, C1, R2, C2> for ArrayStorage<N, R1, C1>
|
||||||
|
where
|
||||||
|
N: Scalar,
|
||||||
|
R1: DimName,
|
||||||
|
C1: DimName,
|
||||||
|
R1::Value: Mul<C1::Value>,
|
||||||
|
Prod<R1::Value, C1::Value>: ArrayLength<N>,
|
||||||
|
R2: DimName,
|
||||||
|
C2: DimName,
|
||||||
|
R2::Value: Mul<C2::Value, Output = Prod<R1::Value, C1::Value>>,
|
||||||
|
Prod<R2::Value, C2::Value>: ArrayLength<N>,
|
||||||
|
{
|
||||||
|
type Output = ArrayStorage<N, R2, C2>;
|
||||||
|
|
||||||
|
fn reshape_generic(self, _: R2, _: C2) -> Self::Output {
|
||||||
|
ArrayStorage { data: self.data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Allocation-less serde impls.
|
* Allocation-less serde impls.
|
||||||
|
@ -284,7 +284,16 @@ where
|
|||||||
{
|
{
|
||||||
assert!(
|
assert!(
|
||||||
self.nrows() == rhs.nrows(),
|
self.nrows() == rhs.nrows(),
|
||||||
"Dot product dimensions mismatch."
|
"Dot product dimensions mismatch for shapes {:?} and {:?}: left rows != right rows.",
|
||||||
|
self.shape(),
|
||||||
|
rhs.shape(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
self.ncols() == rhs.ncols(),
|
||||||
|
"Dot product dimensions mismatch for shapes {:?} and {:?}: left cols != right cols.",
|
||||||
|
self.shape(),
|
||||||
|
rhs.shape(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// So we do some special cases for common fixed-size vectors of dimension lower than 8
|
// So we do some special cases for common fixed-size vectors of dimension lower than 8
|
||||||
@ -361,8 +370,8 @@ where
|
|||||||
|
|
||||||
while self.nrows() - i >= 8 {
|
while self.nrows() - i >= 8 {
|
||||||
acc0 += unsafe {
|
acc0 += unsafe {
|
||||||
conjugate(self.get_unchecked((i + 0, j)).inlined_clone())
|
conjugate(self.get_unchecked((i, j)).inlined_clone())
|
||||||
* rhs.get_unchecked((i + 0, j)).inlined_clone()
|
* rhs.get_unchecked((i, j)).inlined_clone()
|
||||||
};
|
};
|
||||||
acc1 += unsafe {
|
acc1 += unsafe {
|
||||||
conjugate(self.get_unchecked((i + 1, j)).inlined_clone())
|
conjugate(self.get_unchecked((i + 1, j)).inlined_clone())
|
||||||
@ -496,8 +505,9 @@ where
|
|||||||
ShapeConstraint: DimEq<C, R2> + DimEq<R, C2>,
|
ShapeConstraint: DimEq<C, R2> + DimEq<R, C2>,
|
||||||
{
|
{
|
||||||
let (nrows, ncols) = self.shape();
|
let (nrows, ncols) = self.shape();
|
||||||
assert!(
|
assert_eq!(
|
||||||
(ncols, nrows) == rhs.shape(),
|
(ncols, nrows),
|
||||||
|
rhs.shape(),
|
||||||
"Transposed dot product dimension mismatch."
|
"Transposed dot product dimension mismatch."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -11,11 +11,12 @@ use crate::base::allocator::Allocator;
|
|||||||
use crate::base::dimension::{DimName, DimNameDiff, DimNameSub, U1};
|
use crate::base::dimension::{DimName, DimNameDiff, DimNameSub, U1};
|
||||||
use crate::base::storage::{Storage, StorageMut};
|
use crate::base::storage::{Storage, StorageMut};
|
||||||
use crate::base::{
|
use crate::base::{
|
||||||
DefaultAllocator, Matrix3, Matrix4, MatrixN, Scalar, SquareMatrix, Unit, Vector, Vector3,
|
DefaultAllocator, Matrix3, Matrix4, MatrixN, Scalar, SquareMatrix, Unit, Vector, Vector2,
|
||||||
VectorN,
|
Vector3, VectorN,
|
||||||
};
|
};
|
||||||
use crate::geometry::{
|
use crate::geometry::{
|
||||||
Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point3, Rotation2, Rotation3,
|
Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point2, Point3, Rotation2,
|
||||||
|
Rotation3,
|
||||||
};
|
};
|
||||||
|
|
||||||
use simba::scalar::{ClosedAdd, ClosedMul, RealField};
|
use simba::scalar::{ClosedAdd, ClosedMul, RealField};
|
||||||
@ -70,6 +71,26 @@ impl<N: RealField> Matrix3<N> {
|
|||||||
pub fn new_rotation(angle: N) -> Self {
|
pub fn new_rotation(angle: N) -> Self {
|
||||||
Rotation2::new(angle).to_homogeneous()
|
Rotation2::new(angle).to_homogeneous()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point.
|
||||||
|
///
|
||||||
|
/// Can be used to implement "zoom_to" functionality.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector2<N>, pt: &Point2<N>) -> Self {
|
||||||
|
let _0 = N::zero();
|
||||||
|
let _1 = N::one();
|
||||||
|
Matrix3::new(
|
||||||
|
scaling.x,
|
||||||
|
_0,
|
||||||
|
pt.x - pt.x * scaling.x,
|
||||||
|
_0,
|
||||||
|
scaling.y,
|
||||||
|
pt.y - pt.y * scaling.y,
|
||||||
|
_0,
|
||||||
|
_0,
|
||||||
|
_1,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: RealField> Matrix4<N> {
|
impl<N: RealField> Matrix4<N> {
|
||||||
@ -90,6 +111,33 @@ impl<N: RealField> Matrix4<N> {
|
|||||||
Isometry::rotation_wrt_point(rot, pt).to_homogeneous()
|
Isometry::rotation_wrt_point(rot, pt).to_homogeneous()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point.
|
||||||
|
///
|
||||||
|
/// Can be used to implement "zoom_to" functionality.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector3<N>, pt: &Point3<N>) -> Self {
|
||||||
|
let _0 = N::zero();
|
||||||
|
let _1 = N::one();
|
||||||
|
Matrix4::new(
|
||||||
|
scaling.x,
|
||||||
|
_0,
|
||||||
|
_0,
|
||||||
|
pt.x - pt.x * scaling.x,
|
||||||
|
_0,
|
||||||
|
scaling.y,
|
||||||
|
_0,
|
||||||
|
pt.y - pt.y * scaling.y,
|
||||||
|
_0,
|
||||||
|
_0,
|
||||||
|
scaling.z,
|
||||||
|
pt.z - pt.z * scaling.z,
|
||||||
|
_0,
|
||||||
|
_0,
|
||||||
|
_0,
|
||||||
|
_1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
|
/// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
|
||||||
///
|
///
|
||||||
/// Returns the identity matrix if the given argument is zero.
|
/// Returns the identity matrix if the given argument is zero.
|
||||||
|
@ -1011,6 +1011,20 @@ where
|
|||||||
N: Scalar + Zero + One,
|
N: Scalar + Zero + One,
|
||||||
DefaultAllocator: Allocator<N, R>,
|
DefaultAllocator: Allocator<N, R>,
|
||||||
{
|
{
|
||||||
|
/// The column vector with `val` as its i-th component.
|
||||||
|
#[inline]
|
||||||
|
pub fn ith(i: usize, val: N) -> Self {
|
||||||
|
let mut res = Self::zeros();
|
||||||
|
res[i] = val;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The column unit vector with `N::one()` as its i-th component.
|
||||||
|
#[inline]
|
||||||
|
pub fn ith_axis(i: usize) -> Unit<Self> {
|
||||||
|
Unit::new_unchecked(Self::ith(i, N::one()))
|
||||||
|
}
|
||||||
|
|
||||||
/// The column vector with a 1 as its first component, and zero elsewhere.
|
/// The column vector with a 1 as its first component, and zero elsewhere.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn x() -> Self
|
pub fn x() -> Self
|
||||||
|
@ -23,7 +23,7 @@ impl Dynamic {
|
|||||||
/// A dynamic size equal to `value`.
|
/// A dynamic size equal to `value`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(value: usize) -> Self {
|
pub fn new(value: usize) -> Self {
|
||||||
Self { value: value }
|
Self { value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ use crate::base::dimension::Dynamic;
|
|||||||
use crate::base::dimension::{
|
use crate::base::dimension::{
|
||||||
Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimName, DimSub, DimSum, U1,
|
Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimName, DimSub, DimSum, U1,
|
||||||
};
|
};
|
||||||
use crate::base::storage::{Storage, StorageMut};
|
use crate::base::storage::{ReshapableStorage, Storage, StorageMut};
|
||||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
use crate::base::DMatrix;
|
use crate::base::DMatrix;
|
||||||
use crate::base::{DefaultAllocator, Matrix, MatrixMN, RowVector, Scalar, Vector};
|
use crate::base::{DefaultAllocator, Matrix, MatrixMN, RowVector, Scalar, Vector};
|
||||||
@ -745,7 +745,7 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
|||||||
self.resize_generic(R2::name(), C2::name(), val)
|
self.resize_generic(R2::name(), C2::name(), val)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resizes `self` such that it has dimensions `new_nrows × now_ncols`.
|
/// Resizes `self` such that it has dimensions `new_nrows × new_ncols`.
|
||||||
///
|
///
|
||||||
/// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more
|
/// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more
|
||||||
/// rows and/or columns than `self`, then the extra rows or columns are filled with `val`.
|
/// rows and/or columns than `self`, then the extra rows or columns are filled with `val`.
|
||||||
@ -813,6 +813,80 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N, R, C, S> Matrix<N, R, C, S>
|
||||||
|
where
|
||||||
|
N: Scalar,
|
||||||
|
R: Dim,
|
||||||
|
C: Dim,
|
||||||
|
{
|
||||||
|
/// Reshapes `self` such that it has dimensions `new_nrows × new_ncols`.
|
||||||
|
///
|
||||||
|
/// This will reinterpret `self` as if it is a matrix with `new_nrows` rows and `new_ncols`
|
||||||
|
/// columns. The arrangements of the component in the output matrix are the same as what
|
||||||
|
/// would be obtained by `Matrix::from_slice_generic(self.as_slice(), new_nrows, new_ncols)`.
|
||||||
|
///
|
||||||
|
/// If `self` is a dynamically-sized matrix, then its components are neither copied nor moved.
|
||||||
|
/// If `self` is staticyll-sized, then a copy may happen in some situations.
|
||||||
|
/// This function will panic if the given dimensions are such that the number of elements of
|
||||||
|
/// the input matrix are not equal to the number of elements of the output matrix.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use nalgebra::{Matrix3x2, Matrix2x3, DMatrix, U2, U3, Dynamic};
|
||||||
|
///
|
||||||
|
/// let m1 = Matrix2x3::new(
|
||||||
|
/// 1.1, 1.2, 1.3,
|
||||||
|
/// 2.1, 2.2, 2.3
|
||||||
|
/// );
|
||||||
|
/// let m2 = Matrix3x2::new(
|
||||||
|
/// 1.1, 2.2,
|
||||||
|
/// 2.1, 1.3,
|
||||||
|
/// 1.2, 2.3
|
||||||
|
/// );
|
||||||
|
/// let reshaped = m1.reshape_generic(U3, U2);
|
||||||
|
/// assert_eq!(reshaped, m2);
|
||||||
|
///
|
||||||
|
/// let dm1 = DMatrix::from_row_slice(
|
||||||
|
/// 4,
|
||||||
|
/// 3,
|
||||||
|
/// &[
|
||||||
|
/// 1.0, 0.0, 0.0,
|
||||||
|
/// 0.0, 0.0, 1.0,
|
||||||
|
/// 0.0, 0.0, 0.0,
|
||||||
|
/// 0.0, 1.0, 0.0
|
||||||
|
/// ],
|
||||||
|
/// );
|
||||||
|
/// let dm2 = DMatrix::from_row_slice(
|
||||||
|
/// 6,
|
||||||
|
/// 2,
|
||||||
|
/// &[
|
||||||
|
/// 1.0, 0.0,
|
||||||
|
/// 0.0, 1.0,
|
||||||
|
/// 0.0, 0.0,
|
||||||
|
/// 0.0, 1.0,
|
||||||
|
/// 0.0, 0.0,
|
||||||
|
/// 0.0, 0.0,
|
||||||
|
/// ],
|
||||||
|
/// );
|
||||||
|
/// let reshaped = dm1.reshape_generic(Dynamic::new(6), Dynamic::new(2));
|
||||||
|
/// assert_eq!(reshaped, dm2);
|
||||||
|
/// ```
|
||||||
|
pub fn reshape_generic<R2, C2>(
|
||||||
|
self,
|
||||||
|
new_nrows: R2,
|
||||||
|
new_ncols: C2,
|
||||||
|
) -> Matrix<N, R2, C2, S::Output>
|
||||||
|
where
|
||||||
|
R2: Dim,
|
||||||
|
C2: Dim,
|
||||||
|
S: ReshapableStorage<N, R, C, R2, C2>,
|
||||||
|
{
|
||||||
|
let data = self.data.reshape_generic(new_nrows, new_ncols);
|
||||||
|
Matrix::from_data(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
impl<N: Scalar> DMatrix<N> {
|
impl<N: Scalar> DMatrix<N> {
|
||||||
/// Resizes this matrix in-place.
|
/// Resizes this matrix in-place.
|
||||||
|
@ -159,13 +159,39 @@ impl<N: Scalar, R: Dim, C: Dim, S: Abomonation> Abomonation for Matrix<N, R, C,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compare")]
|
||||||
|
impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> matrixcompare_core::Matrix<N>
|
||||||
|
for Matrix<N, R, C, S>
|
||||||
|
{
|
||||||
|
fn rows(&self) -> usize {
|
||||||
|
self.nrows()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cols(&self) -> usize {
|
||||||
|
self.ncols()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn access(&self) -> matrixcompare_core::Access<N> {
|
||||||
|
matrixcompare_core::Access::Dense(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compare")]
|
||||||
|
impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> matrixcompare_core::DenseAccess<N>
|
||||||
|
for Matrix<N, R, C, S>
|
||||||
|
{
|
||||||
|
fn fetch_single(&self, row: usize, col: usize) -> N {
|
||||||
|
self.index((row, col)).clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<N: Scalar, R: Dim, C: Dim, S> Matrix<N, R, C, S> {
|
impl<N: Scalar, R: Dim, C: Dim, S> Matrix<N, R, C, S> {
|
||||||
/// Creates a new matrix with the given data without statically checking that the matrix
|
/// Creates a new matrix with the given data without statically checking that the matrix
|
||||||
/// dimension matches the storage dimension.
|
/// dimension matches the storage dimension.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn from_data_statically_unchecked(data: S) -> Matrix<N, R, C, S> {
|
pub unsafe fn from_data_statically_unchecked(data: S) -> Matrix<N, R, C, S> {
|
||||||
Matrix {
|
Matrix {
|
||||||
data: data,
|
data,
|
||||||
_phantoms: PhantomData,
|
_phantoms: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -538,8 +564,9 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
|||||||
|
|
||||||
let mut res = unsafe { MatrixMN::new_uninitialized_generic(nrows, ncols) };
|
let mut res = unsafe { MatrixMN::new_uninitialized_generic(nrows, ncols) };
|
||||||
|
|
||||||
assert!(
|
assert_eq!(
|
||||||
(nrows.value(), ncols.value()) == rhs.shape(),
|
(nrows.value(), ncols.value()),
|
||||||
|
rhs.shape(),
|
||||||
"Matrix simultaneous traversal error: dimension mismatch."
|
"Matrix simultaneous traversal error: dimension mismatch."
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -578,9 +605,14 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
|||||||
|
|
||||||
let mut res = unsafe { MatrixMN::new_uninitialized_generic(nrows, ncols) };
|
let mut res = unsafe { MatrixMN::new_uninitialized_generic(nrows, ncols) };
|
||||||
|
|
||||||
assert!(
|
assert_eq!(
|
||||||
(nrows.value(), ncols.value()) == b.shape()
|
(nrows.value(), ncols.value()),
|
||||||
&& (nrows.value(), ncols.value()) == c.shape(),
|
b.shape(),
|
||||||
|
"Matrix simultaneous traversal error: dimension mismatch."
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
(nrows.value(), ncols.value()),
|
||||||
|
c.shape(),
|
||||||
"Matrix simultaneous traversal error: dimension mismatch."
|
"Matrix simultaneous traversal error: dimension mismatch."
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -636,8 +668,9 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
|||||||
|
|
||||||
let mut res = init;
|
let mut res = init;
|
||||||
|
|
||||||
assert!(
|
assert_eq!(
|
||||||
(nrows.value(), ncols.value()) == rhs.shape(),
|
(nrows.value(), ncols.value()),
|
||||||
|
rhs.shape(),
|
||||||
"Matrix simultaneous traversal error: dimension mismatch."
|
"Matrix simultaneous traversal error: dimension mismatch."
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -884,8 +917,9 @@ impl<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
|
|||||||
{
|
{
|
||||||
let (nrows, ncols) = self.shape();
|
let (nrows, ncols) = self.shape();
|
||||||
|
|
||||||
assert!(
|
assert_eq!(
|
||||||
(nrows, ncols) == rhs.shape(),
|
(nrows, ncols),
|
||||||
|
rhs.shape(),
|
||||||
"Matrix simultaneous traversal error: dimension mismatch."
|
"Matrix simultaneous traversal error: dimension mismatch."
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -922,12 +956,14 @@ impl<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
|
|||||||
{
|
{
|
||||||
let (nrows, ncols) = self.shape();
|
let (nrows, ncols) = self.shape();
|
||||||
|
|
||||||
assert!(
|
assert_eq!(
|
||||||
(nrows, ncols) == b.shape(),
|
(nrows, ncols),
|
||||||
|
b.shape(),
|
||||||
"Matrix simultaneous traversal error: dimension mismatch."
|
"Matrix simultaneous traversal error: dimension mismatch."
|
||||||
);
|
);
|
||||||
assert!(
|
assert_eq!(
|
||||||
(nrows, ncols) == c.shape(),
|
(nrows, ncols),
|
||||||
|
c.shape(),
|
||||||
"Matrix simultaneous traversal error: dimension mismatch."
|
"Matrix simultaneous traversal error: dimension mismatch."
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1427,8 +1463,9 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn lt(&self, right: &Self) -> bool {
|
fn lt(&self, right: &Self) -> bool {
|
||||||
assert!(
|
assert_eq!(
|
||||||
self.shape() == right.shape(),
|
self.shape(),
|
||||||
|
right.shape(),
|
||||||
"Matrix comparison error: dimensions mismatch."
|
"Matrix comparison error: dimensions mismatch."
|
||||||
);
|
);
|
||||||
self.iter().zip(right.iter()).all(|(a, b)| a.lt(b))
|
self.iter().zip(right.iter()).all(|(a, b)| a.lt(b))
|
||||||
@ -1436,8 +1473,9 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn le(&self, right: &Self) -> bool {
|
fn le(&self, right: &Self) -> bool {
|
||||||
assert!(
|
assert_eq!(
|
||||||
self.shape() == right.shape(),
|
self.shape(),
|
||||||
|
right.shape(),
|
||||||
"Matrix comparison error: dimensions mismatch."
|
"Matrix comparison error: dimensions mismatch."
|
||||||
);
|
);
|
||||||
self.iter().zip(right.iter()).all(|(a, b)| a.le(b))
|
self.iter().zip(right.iter()).all(|(a, b)| a.le(b))
|
||||||
@ -1445,8 +1483,9 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn gt(&self, right: &Self) -> bool {
|
fn gt(&self, right: &Self) -> bool {
|
||||||
assert!(
|
assert_eq!(
|
||||||
self.shape() == right.shape(),
|
self.shape(),
|
||||||
|
right.shape(),
|
||||||
"Matrix comparison error: dimensions mismatch."
|
"Matrix comparison error: dimensions mismatch."
|
||||||
);
|
);
|
||||||
self.iter().zip(right.iter()).all(|(a, b)| a.gt(b))
|
self.iter().zip(right.iter()).all(|(a, b)| a.gt(b))
|
||||||
@ -1454,8 +1493,9 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ge(&self, right: &Self) -> bool {
|
fn ge(&self, right: &Self) -> bool {
|
||||||
assert!(
|
assert_eq!(
|
||||||
self.shape() == right.shape(),
|
self.shape(),
|
||||||
|
right.shape(),
|
||||||
"Matrix comparison error: dimensions mismatch."
|
"Matrix comparison error: dimensions mismatch."
|
||||||
);
|
);
|
||||||
self.iter().zip(right.iter()).all(|(a, b)| a.ge(b))
|
self.iter().zip(right.iter()).all(|(a, b)| a.ge(b))
|
||||||
@ -1602,7 +1642,11 @@ impl<N: Scalar + ClosedAdd + ClosedSub + ClosedMul, R: Dim, C: Dim, S: Storage<N
|
|||||||
+ SameNumberOfRows<R2, U2>
|
+ SameNumberOfRows<R2, U2>
|
||||||
+ SameNumberOfColumns<C2, U1>,
|
+ SameNumberOfColumns<C2, U1>,
|
||||||
{
|
{
|
||||||
assert!(self.shape() == (2, 1), "2D perpendicular product ");
|
assert!(
|
||||||
|
self.shape() == (2, 1),
|
||||||
|
"2D perpendicular product requires (2, 1) vector but found {:?}",
|
||||||
|
self.shape()
|
||||||
|
);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_unchecked((0, 0)).inlined_clone() * b.get_unchecked((1, 0)).inlined_clone()
|
self.get_unchecked((0, 0)).inlined_clone() * b.get_unchecked((1, 0)).inlined_clone()
|
||||||
@ -1626,13 +1670,11 @@ impl<N: Scalar + ClosedAdd + ClosedSub + ClosedMul, R: Dim, C: Dim, S: Storage<N
|
|||||||
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
|
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
|
||||||
{
|
{
|
||||||
let shape = self.shape();
|
let shape = self.shape();
|
||||||
assert!(
|
assert_eq!(shape, b.shape(), "Vector cross product dimension mismatch.");
|
||||||
shape == b.shape(),
|
|
||||||
"Vector cross product dimension mismatch."
|
|
||||||
);
|
|
||||||
assert!(
|
assert!(
|
||||||
(shape.0 == 3 && shape.1 == 1) || (shape.0 == 1 && shape.1 == 3),
|
(shape.0 == 3 && shape.1 == 1) || (shape.0 == 1 && shape.1 == 3),
|
||||||
"Vector cross product dimension mismatch."
|
"Vector cross product dimension mismatch: must be (3, 1) or (1, 3) but found {:?}.",
|
||||||
|
shape
|
||||||
);
|
);
|
||||||
|
|
||||||
if shape.0 == 3 {
|
if shape.0 == 3 {
|
||||||
|
@ -154,8 +154,8 @@ macro_rules! componentwise_binop_impl(
|
|||||||
out: &mut Matrix<N, R3, C3, SC>)
|
out: &mut Matrix<N, R3, C3, SC>)
|
||||||
where SB: Storage<N, R2, C2>,
|
where SB: Storage<N, R2, C2>,
|
||||||
SC: StorageMut<N, R3, C3> {
|
SC: StorageMut<N, R3, C3> {
|
||||||
assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
||||||
assert!(self.shape() == out.shape(), "Matrix addition/subtraction output dimensions mismatch.");
|
assert_eq!(self.shape(), out.shape(), "Matrix addition/subtraction output dimensions mismatch.");
|
||||||
|
|
||||||
// This is the most common case and should be deduced at compile-time.
|
// This is the most common case and should be deduced at compile-time.
|
||||||
// FIXME: use specialization instead?
|
// FIXME: use specialization instead?
|
||||||
@ -188,7 +188,7 @@ macro_rules! componentwise_binop_impl(
|
|||||||
C2: Dim,
|
C2: Dim,
|
||||||
SA: StorageMut<N, R1, C1>,
|
SA: StorageMut<N, R1, C1>,
|
||||||
SB: Storage<N, R2, C2> {
|
SB: Storage<N, R2, C2> {
|
||||||
assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
||||||
|
|
||||||
// This is the most common case and should be deduced at compile-time.
|
// This is the most common case and should be deduced at compile-time.
|
||||||
// FIXME: use specialization instead?
|
// FIXME: use specialization instead?
|
||||||
@ -218,7 +218,7 @@ macro_rules! componentwise_binop_impl(
|
|||||||
where R2: Dim,
|
where R2: Dim,
|
||||||
C2: Dim,
|
C2: Dim,
|
||||||
SB: StorageMut<N, R2, C2> {
|
SB: StorageMut<N, R2, C2> {
|
||||||
assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
||||||
|
|
||||||
// This is the most common case and should be deduced at compile-time.
|
// This is the most common case and should be deduced at compile-time.
|
||||||
// FIXME: use specialization instead?
|
// FIXME: use specialization instead?
|
||||||
@ -277,7 +277,7 @@ macro_rules! componentwise_binop_impl(
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn $method(self, rhs: &'b Matrix<N, R2, C2, SB>) -> Self::Output {
|
fn $method(self, rhs: &'b Matrix<N, R2, C2, SB>) -> Self::Output {
|
||||||
assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
||||||
let mut res = self.into_owned_sum::<R2, C2>();
|
let mut res = self.into_owned_sum::<R2, C2>();
|
||||||
res.$method_assign_statically_unchecked(rhs);
|
res.$method_assign_statically_unchecked(rhs);
|
||||||
res
|
res
|
||||||
@ -296,7 +296,7 @@ macro_rules! componentwise_binop_impl(
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn $method(self, rhs: Matrix<N, R2, C2, SB>) -> Self::Output {
|
fn $method(self, rhs: Matrix<N, R2, C2, SB>) -> Self::Output {
|
||||||
let mut rhs = rhs.into_owned_sum::<R1, C1>();
|
let mut rhs = rhs.into_owned_sum::<R1, C1>();
|
||||||
assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
|
||||||
self.$method_assign_statically_unchecked_rhs(&mut rhs);
|
self.$method_assign_statically_unchecked_rhs(&mut rhs);
|
||||||
rhs
|
rhs
|
||||||
}
|
}
|
||||||
@ -728,11 +728,21 @@ where
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
nrows1 == nrows2,
|
nrows1 == nrows2,
|
||||||
"Matrix multiplication dimensions mismatch."
|
"Matrix multiplication dimensions mismatch {:?} and {:?}: left rows != right rows.",
|
||||||
|
self.shape(),
|
||||||
|
rhs.shape()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
nrows3 == ncols1 && ncols3 == ncols2,
|
ncols1 == nrows3,
|
||||||
"Matrix multiplication output dimensions mismatch."
|
"Matrix multiplication output dimensions mismatch {:?} and {:?}: left cols != right rows.",
|
||||||
|
self.shape(),
|
||||||
|
out.shape()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
ncols2 == ncols3,
|
||||||
|
"Matrix multiplication output dimensions mismatch {:?} and {:?}: left cols != right cols",
|
||||||
|
rhs.shape(),
|
||||||
|
out.shape()
|
||||||
);
|
);
|
||||||
|
|
||||||
for i in 0..ncols1 {
|
for i in 0..ncols1 {
|
||||||
|
@ -171,7 +171,7 @@ pub unsafe trait StorageMut<N: Scalar, R: Dim, C: Dim = U1>: Storage<N, R, C> {
|
|||||||
|
|
||||||
/// A matrix storage that is stored contiguously in memory.
|
/// A matrix storage that is stored contiguously in memory.
|
||||||
///
|
///
|
||||||
/// The storage requirement means that for any value of `i` in `[0, nrows * ncols[`, the value
|
/// The storage requirement means that for any value of `i` in `[0, nrows * ncols - 1]`, the value
|
||||||
/// `.get_unchecked_linear` returns one of the matrix component. This trait is unsafe because
|
/// `.get_unchecked_linear` returns one of the matrix component. This trait is unsafe because
|
||||||
/// failing to comply to this may cause Undefined Behaviors.
|
/// failing to comply to this may cause Undefined Behaviors.
|
||||||
pub unsafe trait ContiguousStorage<N: Scalar, R: Dim, C: Dim = U1>:
|
pub unsafe trait ContiguousStorage<N: Scalar, R: Dim, C: Dim = U1>:
|
||||||
@ -181,10 +181,26 @@ pub unsafe trait ContiguousStorage<N: Scalar, R: Dim, C: Dim = U1>:
|
|||||||
|
|
||||||
/// A mutable matrix storage that is stored contiguously in memory.
|
/// A mutable matrix storage that is stored contiguously in memory.
|
||||||
///
|
///
|
||||||
/// The storage requirement means that for any value of `i` in `[0, nrows * ncols[`, the value
|
/// The storage requirement means that for any value of `i` in `[0, nrows * ncols - 1]`, the value
|
||||||
/// `.get_unchecked_linear` returns one of the matrix component. This trait is unsafe because
|
/// `.get_unchecked_linear` returns one of the matrix component. This trait is unsafe because
|
||||||
/// failing to comply to this may cause Undefined Behaviors.
|
/// failing to comply to this may cause Undefined Behaviors.
|
||||||
pub unsafe trait ContiguousStorageMut<N: Scalar, R: Dim, C: Dim = U1>:
|
pub unsafe trait ContiguousStorageMut<N: Scalar, R: Dim, C: Dim = U1>:
|
||||||
ContiguousStorage<N, R, C> + StorageMut<N, R, C>
|
ContiguousStorage<N, R, C> + StorageMut<N, R, C>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A matrix storage that can be reshaped in-place.
|
||||||
|
pub trait ReshapableStorage<N, R1, C1, R2, C2>: Storage<N, R1, C1>
|
||||||
|
where
|
||||||
|
N: Scalar,
|
||||||
|
R1: Dim,
|
||||||
|
C1: Dim,
|
||||||
|
R2: Dim,
|
||||||
|
C2: Dim,
|
||||||
|
{
|
||||||
|
/// The reshaped storage type.
|
||||||
|
type Output: Storage<N, R2, C2>;
|
||||||
|
|
||||||
|
/// Reshapes the storage into the output storage type.
|
||||||
|
fn reshape_generic(self, nrows: R2, ncols: C2) -> Self::Output;
|
||||||
|
}
|
||||||
|
@ -8,7 +8,9 @@ use crate::base::allocator::Allocator;
|
|||||||
use crate::base::constraint::{SameNumberOfRows, ShapeConstraint};
|
use crate::base::constraint::{SameNumberOfRows, ShapeConstraint};
|
||||||
use crate::base::default_allocator::DefaultAllocator;
|
use crate::base::default_allocator::DefaultAllocator;
|
||||||
use crate::base::dimension::{Dim, DimName, Dynamic, U1};
|
use crate::base::dimension::{Dim, DimName, Dynamic, U1};
|
||||||
use crate::base::storage::{ContiguousStorage, ContiguousStorageMut, Owned, Storage, StorageMut};
|
use crate::base::storage::{
|
||||||
|
ContiguousStorage, ContiguousStorageMut, Owned, ReshapableStorage, Storage, StorageMut,
|
||||||
|
};
|
||||||
use crate::base::{Scalar, Vector};
|
use crate::base::{Scalar, Vector};
|
||||||
|
|
||||||
#[cfg(feature = "abomonation-serialize")]
|
#[cfg(feature = "abomonation-serialize")]
|
||||||
@ -41,11 +43,7 @@ impl<N, R: Dim, C: Dim> VecStorage<N, R, C> {
|
|||||||
nrows.value() * ncols.value() == data.len(),
|
nrows.value() * ncols.value() == data.len(),
|
||||||
"Data storage buffer dimension mismatch."
|
"Data storage buffer dimension mismatch."
|
||||||
);
|
);
|
||||||
Self {
|
Self { data, nrows, ncols }
|
||||||
data: data,
|
|
||||||
nrows: nrows,
|
|
||||||
ncols: ncols,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The underlying data storage.
|
/// The underlying data storage.
|
||||||
@ -229,6 +227,42 @@ unsafe impl<N: Scalar, C: Dim> ContiguousStorageMut<N, Dynamic, C> for VecStorag
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N, C1, C2> ReshapableStorage<N, Dynamic, C1, Dynamic, C2> for VecStorage<N, Dynamic, C1>
|
||||||
|
where
|
||||||
|
N: Scalar,
|
||||||
|
C1: Dim,
|
||||||
|
C2: Dim,
|
||||||
|
{
|
||||||
|
type Output = VecStorage<N, Dynamic, C2>;
|
||||||
|
|
||||||
|
fn reshape_generic(self, nrows: Dynamic, ncols: C2) -> Self::Output {
|
||||||
|
assert_eq!(nrows.value() * ncols.value(), self.data.len());
|
||||||
|
VecStorage {
|
||||||
|
data: self.data,
|
||||||
|
nrows,
|
||||||
|
ncols,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N, C1, R2> ReshapableStorage<N, Dynamic, C1, R2, Dynamic> for VecStorage<N, Dynamic, C1>
|
||||||
|
where
|
||||||
|
N: Scalar,
|
||||||
|
C1: Dim,
|
||||||
|
R2: DimName,
|
||||||
|
{
|
||||||
|
type Output = VecStorage<N, R2, Dynamic>;
|
||||||
|
|
||||||
|
fn reshape_generic(self, nrows: R2, ncols: Dynamic) -> Self::Output {
|
||||||
|
assert_eq!(nrows.value() * ncols.value(), self.data.len());
|
||||||
|
VecStorage {
|
||||||
|
data: self.data,
|
||||||
|
nrows,
|
||||||
|
ncols,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl<N: Scalar, R: DimName> StorageMut<N, R, Dynamic> for VecStorage<N, R, Dynamic>
|
unsafe impl<N: Scalar, R: DimName> StorageMut<N, R, Dynamic> for VecStorage<N, R, Dynamic>
|
||||||
where
|
where
|
||||||
DefaultAllocator: Allocator<N, R, Dynamic, Buffer = Self>,
|
DefaultAllocator: Allocator<N, R, Dynamic, Buffer = Self>,
|
||||||
@ -244,6 +278,42 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N, R1, C2> ReshapableStorage<N, R1, Dynamic, Dynamic, C2> for VecStorage<N, R1, Dynamic>
|
||||||
|
where
|
||||||
|
N: Scalar,
|
||||||
|
R1: DimName,
|
||||||
|
C2: Dim,
|
||||||
|
{
|
||||||
|
type Output = VecStorage<N, Dynamic, C2>;
|
||||||
|
|
||||||
|
fn reshape_generic(self, nrows: Dynamic, ncols: C2) -> Self::Output {
|
||||||
|
assert_eq!(nrows.value() * ncols.value(), self.data.len());
|
||||||
|
VecStorage {
|
||||||
|
data: self.data,
|
||||||
|
nrows,
|
||||||
|
ncols,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N, R1, R2> ReshapableStorage<N, R1, Dynamic, R2, Dynamic> for VecStorage<N, R1, Dynamic>
|
||||||
|
where
|
||||||
|
N: Scalar,
|
||||||
|
R1: DimName,
|
||||||
|
R2: DimName,
|
||||||
|
{
|
||||||
|
type Output = VecStorage<N, R2, Dynamic>;
|
||||||
|
|
||||||
|
fn reshape_generic(self, nrows: R2, ncols: Dynamic) -> Self::Output {
|
||||||
|
assert_eq!(nrows.value() * ncols.value(), self.data.len());
|
||||||
|
VecStorage {
|
||||||
|
data: self.data,
|
||||||
|
nrows,
|
||||||
|
ncols,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "abomonation-serialize")]
|
#[cfg(feature = "abomonation-serialize")]
|
||||||
impl<N: Abomonation, R: Dim, C: Dim> Abomonation for VecStorage<N, R, C> {
|
impl<N: Abomonation, R: Dim, C: Dim> Abomonation for VecStorage<N, R, C> {
|
||||||
unsafe fn entomb<W: Write>(&self, writer: &mut W) -> IOResult<()> {
|
unsafe fn entomb<W: Write>(&self, writer: &mut W) -> IOResult<()> {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::allocator::Allocator;
|
use crate::allocator::Allocator;
|
||||||
use crate::geometry::{Rotation, UnitComplex, UnitQuaternion};
|
use crate::geometry::{Rotation, UnitComplex, UnitQuaternion};
|
||||||
use crate::{DefaultAllocator, DimName, Point, Scalar, SimdRealField, VectorN, U2, U3};
|
use crate::{DefaultAllocator, DimName, Point, Scalar, SimdRealField, Unit, VectorN, U2, U3};
|
||||||
|
|
||||||
use simba::scalar::ClosedMul;
|
use simba::scalar::ClosedMul;
|
||||||
|
|
||||||
@ -24,6 +24,13 @@ pub trait AbstractRotation<N: Scalar, D: DimName>: PartialEq + ClosedMul + Clone
|
|||||||
fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D>
|
fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D>
|
||||||
where
|
where
|
||||||
DefaultAllocator: Allocator<N, D>;
|
DefaultAllocator: Allocator<N, D>;
|
||||||
|
/// Apply the inverse rotation to the given unit vector.
|
||||||
|
fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>>
|
||||||
|
where
|
||||||
|
DefaultAllocator: Allocator<N, D>,
|
||||||
|
{
|
||||||
|
Unit::new_unchecked(self.inverse_transform_vector(&**v))
|
||||||
|
}
|
||||||
/// Apply the inverse rotation to the given point.
|
/// Apply the inverse rotation to the given point.
|
||||||
fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D>
|
fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D>
|
||||||
where
|
where
|
||||||
@ -74,6 +81,14 @@ where
|
|||||||
self.inverse_transform_vector(v)
|
self.inverse_transform_vector(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>>
|
||||||
|
where
|
||||||
|
DefaultAllocator: Allocator<N, D>,
|
||||||
|
{
|
||||||
|
self.inverse_transform_unit_vector(v)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D>
|
fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D>
|
||||||
where
|
where
|
||||||
|
@ -14,10 +14,12 @@ use simba::scalar::{RealField, SubsetOf};
|
|||||||
use simba::simd::SimdRealField;
|
use simba::simd::SimdRealField;
|
||||||
|
|
||||||
use crate::base::allocator::Allocator;
|
use crate::base::allocator::Allocator;
|
||||||
use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1};
|
use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1, U2, U3};
|
||||||
use crate::base::storage::Owned;
|
use crate::base::storage::Owned;
|
||||||
use crate::base::{DefaultAllocator, MatrixN, Scalar, VectorN};
|
use crate::base::{DefaultAllocator, MatrixN, Scalar, Unit, VectorN};
|
||||||
use crate::geometry::{AbstractRotation, Point, Translation};
|
use crate::geometry::{
|
||||||
|
AbstractRotation, Point, Rotation2, Rotation3, Translation, UnitComplex, UnitQuaternion,
|
||||||
|
};
|
||||||
|
|
||||||
/// A direct isometry, i.e., a rotation followed by a translation, aka. a rigid-body motion, aka. an element of a Special Euclidean (SE) group.
|
/// A direct isometry, i.e., a rotation followed by a translation, aka. a rigid-body motion, aka. an element of a Special Euclidean (SE) group.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -82,21 +84,23 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: Scalar + Copy, D: DimName + Copy, R: AbstractRotation<N, D> + Copy> Copy
|
impl<N: Scalar + Copy, D: DimName + Copy, R: Copy> Copy for Isometry<N, D, R>
|
||||||
for Isometry<N, D, R>
|
|
||||||
where
|
where
|
||||||
DefaultAllocator: Allocator<N, D>,
|
DefaultAllocator: Allocator<N, D>,
|
||||||
Owned<N, D>: Copy,
|
Owned<N, D>: Copy,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: Scalar, D: DimName, R: AbstractRotation<N, D> + Clone> Clone for Isometry<N, D, R>
|
impl<N: Scalar, D: DimName, R: Clone> Clone for Isometry<N, D, R>
|
||||||
where
|
where
|
||||||
DefaultAllocator: Allocator<N, D>,
|
DefaultAllocator: Allocator<N, D>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self::from_parts(self.translation.clone(), self.rotation.clone())
|
Self {
|
||||||
|
rotation: self.rotation.clone(),
|
||||||
|
translation: self.translation.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,6 +352,237 @@ where
|
|||||||
pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> {
|
pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> {
|
||||||
self.rotation.inverse_transform_vector(v)
|
self.rotation.inverse_transform_vector(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transform the given unit vector by the inverse of this isometry, ignoring the
|
||||||
|
/// translation component of the isometry. This may be
|
||||||
|
/// less expensive than computing the entire isometry inverse and then
|
||||||
|
/// transforming the point.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate approx;
|
||||||
|
/// # use std::f32;
|
||||||
|
/// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3};
|
||||||
|
/// let tra = Translation3::new(0.0, 0.0, 3.0);
|
||||||
|
/// let rot = UnitQuaternion::from_scaled_axis(Vector3::z() * f32::consts::FRAC_PI_2);
|
||||||
|
/// let iso = Isometry3::from_parts(tra, rot);
|
||||||
|
///
|
||||||
|
/// let transformed_point = iso.inverse_transform_unit_vector(&Vector3::x_axis());
|
||||||
|
/// assert_relative_eq!(transformed_point, -Vector3::y_axis(), epsilon = 1.0e-6);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>> {
|
||||||
|
self.rotation.inverse_transform_unit_vector(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: SimdRealField> Isometry<N, U3, UnitQuaternion<N>> {
|
||||||
|
/// Interpolates between two isometries using a linear interpolation for the translation part,
|
||||||
|
/// and a spherical interpolation for the rotation part.
|
||||||
|
///
|
||||||
|
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
|
||||||
|
/// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use nalgebra::{Vector3, Translation3, Isometry3, UnitQuaternion};
|
||||||
|
///
|
||||||
|
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
|
||||||
|
/// let t2 = Translation3::new(4.0, 8.0, 12.0);
|
||||||
|
/// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
|
||||||
|
/// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
|
||||||
|
/// let iso1 = Isometry3::from_parts(t1, q1);
|
||||||
|
/// let iso2 = Isometry3::from_parts(t2, q2);
|
||||||
|
///
|
||||||
|
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
|
||||||
|
///
|
||||||
|
/// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0));
|
||||||
|
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
|
||||||
|
where
|
||||||
|
N: RealField,
|
||||||
|
{
|
||||||
|
let tr = self.translation.vector.lerp(&other.translation.vector, t);
|
||||||
|
let rot = self.rotation.slerp(&other.rotation, t);
|
||||||
|
Self::from_parts(tr.into(), rot)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to interpolate between two isometries using a linear interpolation for the translation part,
|
||||||
|
/// and a spherical interpolation for the rotation part.
|
||||||
|
///
|
||||||
|
/// Retuns `None` if the angle between both rotations is 180 degrees (in which case the interpolation
|
||||||
|
/// is not well-defined).
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use nalgebra::{Vector3, Translation3, Isometry3, UnitQuaternion};
|
||||||
|
///
|
||||||
|
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
|
||||||
|
/// let t2 = Translation3::new(4.0, 8.0, 12.0);
|
||||||
|
/// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
|
||||||
|
/// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
|
||||||
|
/// let iso1 = Isometry3::from_parts(t1, q1);
|
||||||
|
/// let iso2 = Isometry3::from_parts(t2, q2);
|
||||||
|
///
|
||||||
|
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
|
||||||
|
///
|
||||||
|
/// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0));
|
||||||
|
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn try_lerp_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self>
|
||||||
|
where
|
||||||
|
N: RealField,
|
||||||
|
{
|
||||||
|
let tr = self.translation.vector.lerp(&other.translation.vector, t);
|
||||||
|
let rot = self.rotation.try_slerp(&other.rotation, t, epsilon)?;
|
||||||
|
Some(Self::from_parts(tr.into(), rot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: SimdRealField> Isometry<N, U3, Rotation3<N>> {
|
||||||
|
/// Interpolates between two isometries using a linear interpolation for the translation part,
|
||||||
|
/// and a spherical interpolation for the rotation part.
|
||||||
|
///
|
||||||
|
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
|
||||||
|
/// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use nalgebra::{Vector3, Translation3, Rotation3, IsometryMatrix3};
|
||||||
|
///
|
||||||
|
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
|
||||||
|
/// let t2 = Translation3::new(4.0, 8.0, 12.0);
|
||||||
|
/// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
|
||||||
|
/// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
|
||||||
|
/// let iso1 = IsometryMatrix3::from_parts(t1, q1);
|
||||||
|
/// let iso2 = IsometryMatrix3::from_parts(t2, q2);
|
||||||
|
///
|
||||||
|
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
|
||||||
|
///
|
||||||
|
/// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0));
|
||||||
|
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
|
||||||
|
where
|
||||||
|
N: RealField,
|
||||||
|
{
|
||||||
|
let tr = self.translation.vector.lerp(&other.translation.vector, t);
|
||||||
|
let rot = self.rotation.slerp(&other.rotation, t);
|
||||||
|
Self::from_parts(tr.into(), rot)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to interpolate between two isometries using a linear interpolation for the translation part,
|
||||||
|
/// and a spherical interpolation for the rotation part.
|
||||||
|
///
|
||||||
|
/// Retuns `None` if the angle between both rotations is 180 degrees (in which case the interpolation
|
||||||
|
/// is not well-defined).
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use nalgebra::{Vector3, Translation3, Rotation3, IsometryMatrix3};
|
||||||
|
///
|
||||||
|
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
|
||||||
|
/// let t2 = Translation3::new(4.0, 8.0, 12.0);
|
||||||
|
/// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
|
||||||
|
/// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
|
||||||
|
/// let iso1 = IsometryMatrix3::from_parts(t1, q1);
|
||||||
|
/// let iso2 = IsometryMatrix3::from_parts(t2, q2);
|
||||||
|
///
|
||||||
|
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
|
||||||
|
///
|
||||||
|
/// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0));
|
||||||
|
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn try_lerp_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self>
|
||||||
|
where
|
||||||
|
N: RealField,
|
||||||
|
{
|
||||||
|
let tr = self.translation.vector.lerp(&other.translation.vector, t);
|
||||||
|
let rot = self.rotation.try_slerp(&other.rotation, t, epsilon)?;
|
||||||
|
Some(Self::from_parts(tr.into(), rot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: SimdRealField> Isometry<N, U2, UnitComplex<N>> {
|
||||||
|
/// Interpolates between two isometries using a linear interpolation for the translation part,
|
||||||
|
/// and a spherical interpolation for the rotation part.
|
||||||
|
///
|
||||||
|
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
|
||||||
|
/// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate approx;
|
||||||
|
/// # use nalgebra::{Vector2, Translation2, UnitComplex, Isometry2};
|
||||||
|
///
|
||||||
|
/// let t1 = Translation2::new(1.0, 2.0);
|
||||||
|
/// let t2 = Translation2::new(4.0, 8.0);
|
||||||
|
/// let q1 = UnitComplex::new(std::f32::consts::FRAC_PI_4);
|
||||||
|
/// let q2 = UnitComplex::new(-std::f32::consts::PI);
|
||||||
|
/// let iso1 = Isometry2::from_parts(t1, q1);
|
||||||
|
/// let iso2 = Isometry2::from_parts(t2, q2);
|
||||||
|
///
|
||||||
|
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
|
||||||
|
///
|
||||||
|
/// assert_eq!(iso3.translation.vector, Vector2::new(2.0, 4.0));
|
||||||
|
/// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
|
||||||
|
where
|
||||||
|
N: RealField,
|
||||||
|
{
|
||||||
|
let tr = self.translation.vector.lerp(&other.translation.vector, t);
|
||||||
|
let rot = self.rotation.slerp(&other.rotation, t);
|
||||||
|
Self::from_parts(tr.into(), rot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: SimdRealField> Isometry<N, U2, Rotation2<N>> {
|
||||||
|
/// Interpolates between two isometries using a linear interpolation for the translation part,
|
||||||
|
/// and a spherical interpolation for the rotation part.
|
||||||
|
///
|
||||||
|
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
|
||||||
|
/// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate approx;
|
||||||
|
/// # use nalgebra::{Vector2, Translation2, Rotation2, IsometryMatrix2};
|
||||||
|
///
|
||||||
|
/// let t1 = Translation2::new(1.0, 2.0);
|
||||||
|
/// let t2 = Translation2::new(4.0, 8.0);
|
||||||
|
/// let q1 = Rotation2::new(std::f32::consts::FRAC_PI_4);
|
||||||
|
/// let q2 = Rotation2::new(-std::f32::consts::PI);
|
||||||
|
/// let iso1 = IsometryMatrix2::from_parts(t1, q1);
|
||||||
|
/// let iso2 = IsometryMatrix2::from_parts(t2, q2);
|
||||||
|
///
|
||||||
|
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
|
||||||
|
///
|
||||||
|
/// assert_eq!(iso3.translation.vector, Vector2::new(2.0, 4.0));
|
||||||
|
/// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
|
||||||
|
where
|
||||||
|
N: RealField,
|
||||||
|
{
|
||||||
|
let tr = self.translation.vector.lerp(&other.translation.vector, t);
|
||||||
|
let rot = self.rotation.slerp(&other.rotation, t);
|
||||||
|
Self::from_parts(tr.into(), rot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation
|
// NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation
|
||||||
|
@ -13,3 +13,15 @@ pub type IsometryMatrix2<N> = Isometry<N, U2, Rotation2<N>>;
|
|||||||
|
|
||||||
/// A 3-dimensional direct isometry using a rotation matrix for its rotational part. Also known as a rigid-body motion, or as an element of SE(3).
|
/// A 3-dimensional direct isometry using a rotation matrix for its rotational part. Also known as a rigid-body motion, or as an element of SE(3).
|
||||||
pub type IsometryMatrix3<N> = Isometry<N, U3, Rotation3<N>>;
|
pub type IsometryMatrix3<N> = Isometry<N, U3, Rotation3<N>>;
|
||||||
|
|
||||||
|
// This tests that the types correctly implement `Copy`, without having to run tests
|
||||||
|
// (when targeting no-std for example).
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn ensure_copy() {
|
||||||
|
fn is_copy<T: Copy>() {}
|
||||||
|
|
||||||
|
is_copy::<IsometryMatrix2<f32>>();
|
||||||
|
is_copy::<IsometryMatrix3<f32>>();
|
||||||
|
is_copy::<Isometry2<f32>>();
|
||||||
|
is_copy::<Isometry3<f32>>();
|
||||||
|
}
|
||||||
|
@ -151,6 +151,17 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N: SimdRealField, D: DimName, R: AbstractRotation<N, D>> From<Translation<N, D>>
|
||||||
|
for Isometry<N, D, R>
|
||||||
|
where
|
||||||
|
DefaultAllocator: Allocator<N, D>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn from(tra: Translation<N, D>) -> Self {
|
||||||
|
Self::from_parts(tra, R::identity())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<N: SimdRealField, D: DimName, R> From<Isometry<N, D, R>> for MatrixN<N, DimNameSum<D, U1>>
|
impl<N: SimdRealField, D: DimName, R> From<Isometry<N, D, R>> for MatrixN<N, DimNameSum<D, U1>>
|
||||||
where
|
where
|
||||||
D: DimNameAdd<U1>,
|
D: DimNameAdd<U1>,
|
||||||
|
@ -149,6 +149,7 @@ isometry_binop_impl_all!(
|
|||||||
[ref ref] => {
|
[ref ref] => {
|
||||||
let shift = self.rotation.transform_vector(&rhs.translation.vector);
|
let shift = self.rotation.transform_vector(&rhs.translation.vector);
|
||||||
|
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
Isometry::from_parts(Translation::from(&self.translation.vector + shift),
|
Isometry::from_parts(Translation::from(&self.translation.vector + shift),
|
||||||
self.rotation.clone() * rhs.rotation.clone()) // FIXME: too bad we have to clone.
|
self.rotation.clone() * rhs.rotation.clone()) // FIXME: too bad we have to clone.
|
||||||
};
|
};
|
||||||
@ -157,10 +158,10 @@ isometry_binop_impl_all!(
|
|||||||
isometry_binop_impl_all!(
|
isometry_binop_impl_all!(
|
||||||
Div, div;
|
Div, div;
|
||||||
self: Isometry<N, D, R>, rhs: Isometry<N, D, R>, Output = Isometry<N, D, R>;
|
self: Isometry<N, D, R>, rhs: Isometry<N, D, R>, Output = Isometry<N, D, R>;
|
||||||
[val val] => self * rhs.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref val] => self * rhs.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[val ref] => self * rhs.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref ref] => self * rhs.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Isometry ×= Translation
|
// Isometry ×= Translation
|
||||||
@ -289,6 +290,7 @@ isometry_binop_impl_all!(
|
|||||||
[ref val] => self * &right;
|
[ref val] => self * &right;
|
||||||
[val ref] => &self * right;
|
[val ref] => &self * right;
|
||||||
[ref ref] => {
|
[ref ref] => {
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
let new_tr = &self.translation.vector + self.rotation.transform_vector(&right.vector);
|
let new_tr = &self.translation.vector + self.rotation.transform_vector(&right.vector);
|
||||||
Isometry::from_parts(Translation::from(new_tr), self.rotation.clone())
|
Isometry::from_parts(Translation::from(new_tr), self.rotation.clone())
|
||||||
};
|
};
|
||||||
@ -427,10 +429,10 @@ isometry_from_composition_impl_all!(
|
|||||||
self: Rotation<N, D>, right: Isometry<N, D, Rotation<N, D>>,
|
self: Rotation<N, D>, right: Isometry<N, D, Rotation<N, D>>,
|
||||||
Output = Isometry<N, D, Rotation<N, D>>;
|
Output = Isometry<N, D, Rotation<N, D>>;
|
||||||
// FIXME: don't call inverse explicitly?
|
// FIXME: don't call inverse explicitly?
|
||||||
[val val] => self * right.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref val] => self * right.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[val ref] => self * right.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref ref] => self * right.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Isometry × UnitQuaternion
|
// Isometry × UnitQuaternion
|
||||||
@ -479,10 +481,10 @@ isometry_from_composition_impl_all!(
|
|||||||
self: UnitQuaternion<N>, right: Isometry<N, U3, UnitQuaternion<N>>,
|
self: UnitQuaternion<N>, right: Isometry<N, U3, UnitQuaternion<N>>,
|
||||||
Output = Isometry<N, U3, UnitQuaternion<N>>;
|
Output = Isometry<N, U3, UnitQuaternion<N>>;
|
||||||
// FIXME: don't call inverse explicitly?
|
// FIXME: don't call inverse explicitly?
|
||||||
[val val] => self * right.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref val] => self * right.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[val ref] => self * right.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref ref] => self * right.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Translation × Rotation
|
// Translation × Rotation
|
||||||
|
@ -139,7 +139,7 @@ impl<N: RealField> Orthographic3<N> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_matrix_unchecked(matrix: Matrix4<N>) -> Self {
|
pub fn from_matrix_unchecked(matrix: Matrix4<N>) -> Self {
|
||||||
Self { matrix: matrix }
|
Self { matrix }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new orthographic projection matrix from an aspect ratio and the vertical field of view.
|
/// Creates a new orthographic projection matrix from an aspect ratio and the vertical field of view.
|
||||||
@ -151,7 +151,7 @@ impl<N: RealField> Orthographic3<N> {
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!relative_eq!(aspect, N::zero()),
|
!relative_eq!(aspect, N::zero()),
|
||||||
"The apsect ratio must not be zero."
|
"The aspect ratio must not be zero."
|
||||||
);
|
);
|
||||||
|
|
||||||
let half: N = crate::convert(0.5);
|
let half: N = crate::convert(0.5);
|
||||||
|
@ -75,7 +75,7 @@ impl<N: RealField> Perspective3<N> {
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!relative_eq!(aspect, N::zero()),
|
!relative_eq!(aspect, N::zero()),
|
||||||
"The apsect ratio must not be zero."
|
"The aspect ratio must not be zero."
|
||||||
);
|
);
|
||||||
|
|
||||||
let matrix = Matrix4::identity();
|
let matrix = Matrix4::identity();
|
||||||
@ -97,7 +97,7 @@ impl<N: RealField> Perspective3<N> {
|
|||||||
/// projection.
|
/// projection.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_matrix_unchecked(matrix: Matrix4<N>) -> Self {
|
pub fn from_matrix_unchecked(matrix: Matrix4<N>) -> Self {
|
||||||
Self { matrix: matrix }
|
Self { matrix }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the inverse of the underlying homogeneous matrix.
|
/// Retrieves the inverse of the underlying homogeneous matrix.
|
||||||
@ -294,7 +294,7 @@ impl<N: RealField + Arbitrary> Arbitrary for Perspective3<N> {
|
|||||||
|
|
||||||
impl<N: RealField> From<Perspective3<N>> for Matrix4<N> {
|
impl<N: RealField> From<Perspective3<N>> for Matrix4<N> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(orth: Perspective3<N>) -> Self {
|
fn from(pers: Perspective3<N>) -> Self {
|
||||||
orth.into_inner()
|
pers.into_inner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,45 @@ impl<N: Scalar, D: DimName> Point<N, D>
|
|||||||
where
|
where
|
||||||
DefaultAllocator: Allocator<N, D>,
|
DefaultAllocator: Allocator<N, D>,
|
||||||
{
|
{
|
||||||
|
/// Returns a point containing the result of `f` applied to each of its entries.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// # use nalgebra::{Point2, Point3};
|
||||||
|
/// let p = Point2::new(1.0, 2.0);
|
||||||
|
/// assert_eq!(p.map(|e| e * 10.0), Point2::new(10.0, 20.0));
|
||||||
|
///
|
||||||
|
/// // This works in any dimension.
|
||||||
|
/// let p = Point3::new(1.1, 2.1, 3.1);
|
||||||
|
/// assert_eq!(p.map(|e| e as u32), Point3::new(1, 2, 3));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn map<N2: Scalar, F: FnMut(N) -> N2>(&self, f: F) -> Point<N2, D>
|
||||||
|
where
|
||||||
|
DefaultAllocator: Allocator<N2, D>,
|
||||||
|
{
|
||||||
|
self.coords.map(f).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces each component of `self` by the result of a closure `f` applied on it.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// # use nalgebra::{Point2, Point3};
|
||||||
|
/// let mut p = Point2::new(1.0, 2.0);
|
||||||
|
/// p.apply(|e| e * 10.0);
|
||||||
|
/// assert_eq!(p, Point2::new(10.0, 20.0));
|
||||||
|
///
|
||||||
|
/// // This works in any dimension.
|
||||||
|
/// let mut p = Point3::new(1.0, 2.0, 3.0);
|
||||||
|
/// p.apply(|e| e * 10.0);
|
||||||
|
/// assert_eq!(p, Point3::new(10.0, 20.0, 30.0));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn apply<F: FnMut(N) -> N>(&mut self, f: F) {
|
||||||
|
self.coords.apply(f)
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts this point into a vector in homogeneous coordinates, i.e., appends a `1` at the
|
/// Converts this point into a vector in homogeneous coordinates, i.e., appends a `1` at the
|
||||||
/// end of it.
|
/// end of it.
|
||||||
///
|
///
|
||||||
@ -135,7 +174,7 @@ where
|
|||||||
#[deprecated(note = "Use Point::from(vector) instead.")]
|
#[deprecated(note = "Use Point::from(vector) instead.")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_coordinates(coords: VectorN<N, D>) -> Self {
|
pub fn from_coordinates(coords: VectorN<N, D>) -> Self {
|
||||||
Self { coords: coords }
|
Self { coords }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The dimension of this point.
|
/// The dimension of this point.
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use std::mem;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use crate::base::allocator::Allocator;
|
use crate::base::allocator::Allocator;
|
||||||
@ -22,7 +21,7 @@ macro_rules! deref_impl(
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
unsafe { mem::transmute(self) }
|
&*self.coords
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +29,7 @@ macro_rules! deref_impl(
|
|||||||
where DefaultAllocator: Allocator<N, $D> {
|
where DefaultAllocator: Allocator<N, $D> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
unsafe { mem::transmute(self) }
|
&mut *self.coords
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,6 +389,7 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn outer(&self, other: &Self) -> Self {
|
pub fn outer(&self, other: &Self) -> Self {
|
||||||
|
#[allow(clippy::eq_op)]
|
||||||
(self * other - other * self).half()
|
(self * other - other * self).half()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1542,6 +1543,26 @@ where
|
|||||||
pub fn inverse_transform_vector(&self, v: &Vector3<N>) -> Vector3<N> {
|
pub fn inverse_transform_vector(&self, v: &Vector3<N>) -> Vector3<N> {
|
||||||
self.inverse() * v
|
self.inverse() * v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rotate a vector by the inverse of this unit quaternion. This may be
|
||||||
|
/// cheaper than inverting the unit quaternion and transforming the
|
||||||
|
/// vector.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate approx;
|
||||||
|
/// # use std::f32;
|
||||||
|
/// # use nalgebra::{UnitQuaternion, Vector3};
|
||||||
|
/// let rot = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_2);
|
||||||
|
/// let transformed_vector = rot.inverse_transform_unit_vector(&Vector3::x_axis());
|
||||||
|
///
|
||||||
|
/// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector3<N>>) -> Unit<Vector3<N>> {
|
||||||
|
self.inverse() * v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: RealField> Default for UnitQuaternion<N> {
|
impl<N: RealField> Default for UnitQuaternion<N> {
|
||||||
|
@ -374,7 +374,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The unit quaternion needed to make `a` and `b` be collinear and point toward the same
|
/// The unit quaternion needed to make `a` and `b` be collinear and point toward the same
|
||||||
/// direction.
|
/// direction. Returns `None` if both `a` and `b` are collinear and point to opposite directions, as then the
|
||||||
|
/// rotation desired is not unique.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
@ -491,18 +492,18 @@ where
|
|||||||
|
|
||||||
// The cosinus may be out of [-1, 1] because of inaccuracies.
|
// The cosinus may be out of [-1, 1] because of inaccuracies.
|
||||||
if cos <= -N::one() {
|
if cos <= -N::one() {
|
||||||
return None;
|
None
|
||||||
} else if cos >= N::one() {
|
} else if cos >= N::one() {
|
||||||
return Some(Self::identity());
|
Some(Self::identity())
|
||||||
} else {
|
} else {
|
||||||
return Some(Self::from_axis_angle(&axis, cos.acos() * s));
|
Some(Self::from_axis_angle(&axis, cos.acos() * s))
|
||||||
}
|
}
|
||||||
} else if na.dot(&nb) < N::zero() {
|
} else if na.dot(&nb) < N::zero() {
|
||||||
// PI
|
// PI
|
||||||
//
|
//
|
||||||
// The rotation axis is undefined but the angle not zero. This is not a
|
// The rotation axis is undefined but the angle not zero. This is not a
|
||||||
// simple rotation.
|
// simple rotation.
|
||||||
return None;
|
None
|
||||||
} else {
|
} else {
|
||||||
// Zero
|
// Zero
|
||||||
Some(Self::identity())
|
Some(Self::identity())
|
||||||
|
@ -265,6 +265,15 @@ impl<N: Scalar + SimdValue> From<Vector4<N>> for Quaternion<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N: Scalar + SimdValue> From<[N; 4]> for Quaternion<N> {
|
||||||
|
#[inline]
|
||||||
|
fn from(coords: [N; 4]) -> Self {
|
||||||
|
Self {
|
||||||
|
coords: coords.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<N: Scalar + PrimitiveSimdValue> From<[Quaternion<N::Element>; 2]> for Quaternion<N>
|
impl<N: Scalar + PrimitiveSimdValue> From<[Quaternion<N::Element>; 2]> for Quaternion<N>
|
||||||
where
|
where
|
||||||
N: From<[<N as SimdValue>::Element; 2]>,
|
N: From<[<N as SimdValue>::Element; 2]>,
|
||||||
|
@ -124,8 +124,7 @@ quaternion_op_impl!(
|
|||||||
Add, add;
|
Add, add;
|
||||||
(U4, U1), (U4, U1);
|
(U4, U1), (U4, U1);
|
||||||
self: Quaternion<N>, rhs: Quaternion<N>, Output = Quaternion<N>;
|
self: Quaternion<N>, rhs: Quaternion<N>, Output = Quaternion<N>;
|
||||||
Quaternion::from(self.coords + rhs.coords);
|
Quaternion::from(self.coords + rhs.coords); );
|
||||||
);
|
|
||||||
|
|
||||||
// Quaternion - Quaternion
|
// Quaternion - Quaternion
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -153,8 +152,7 @@ quaternion_op_impl!(
|
|||||||
Sub, sub;
|
Sub, sub;
|
||||||
(U4, U1), (U4, U1);
|
(U4, U1), (U4, U1);
|
||||||
self: Quaternion<N>, rhs: Quaternion<N>, Output = Quaternion<N>;
|
self: Quaternion<N>, rhs: Quaternion<N>, Output = Quaternion<N>;
|
||||||
Quaternion::from(self.coords - rhs.coords);
|
Quaternion::from(self.coords - rhs.coords); );
|
||||||
);
|
|
||||||
|
|
||||||
// Quaternion × Quaternion
|
// Quaternion × Quaternion
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -186,8 +184,7 @@ quaternion_op_impl!(
|
|||||||
Mul, mul;
|
Mul, mul;
|
||||||
(U4, U1), (U4, U1);
|
(U4, U1), (U4, U1);
|
||||||
self: Quaternion<N>, rhs: Quaternion<N>, Output = Quaternion<N>;
|
self: Quaternion<N>, rhs: Quaternion<N>, Output = Quaternion<N>;
|
||||||
&self * &rhs;
|
&self * &rhs; );
|
||||||
);
|
|
||||||
|
|
||||||
// UnitQuaternion × UnitQuaternion
|
// UnitQuaternion × UnitQuaternion
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -215,15 +212,14 @@ quaternion_op_impl!(
|
|||||||
Mul, mul;
|
Mul, mul;
|
||||||
(U4, U1), (U4, U1);
|
(U4, U1), (U4, U1);
|
||||||
self: UnitQuaternion<N>, rhs: UnitQuaternion<N>, Output = UnitQuaternion<N>;
|
self: UnitQuaternion<N>, rhs: UnitQuaternion<N>, Output = UnitQuaternion<N>;
|
||||||
&self * &rhs;
|
&self * &rhs; );
|
||||||
);
|
|
||||||
|
|
||||||
// UnitQuaternion ÷ UnitQuaternion
|
// UnitQuaternion ÷ UnitQuaternion
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
Div, div;
|
Div, div;
|
||||||
(U4, U1), (U4, U1);
|
(U4, U1), (U4, U1);
|
||||||
self: &'a UnitQuaternion<N>, rhs: &'b UnitQuaternion<N>, Output = UnitQuaternion<N>;
|
self: &'a UnitQuaternion<N>, rhs: &'b UnitQuaternion<N>, Output = UnitQuaternion<N>;
|
||||||
self * rhs.inverse();
|
#[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
'a, 'b);
|
'a, 'b);
|
||||||
|
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -244,8 +240,7 @@ quaternion_op_impl!(
|
|||||||
Div, div;
|
Div, div;
|
||||||
(U4, U1), (U4, U1);
|
(U4, U1), (U4, U1);
|
||||||
self: UnitQuaternion<N>, rhs: UnitQuaternion<N>, Output = UnitQuaternion<N>;
|
self: UnitQuaternion<N>, rhs: UnitQuaternion<N>, Output = UnitQuaternion<N>;
|
||||||
&self / &rhs;
|
&self / &rhs; );
|
||||||
);
|
|
||||||
|
|
||||||
// UnitQuaternion × Rotation
|
// UnitQuaternion × Rotation
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -278,8 +273,7 @@ Mul, mul;
|
|||||||
(U4, U1), (U3, U3);
|
(U4, U1), (U3, U3);
|
||||||
self: UnitQuaternion<N>, rhs: Rotation<N, U3>,
|
self: UnitQuaternion<N>, rhs: Rotation<N, U3>,
|
||||||
Output = UnitQuaternion<N> => U3, U3;
|
Output = UnitQuaternion<N> => U3, U3;
|
||||||
self * UnitQuaternion::<N>::from_rotation_matrix(&rhs);
|
self * UnitQuaternion::<N>::from_rotation_matrix(&rhs); );
|
||||||
);
|
|
||||||
|
|
||||||
// UnitQuaternion ÷ Rotation
|
// UnitQuaternion ÷ Rotation
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -312,8 +306,7 @@ Div, div;
|
|||||||
(U4, U1), (U3, U3);
|
(U4, U1), (U3, U3);
|
||||||
self: UnitQuaternion<N>, rhs: Rotation<N, U3>,
|
self: UnitQuaternion<N>, rhs: Rotation<N, U3>,
|
||||||
Output = UnitQuaternion<N> => U3, U3;
|
Output = UnitQuaternion<N> => U3, U3;
|
||||||
self / UnitQuaternion::<N>::from_rotation_matrix(&rhs);
|
self / UnitQuaternion::<N>::from_rotation_matrix(&rhs); );
|
||||||
);
|
|
||||||
|
|
||||||
// Rotation × UnitQuaternion
|
// Rotation × UnitQuaternion
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -346,8 +339,7 @@ Mul, mul;
|
|||||||
(U3, U3), (U4, U1);
|
(U3, U3), (U4, U1);
|
||||||
self: Rotation<N, U3>, rhs: UnitQuaternion<N>,
|
self: Rotation<N, U3>, rhs: UnitQuaternion<N>,
|
||||||
Output = UnitQuaternion<N> => U3, U3;
|
Output = UnitQuaternion<N> => U3, U3;
|
||||||
UnitQuaternion::<N>::from_rotation_matrix(&self) * rhs;
|
UnitQuaternion::<N>::from_rotation_matrix(&self) * rhs; );
|
||||||
);
|
|
||||||
|
|
||||||
// Rotation ÷ UnitQuaternion
|
// Rotation ÷ UnitQuaternion
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -380,8 +372,7 @@ Div, div;
|
|||||||
(U3, U3), (U4, U1);
|
(U3, U3), (U4, U1);
|
||||||
self: Rotation<N, U3>, rhs: UnitQuaternion<N>,
|
self: Rotation<N, U3>, rhs: UnitQuaternion<N>,
|
||||||
Output = UnitQuaternion<N> => U3, U3;
|
Output = UnitQuaternion<N> => U3, U3;
|
||||||
UnitQuaternion::<N>::from_rotation_matrix(&self) / rhs;
|
UnitQuaternion::<N>::from_rotation_matrix(&self) / rhs; );
|
||||||
);
|
|
||||||
|
|
||||||
// UnitQuaternion × Vector
|
// UnitQuaternion × Vector
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -419,8 +410,7 @@ Mul, mul;
|
|||||||
(U4, U1), (U3, U1) for SB: Storage<N, U3> ;
|
(U4, U1), (U3, U1) for SB: Storage<N, U3> ;
|
||||||
self: UnitQuaternion<N>, rhs: Vector<N, U3, SB>,
|
self: UnitQuaternion<N>, rhs: Vector<N, U3, SB>,
|
||||||
Output = Vector3<N> => U3, U4;
|
Output = Vector3<N> => U3, U4;
|
||||||
&self * &rhs;
|
&self * &rhs; );
|
||||||
);
|
|
||||||
|
|
||||||
// UnitQuaternion × Point
|
// UnitQuaternion × Point
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -452,8 +442,7 @@ Mul, mul;
|
|||||||
(U4, U1), (U3, U1);
|
(U4, U1), (U3, U1);
|
||||||
self: UnitQuaternion<N>, rhs: Point3<N>,
|
self: UnitQuaternion<N>, rhs: Point3<N>,
|
||||||
Output = Point3<N> => U3, U4;
|
Output = Point3<N> => U3, U4;
|
||||||
Point3::from(self * rhs.coords);
|
Point3::from(self * rhs.coords); );
|
||||||
);
|
|
||||||
|
|
||||||
// UnitQuaternion × Unit<Vector>
|
// UnitQuaternion × Unit<Vector>
|
||||||
quaternion_op_impl!(
|
quaternion_op_impl!(
|
||||||
@ -485,8 +474,7 @@ Mul, mul;
|
|||||||
(U4, U1), (U3, U1) for SB: Storage<N, U3> ;
|
(U4, U1), (U3, U1) for SB: Storage<N, U3> ;
|
||||||
self: UnitQuaternion<N>, rhs: Unit<Vector<N, U3, SB>>,
|
self: UnitQuaternion<N>, rhs: Unit<Vector<N, U3, SB>>,
|
||||||
Output = Unit<Vector3<N>> => U3, U4;
|
Output = Unit<Vector3<N>> => U3, U4;
|
||||||
Unit::new_unchecked(self * rhs.into_inner());
|
Unit::new_unchecked(self * rhs.into_inner()); );
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! scalar_op_impl(
|
macro_rules! scalar_op_impl(
|
||||||
($($Op: ident, $op: ident, $OpAssign: ident, $op_assign: ident);* $(;)*) => {$(
|
($($Op: ident, $op: ident, $OpAssign: ident, $op_assign: ident);* $(;)*) => {$(
|
||||||
|
@ -19,7 +19,7 @@ use simba::simd::SimdRealField;
|
|||||||
|
|
||||||
use crate::base::allocator::Allocator;
|
use crate::base::allocator::Allocator;
|
||||||
use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1};
|
use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1};
|
||||||
use crate::base::{DefaultAllocator, MatrixN, Scalar, VectorN};
|
use crate::base::{DefaultAllocator, MatrixN, Scalar, Unit, VectorN};
|
||||||
use crate::geometry::Point;
|
use crate::geometry::Point;
|
||||||
|
|
||||||
/// A rotation matrix.
|
/// A rotation matrix.
|
||||||
@ -255,7 +255,7 @@ where
|
|||||||
"Unable to create a rotation from a non-square matrix."
|
"Unable to create a rotation from a non-square matrix."
|
||||||
);
|
);
|
||||||
|
|
||||||
Self { matrix: matrix }
|
Self { matrix }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transposes `self`.
|
/// Transposes `self`.
|
||||||
@ -441,6 +441,25 @@ where
|
|||||||
pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> {
|
pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> {
|
||||||
self.matrix().tr_mul(v)
|
self.matrix().tr_mul(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rotate the given vector by the inverse of this rotation. This may be
|
||||||
|
/// cheaper than inverting the rotation and then transforming the given
|
||||||
|
/// vector.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate approx;
|
||||||
|
/// # use std::f32;
|
||||||
|
/// # use nalgebra::{Rotation2, Rotation3, UnitQuaternion, Vector3};
|
||||||
|
/// let rot = Rotation3::new(Vector3::z() * f32::consts::FRAC_PI_2);
|
||||||
|
/// let transformed_vector = rot.inverse_transform_unit_vector(&Vector3::x_axis());
|
||||||
|
///
|
||||||
|
/// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>> {
|
||||||
|
Unit::new_unchecked(self.inverse_transform_vector(&**v))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: Scalar + Eq, D: DimName> Eq for Rotation<N, D> where DefaultAllocator: Allocator<N, D, D> {}
|
impl<N: Scalar + Eq, D: DimName> Eq for Rotation<N, D> where DefaultAllocator: Allocator<N, D, D> {}
|
||||||
|
@ -59,10 +59,10 @@ md_impl_all!(
|
|||||||
Div, div;
|
Div, div;
|
||||||
(D, D), (D, D) for D: DimName;
|
(D, D), (D, D) for D: DimName;
|
||||||
self: Rotation<N, D>, right: Rotation<N, D>, Output = Rotation<N, D>;
|
self: Rotation<N, D>, right: Rotation<N, D>, Output = Rotation<N, D>;
|
||||||
[val val] => self * right.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref val] => self * right.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[val ref] => self * right.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref ref] => self * right.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rotation × Matrix
|
// Rotation × Matrix
|
||||||
@ -98,10 +98,10 @@ md_impl_all!(
|
|||||||
where DefaultAllocator: Allocator<N, R1, D2>
|
where DefaultAllocator: Allocator<N, R1, D2>
|
||||||
where ShapeConstraint: AreMultipliable<R1, C1, D2, D2>;
|
where ShapeConstraint: AreMultipliable<R1, C1, D2, D2>;
|
||||||
self: Matrix<N, R1, C1, SA>, right: Rotation<N, D2>, Output = MatrixMN<N, R1, D2>;
|
self: Matrix<N, R1, C1, SA>, right: Rotation<N, D2>, Output = MatrixMN<N, R1, D2>;
|
||||||
[val val] => self * right.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref val] => self * right.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[val ref] => self * right.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref ref] => self * right.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rotation × Point
|
// Rotation × Point
|
||||||
|
@ -236,6 +236,31 @@ impl<N: SimdRealField> Rotation2<N> {
|
|||||||
pub fn scaled_axis(&self) -> VectorN<N, U1> {
|
pub fn scaled_axis(&self) -> VectorN<N, U1> {
|
||||||
Vector1::new(self.angle())
|
Vector1::new(self.angle())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spherical linear interpolation between two rotation matrices.
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate approx;
|
||||||
|
/// # use nalgebra::geometry::Rotation2;
|
||||||
|
///
|
||||||
|
/// let rot1 = Rotation2::new(std::f32::consts::FRAC_PI_4);
|
||||||
|
/// let rot2 = Rotation2::new(-std::f32::consts::PI);
|
||||||
|
///
|
||||||
|
/// let rot = rot1.slerp(&rot2, 1.0 / 3.0);
|
||||||
|
///
|
||||||
|
/// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn slerp(&self, other: &Self, t: N) -> Self
|
||||||
|
where
|
||||||
|
N::Element: SimdRealField,
|
||||||
|
{
|
||||||
|
let c1 = UnitComplex::from(*self);
|
||||||
|
let c2 = UnitComplex::from(*other);
|
||||||
|
c1.slerp(&c2, t).into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: SimdRealField> Distribution<Rotation2<N>> for Standard
|
impl<N: SimdRealField> Distribution<Rotation2<N>> for Standard
|
||||||
@ -862,6 +887,53 @@ where
|
|||||||
Self::identity()
|
Self::identity()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spherical linear interpolation between two rotation matrices.
|
||||||
|
///
|
||||||
|
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
|
||||||
|
/// is not well-defined). Use `.try_slerp` instead to avoid the panic.
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use nalgebra::geometry::Rotation3;
|
||||||
|
///
|
||||||
|
/// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
|
||||||
|
/// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
|
||||||
|
///
|
||||||
|
/// let q = q1.slerp(&q2, 1.0 / 3.0);
|
||||||
|
///
|
||||||
|
/// assert_eq!(q.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn slerp(&self, other: &Self, t: N) -> Self
|
||||||
|
where
|
||||||
|
N: RealField,
|
||||||
|
{
|
||||||
|
let q1 = UnitQuaternion::from(*self);
|
||||||
|
let q2 = UnitQuaternion::from(*other);
|
||||||
|
q1.slerp(&q2, t).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the spherical linear interpolation between two rotation matrices or returns `None`
|
||||||
|
/// if both rotations are approximately 180 degrees apart (in which case the interpolation is
|
||||||
|
/// not well-defined).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `self`: the first rotation to interpolate from.
|
||||||
|
/// * `other`: the second rotation to interpolate toward.
|
||||||
|
/// * `t`: the interpolation parameter. Should be between 0 and 1.
|
||||||
|
/// * `epsilon`: the value below which the sinus of the angle separating both rotations
|
||||||
|
/// must be to return `None`.
|
||||||
|
#[inline]
|
||||||
|
pub fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self>
|
||||||
|
where
|
||||||
|
N: RealField,
|
||||||
|
{
|
||||||
|
let q1 = Rotation3::from(*self);
|
||||||
|
let q2 = Rotation3::from(*other);
|
||||||
|
q1.try_slerp(&q2, t, epsilon).map(|q| q.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: SimdRealField> Distribution<Rotation3<N>> for Standard
|
impl<N: SimdRealField> Distribution<Rotation3<N>> for Standard
|
||||||
|
@ -158,15 +158,14 @@ similarity_binop_impl_all!(
|
|||||||
similarity_binop_impl_all!(
|
similarity_binop_impl_all!(
|
||||||
Div, div;
|
Div, div;
|
||||||
self: Similarity<N, D, R>, rhs: Similarity<N, D, R>, Output = Similarity<N, D, R>;
|
self: Similarity<N, D, R>, rhs: Similarity<N, D, R>, Output = Similarity<N, D, R>;
|
||||||
[val val] => self * rhs.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref val] => self * rhs.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[val ref] => self * rhs.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref ref] => self * rhs.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Similarity ×= Translation
|
// Similarity ×= Translation
|
||||||
similarity_binop_assign_impl_all!(
|
similarity_binop_assign_impl_all!(
|
||||||
|
|
||||||
MulAssign, mul_assign;
|
MulAssign, mul_assign;
|
||||||
self: Similarity<N, D, R>, rhs: Translation<N, D>;
|
self: Similarity<N, D, R>, rhs: Translation<N, D>;
|
||||||
[val] => *self *= &rhs;
|
[val] => *self *= &rhs;
|
||||||
@ -281,6 +280,7 @@ similarity_binop_impl_all!(
|
|||||||
[ref ref] => {
|
[ref ref] => {
|
||||||
let shift = self.isometry.rotation.transform_vector(&rhs.translation.vector) * self.scaling();
|
let shift = self.isometry.rotation.transform_vector(&rhs.translation.vector) * self.scaling();
|
||||||
Similarity::from_parts(
|
Similarity::from_parts(
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
Translation::from(&self.isometry.translation.vector + shift),
|
Translation::from(&self.isometry.translation.vector + shift),
|
||||||
self.isometry.rotation.clone() * rhs.rotation.clone(),
|
self.isometry.rotation.clone() * rhs.rotation.clone(),
|
||||||
self.scaling())
|
self.scaling())
|
||||||
@ -290,10 +290,10 @@ similarity_binop_impl_all!(
|
|||||||
similarity_binop_impl_all!(
|
similarity_binop_impl_all!(
|
||||||
Div, div;
|
Div, div;
|
||||||
self: Similarity<N, D, R>, rhs: Isometry<N, D, R>, Output = Similarity<N, D, R>;
|
self: Similarity<N, D, R>, rhs: Isometry<N, D, R>, Output = Similarity<N, D, R>;
|
||||||
[val val] => self * rhs.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref val] => self * rhs.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[val ref] => self * rhs.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref ref] => self * rhs.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Isometry × Similarity
|
// Isometry × Similarity
|
||||||
@ -322,10 +322,10 @@ similarity_binop_impl_all!(
|
|||||||
similarity_binop_impl_all!(
|
similarity_binop_impl_all!(
|
||||||
Div, div;
|
Div, div;
|
||||||
self: Isometry<N, D, R>, rhs: Similarity<N, D, R>, Output = Similarity<N, D, R>;
|
self: Isometry<N, D, R>, rhs: Similarity<N, D, R>, Output = Similarity<N, D, R>;
|
||||||
[val val] => self * rhs.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref val] => self * rhs.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[val ref] => self * rhs.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref ref] => self * rhs.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Similarity × Point
|
// Similarity × Point
|
||||||
@ -364,6 +364,7 @@ similarity_binop_impl_all!(
|
|||||||
[ref ref] => {
|
[ref ref] => {
|
||||||
let shift = self.isometry.rotation.transform_vector(&right.vector) * self.scaling();
|
let shift = self.isometry.rotation.transform_vector(&right.vector) * self.scaling();
|
||||||
Similarity::from_parts(
|
Similarity::from_parts(
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
Translation::from(&self.isometry.translation.vector + shift),
|
Translation::from(&self.isometry.translation.vector + shift),
|
||||||
self.isometry.rotation.clone(),
|
self.isometry.rotation.clone(),
|
||||||
self.scaling())
|
self.scaling())
|
||||||
@ -495,10 +496,10 @@ similarity_from_composition_impl_all!(
|
|||||||
self: Rotation<N, D>, right: Similarity<N, D, Rotation<N, D>>,
|
self: Rotation<N, D>, right: Similarity<N, D, Rotation<N, D>>,
|
||||||
Output = Similarity<N, D, Rotation<N, D>>;
|
Output = Similarity<N, D, Rotation<N, D>>;
|
||||||
// FIXME: don't call inverse explicitly?
|
// FIXME: don't call inverse explicitly?
|
||||||
[val val] => self * right.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref val] => self * right.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[val ref] => self * right.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref ref] => self * right.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Similarity × UnitQuaternion
|
// Similarity × UnitQuaternion
|
||||||
@ -556,10 +557,10 @@ similarity_from_composition_impl_all!(
|
|||||||
self: UnitQuaternion<N>, right: Similarity<N, U3, UnitQuaternion<N>>,
|
self: UnitQuaternion<N>, right: Similarity<N, U3, UnitQuaternion<N>>,
|
||||||
Output = Similarity<N, U3, UnitQuaternion<N>>;
|
Output = Similarity<N, U3, UnitQuaternion<N>>;
|
||||||
// FIXME: don't call inverse explicitly?
|
// FIXME: don't call inverse explicitly?
|
||||||
[val val] => self * right.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref val] => self * right.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[val ref] => self * right.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
[ref ref] => self * right.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Similarity × UnitComplex
|
// Similarity × UnitComplex
|
||||||
|
@ -245,7 +245,7 @@ where
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_matrix_unchecked(matrix: MatrixN<N, DimNameSum<D, U1>>) -> Self {
|
pub fn from_matrix_unchecked(matrix: MatrixN<N, DimNameSum<D, U1>>) -> Self {
|
||||||
Transform {
|
Transform {
|
||||||
matrix: matrix,
|
matrix,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,7 @@ md_impl_all!(
|
|||||||
|
|
||||||
if C::has_normalizer() {
|
if C::has_normalizer() {
|
||||||
let normalizer = self.matrix().fixed_slice::<U1, D>(D::dim(), 0);
|
let normalizer = self.matrix().fixed_slice::<U1, D>(D::dim(), 0);
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
let n = normalizer.tr_dot(&rhs.coords) + unsafe { *self.matrix().get_unchecked((D::dim(), D::dim())) };
|
let n = normalizer.tr_dot(&rhs.coords) + unsafe { *self.matrix().get_unchecked((D::dim(), D::dim())) };
|
||||||
|
|
||||||
if !n.is_zero() {
|
if !n.is_zero() {
|
||||||
@ -293,10 +294,10 @@ md_impl_all!(
|
|||||||
Div, div where N: RealField;
|
Div, div where N: RealField;
|
||||||
(DimNameSum<D, U1>, DimNameSum<D, U1>), (DimNameSum<D, U1>, DimNameSum<D, U1>) for D: DimNameAdd<U1>, CA: TCategoryMul<CB>, CB: SubTCategoryOf<TProjective>;
|
(DimNameSum<D, U1>, DimNameSum<D, U1>), (DimNameSum<D, U1>, DimNameSum<D, U1>) for D: DimNameAdd<U1>, CA: TCategoryMul<CB>, CB: SubTCategoryOf<TProjective>;
|
||||||
self: Transform<N, D, CA>, rhs: Transform<N, D, CB>, Output = Transform<N, D, CA::Representative>;
|
self: Transform<N, D, CA>, rhs: Transform<N, D, CB>, Output = Transform<N, D, CA::Representative>;
|
||||||
[val val] => self * rhs.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref val] => self * rhs.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[val ref] => self * rhs.clone().inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.clone().inverse() };
|
||||||
[ref ref] => self * rhs.clone().inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.clone().inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Transform ÷ Rotation
|
// Transform ÷ Rotation
|
||||||
@ -304,10 +305,10 @@ md_impl_all!(
|
|||||||
Div, div where N: RealField;
|
Div, div where N: RealField;
|
||||||
(DimNameSum<D, U1>, DimNameSum<D, U1>), (D, D) for D: DimNameAdd<U1>, C: TCategoryMul<TAffine>;
|
(DimNameSum<D, U1>, DimNameSum<D, U1>), (D, D) for D: DimNameAdd<U1>, C: TCategoryMul<TAffine>;
|
||||||
self: Transform<N, D, C>, rhs: Rotation<N, D>, Output = Transform<N, D, C::Representative>;
|
self: Transform<N, D, C>, rhs: Rotation<N, D>, Output = Transform<N, D, C::Representative>;
|
||||||
[val val] => self * rhs.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref val] => self * rhs.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[val ref] => self * rhs.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref ref] => self * rhs.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rotation ÷ Transform
|
// Rotation ÷ Transform
|
||||||
@ -315,10 +316,10 @@ md_impl_all!(
|
|||||||
Div, div where N: RealField;
|
Div, div where N: RealField;
|
||||||
(D, D), (DimNameSum<D, U1>, DimNameSum<D, U1>) for D: DimNameAdd<U1>, C: TCategoryMul<TAffine>;
|
(D, D), (DimNameSum<D, U1>, DimNameSum<D, U1>) for D: DimNameAdd<U1>, C: TCategoryMul<TAffine>;
|
||||||
self: Rotation<N, D>, rhs: Transform<N, D, C>, Output = Transform<N, D, C::Representative>;
|
self: Rotation<N, D>, rhs: Transform<N, D, C>, Output = Transform<N, D, C::Representative>;
|
||||||
[val val] => self.inverse() * rhs;
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
[ref val] => self.inverse() * rhs;
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
[val ref] => self.inverse() * rhs;
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
[ref ref] => self.inverse() * rhs;
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Transform ÷ UnitQuaternion
|
// Transform ÷ UnitQuaternion
|
||||||
@ -326,10 +327,10 @@ md_impl_all!(
|
|||||||
Div, div where N: RealField;
|
Div, div where N: RealField;
|
||||||
(U4, U4), (U4, U1) for C: TCategoryMul<TAffine>;
|
(U4, U4), (U4, U1) for C: TCategoryMul<TAffine>;
|
||||||
self: Transform<N, U3, C>, rhs: UnitQuaternion<N>, Output = Transform<N, U3, C::Representative>;
|
self: Transform<N, U3, C>, rhs: UnitQuaternion<N>, Output = Transform<N, U3, C::Representative>;
|
||||||
[val val] => self * rhs.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref val] => self * rhs.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[val ref] => self * rhs.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref ref] => self * rhs.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// UnitQuaternion ÷ Transform
|
// UnitQuaternion ÷ Transform
|
||||||
@ -337,10 +338,10 @@ md_impl_all!(
|
|||||||
Div, div where N: RealField;
|
Div, div where N: RealField;
|
||||||
(U4, U1), (U4, U4) for C: TCategoryMul<TAffine>;
|
(U4, U1), (U4, U4) for C: TCategoryMul<TAffine>;
|
||||||
self: UnitQuaternion<N>, rhs: Transform<N, U3, C>, Output = Transform<N, U3, C::Representative>;
|
self: UnitQuaternion<N>, rhs: Transform<N, U3, C>, Output = Transform<N, U3, C::Representative>;
|
||||||
[val val] => self.inverse() * rhs;
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
[ref val] => self.inverse() * rhs;
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
[val ref] => self.inverse() * rhs;
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
[ref ref] => self.inverse() * rhs;
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
);
|
);
|
||||||
|
|
||||||
// // Transform ÷ Isometry
|
// // Transform ÷ Isometry
|
||||||
@ -402,10 +403,10 @@ md_impl_all!(
|
|||||||
Div, div where N: RealField;
|
Div, div where N: RealField;
|
||||||
(DimNameSum<D, U1>, DimNameSum<D, U1>), (D, U1) for D: DimNameAdd<U1>, C: TCategoryMul<TAffine>;
|
(DimNameSum<D, U1>, DimNameSum<D, U1>), (D, U1) for D: DimNameAdd<U1>, C: TCategoryMul<TAffine>;
|
||||||
self: Transform<N, D, C>, rhs: Translation<N, D>, Output = Transform<N, D, C::Representative>;
|
self: Transform<N, D, C>, rhs: Translation<N, D>, Output = Transform<N, D, C::Representative>;
|
||||||
[val val] => self * rhs.inverse();
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref val] => self * rhs.inverse();
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[val ref] => self * rhs.inverse();
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
[ref ref] => self * rhs.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Translation ÷ Transform
|
// Translation ÷ Transform
|
||||||
@ -414,10 +415,10 @@ md_impl_all!(
|
|||||||
(D, U1), (DimNameSum<D, U1>, DimNameSum<D, U1>)
|
(D, U1), (DimNameSum<D, U1>, DimNameSum<D, U1>)
|
||||||
for D: DimNameAdd<U1>, C: TCategoryMul<TAffine>;
|
for D: DimNameAdd<U1>, C: TCategoryMul<TAffine>;
|
||||||
self: Translation<N, D>, rhs: Transform<N, D, C>, Output = Transform<N, D, C::Representative>;
|
self: Translation<N, D>, rhs: Transform<N, D, C>, Output = Transform<N, D, C::Representative>;
|
||||||
[val val] => self.inverse() * rhs;
|
[val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
[ref val] => self.inverse() * rhs;
|
[ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
[val ref] => self.inverse() * rhs;
|
[val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
[ref ref] => self.inverse() * rhs;
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Transform ×= Transform
|
// Transform ×= Transform
|
||||||
|
@ -119,7 +119,7 @@ where
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[deprecated(note = "Use `::from` instead.")]
|
#[deprecated(note = "Use `::from` instead.")]
|
||||||
pub fn from_vector(vector: VectorN<N, D>) -> Translation<N, D> {
|
pub fn from_vector(vector: VectorN<N, D>) -> Translation<N, D> {
|
||||||
Translation { vector: vector }
|
Translation { vector }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inverts `self`.
|
/// Inverts `self`.
|
||||||
|
@ -13,44 +13,50 @@ use crate::geometry::{Point, Translation};
|
|||||||
add_sub_impl!(Mul, mul, ClosedAdd;
|
add_sub_impl!(Mul, mul, ClosedAdd;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: &'a Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
|
self: &'a Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
|
||||||
Translation::from(&self.vector + &right.vector); 'a, 'b);
|
#[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(&self.vector + &right.vector) };
|
||||||
|
'a, 'b);
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul, ClosedAdd;
|
add_sub_impl!(Mul, mul, ClosedAdd;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: &'a Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
|
self: &'a Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
|
||||||
Translation::from(&self.vector + right.vector); 'a);
|
#[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(&self.vector + right.vector) };
|
||||||
|
'a);
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul, ClosedAdd;
|
add_sub_impl!(Mul, mul, ClosedAdd;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
|
self: Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
|
||||||
Translation::from(self.vector + &right.vector); 'b);
|
#[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(self.vector + &right.vector) };
|
||||||
|
'b);
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul, ClosedAdd;
|
add_sub_impl!(Mul, mul, ClosedAdd;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
|
self: Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
|
||||||
Translation::from(self.vector + right.vector); );
|
#[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(self.vector + right.vector) }; );
|
||||||
|
|
||||||
// Translation ÷ Translation
|
// Translation ÷ Translation
|
||||||
// FIXME: instead of calling inverse explicitly, could we just add a `mul_tr` or `mul_inv` method?
|
// FIXME: instead of calling inverse explicitly, could we just add a `mul_tr` or `mul_inv` method?
|
||||||
add_sub_impl!(Div, div, ClosedSub;
|
add_sub_impl!(Div, div, ClosedSub;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: &'a Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
|
self: &'a Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
|
||||||
Translation::from(&self.vector - &right.vector); 'a, 'b);
|
#[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(&self.vector - &right.vector) };
|
||||||
|
'a, 'b);
|
||||||
|
|
||||||
add_sub_impl!(Div, div, ClosedSub;
|
add_sub_impl!(Div, div, ClosedSub;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: &'a Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
|
self: &'a Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
|
||||||
Translation::from(&self.vector - right.vector); 'a);
|
#[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(&self.vector - right.vector) };
|
||||||
|
'a);
|
||||||
|
|
||||||
add_sub_impl!(Div, div, ClosedSub;
|
add_sub_impl!(Div, div, ClosedSub;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
|
self: Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
|
||||||
Translation::from(self.vector - &right.vector); 'b);
|
#[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(self.vector - &right.vector) };
|
||||||
|
'b);
|
||||||
|
|
||||||
add_sub_impl!(Div, div, ClosedSub;
|
add_sub_impl!(Div, div, ClosedSub;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
|
self: Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
|
||||||
Translation::from(self.vector - right.vector); );
|
#[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(self.vector - right.vector) }; );
|
||||||
|
|
||||||
// Translation × Point
|
// Translation × Point
|
||||||
// FIXME: we don't handle properly non-zero origins here. Do we want this to be the intended
|
// FIXME: we don't handle properly non-zero origins here. Do we want this to be the intended
|
||||||
@ -58,107 +64,45 @@ add_sub_impl!(Div, div, ClosedSub;
|
|||||||
add_sub_impl!(Mul, mul, ClosedAdd;
|
add_sub_impl!(Mul, mul, ClosedAdd;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: &'a Translation<N, D>, right: &'b Point<N, D>, Output = Point<N, D>;
|
self: &'a Translation<N, D>, right: &'b Point<N, D>, Output = Point<N, D>;
|
||||||
right + &self.vector; 'a, 'b);
|
#[allow(clippy::suspicious_arithmetic_impl)] { right + &self.vector };
|
||||||
|
'a, 'b);
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul, ClosedAdd;
|
add_sub_impl!(Mul, mul, ClosedAdd;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: &'a Translation<N, D>, right: Point<N, D>, Output = Point<N, D>;
|
self: &'a Translation<N, D>, right: Point<N, D>, Output = Point<N, D>;
|
||||||
right + &self.vector; 'a);
|
#[allow(clippy::suspicious_arithmetic_impl)] { right + &self.vector };
|
||||||
|
'a);
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul, ClosedAdd;
|
add_sub_impl!(Mul, mul, ClosedAdd;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: Translation<N, D>, right: &'b Point<N, D>, Output = Point<N, D>;
|
self: Translation<N, D>, right: &'b Point<N, D>, Output = Point<N, D>;
|
||||||
right + self.vector; 'b);
|
#[allow(clippy::suspicious_arithmetic_impl)] { right + self.vector };
|
||||||
|
'b);
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul, ClosedAdd;
|
add_sub_impl!(Mul, mul, ClosedAdd;
|
||||||
(D, U1), (D, U1) -> (D) for D: DimName;
|
(D, U1), (D, U1) -> (D) for D: DimName;
|
||||||
self: Translation<N, D>, right: Point<N, D>, Output = Point<N, D>;
|
self: Translation<N, D>, right: Point<N, D>, Output = Point<N, D>;
|
||||||
right + self.vector; );
|
#[allow(clippy::suspicious_arithmetic_impl)] { right + self.vector }; );
|
||||||
|
|
||||||
// Translation *= Translation
|
// Translation *= Translation
|
||||||
add_sub_assign_impl!(MulAssign, mul_assign, ClosedAdd;
|
add_sub_assign_impl!(MulAssign, mul_assign, ClosedAdd;
|
||||||
(D, U1), (D, U1) for D: DimName;
|
(D, U1), (D, U1) for D: DimName;
|
||||||
self: Translation<N, D>, right: &'b Translation<N, D>;
|
self: Translation<N, D>, right: &'b Translation<N, D>;
|
||||||
self.vector += &right.vector; 'b);
|
#[allow(clippy::suspicious_arithmetic_impl)] { self.vector += &right.vector };
|
||||||
|
'b);
|
||||||
|
|
||||||
add_sub_assign_impl!(MulAssign, mul_assign, ClosedAdd;
|
add_sub_assign_impl!(MulAssign, mul_assign, ClosedAdd;
|
||||||
(D, U1), (D, U1) for D: DimName;
|
(D, U1), (D, U1) for D: DimName;
|
||||||
self: Translation<N, D>, right: Translation<N, D>;
|
self: Translation<N, D>, right: Translation<N, D>;
|
||||||
self.vector += right.vector; );
|
#[allow(clippy::suspicious_arithmetic_impl)] { self.vector += right.vector }; );
|
||||||
|
|
||||||
add_sub_assign_impl!(DivAssign, div_assign, ClosedSub;
|
add_sub_assign_impl!(DivAssign, div_assign, ClosedSub;
|
||||||
(D, U1), (D, U1) for D: DimName;
|
(D, U1), (D, U1) for D: DimName;
|
||||||
self: Translation<N, D>, right: &'b Translation<N, D>;
|
self: Translation<N, D>, right: &'b Translation<N, D>;
|
||||||
self.vector -= &right.vector; 'b);
|
#[allow(clippy::suspicious_arithmetic_impl)] { self.vector -= &right.vector };
|
||||||
|
'b);
|
||||||
|
|
||||||
add_sub_assign_impl!(DivAssign, div_assign, ClosedSub;
|
add_sub_assign_impl!(DivAssign, div_assign, ClosedSub;
|
||||||
(D, U1), (D, U1) for D: DimName;
|
(D, U1), (D, U1) for D: DimName;
|
||||||
self: Translation<N, D>, right: Translation<N, D>;
|
self: Translation<N, D>, right: Translation<N, D>;
|
||||||
self.vector -= right.vector; );
|
#[allow(clippy::suspicious_arithmetic_impl)] { self.vector -= right.vector }; );
|
||||||
|
|
||||||
/*
|
|
||||||
// Translation × Matrix
|
|
||||||
add_sub_impl!(Mul, mul;
|
|
||||||
(D1, D1), (R2, C2) for D1, R2, C2;
|
|
||||||
self: &'a Translation<N, D1>, right: &'b Matrix<N, R2, C2>, Output = MatrixMN<N, D1, C2>;
|
|
||||||
self.vector() * right; 'a, 'b);
|
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul;
|
|
||||||
(D1, D1), (R2, C2) for D1, R2, C2;
|
|
||||||
self: &'a Translation<N, D1>, right: Matrix<N, R2, C2>, Output = MatrixMN<N, D1, C2>;
|
|
||||||
self.vector() * right; 'a);
|
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul;
|
|
||||||
(D1, D1), (R2, C2) for D1, R2, C2;
|
|
||||||
self: Translation<N, D1>, right: &'b Matrix<N, R2, C2>, Output = MatrixMN<N, D1, C2>;
|
|
||||||
self.unwrap() * right; 'b);
|
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul;
|
|
||||||
(D1, D1), (R2, C2) for D1, R2, C2;
|
|
||||||
self: Translation<N, D1>, right: Matrix<N, R2, C2>, Output = MatrixMN<N, D1, C2>;
|
|
||||||
self.unwrap() * right; );
|
|
||||||
|
|
||||||
// Matrix × Translation
|
|
||||||
add_sub_impl!(Mul, mul;
|
|
||||||
(R1, C1), (D2, D2) for R1, C1, D2;
|
|
||||||
self: &'a Matrix<N, R1, C1>, right: &'b Translation<N, D2>, Output = MatrixMN<N, R1, D2>;
|
|
||||||
self * right.vector(); 'a, 'b);
|
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul;
|
|
||||||
(R1, C1), (D2, D2) for R1, C1, D2;
|
|
||||||
self: &'a Matrix<N, R1, C1>, right: Translation<N, D2>, Output = MatrixMN<N, R1, D2>;
|
|
||||||
self * right.unwrap(); 'a);
|
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul;
|
|
||||||
(R1, C1), (D2, D2) for R1, C1, D2;
|
|
||||||
self: Matrix<N, R1, C1>, right: &'b Translation<N, D2>, Output = MatrixMN<N, R1, D2>;
|
|
||||||
self * right.vector(); 'b);
|
|
||||||
|
|
||||||
|
|
||||||
add_sub_impl!(Mul, mul;
|
|
||||||
(R1, C1), (D2, D2) for R1, C1, D2;
|
|
||||||
self: Matrix<N, R1, C1>, right: Translation<N, D2>, Output = MatrixMN<N, R1, D2>;
|
|
||||||
self * right.unwrap(); );
|
|
||||||
|
|
||||||
// Matrix *= Translation
|
|
||||||
md_assign_impl!(MulAssign, mul_assign;
|
|
||||||
(R1, C1), (C1, C1) for R1, C1;
|
|
||||||
self: Matrix<N, R1, C1>, right: &'b Translation<N, C1>;
|
|
||||||
self.mul_assign(right.vector()); 'b);
|
|
||||||
|
|
||||||
md_assign_impl!(MulAssign, mul_assign;
|
|
||||||
(R1, C1), (C1, C1) for R1, C1;
|
|
||||||
self: Matrix<N, R1, C1>, right: Translation<N, C1>;
|
|
||||||
self.mul_assign(right.unwrap()); );
|
|
||||||
|
|
||||||
|
|
||||||
md_assign_impl!(DivAssign, div_assign;
|
|
||||||
(R1, C1), (C1, C1) for R1, C1;
|
|
||||||
self: Matrix<N, R1, C1>, right: &'b Translation<N, C1>;
|
|
||||||
self.mul_assign(right.inverse().vector()); 'b);
|
|
||||||
|
|
||||||
md_assign_impl!(DivAssign, div_assign;
|
|
||||||
(R1, C1), (C1, C1) for R1, C1;
|
|
||||||
self: Matrix<N, R1, C1>, right: Translation<N, C1>;
|
|
||||||
self.mul_assign(right.inverse().unwrap()); );
|
|
||||||
*/
|
|
||||||
|
@ -360,6 +360,43 @@ where
|
|||||||
pub fn inverse_transform_vector(&self, v: &Vector2<N>) -> Vector2<N> {
|
pub fn inverse_transform_vector(&self, v: &Vector2<N>) -> Vector2<N> {
|
||||||
self.inverse() * v
|
self.inverse() * v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rotate the given vector by the inverse of this unit complex number.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate approx;
|
||||||
|
/// # use nalgebra::{UnitComplex, Vector2};
|
||||||
|
/// # use std::f32;
|
||||||
|
/// let rot = UnitComplex::new(f32::consts::FRAC_PI_2);
|
||||||
|
/// let transformed_vector = rot.inverse_transform_unit_vector(&Vector2::x_axis());
|
||||||
|
/// assert_relative_eq!(transformed_vector, -Vector2::y_axis(), epsilon = 1.0e-6);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector2<N>>) -> Unit<Vector2<N>> {
|
||||||
|
self.inverse() * v
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spherical linear interpolation between two rotations represented as unit complex numbers.
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate approx;
|
||||||
|
/// # use nalgebra::geometry::UnitComplex;
|
||||||
|
///
|
||||||
|
/// let rot1 = UnitComplex::new(std::f32::consts::FRAC_PI_4);
|
||||||
|
/// let rot2 = UnitComplex::new(-std::f32::consts::PI);
|
||||||
|
///
|
||||||
|
/// let rot = rot1.slerp(&rot2, 1.0 / 3.0);
|
||||||
|
///
|
||||||
|
/// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2);
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn slerp(&self, other: &Self, t: N) -> Self {
|
||||||
|
Self::new(self.angle() * (N::one() - t) + other.angle() * t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: RealField + fmt::Display> fmt::Display for UnitComplex<N> {
|
impl<N: RealField + fmt::Display> fmt::Display for UnitComplex<N> {
|
||||||
|
@ -96,6 +96,7 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn div(self, rhs: Self) -> Self::Output {
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner())
|
Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,6 +109,7 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn div(self, rhs: UnitComplex<N>) -> Self::Output {
|
fn div(self, rhs: UnitComplex<N>) -> Self::Output {
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner())
|
Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,6 +122,7 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn div(self, rhs: &'b UnitComplex<N>) -> Self::Output {
|
fn div(self, rhs: &'b UnitComplex<N>) -> Self::Output {
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner())
|
Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,6 +135,7 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn div(self, rhs: &'b UnitComplex<N>) -> Self::Output {
|
fn div(self, rhs: &'b UnitComplex<N>) -> Self::Output {
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner())
|
Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,7 +210,7 @@ complex_op_impl_all!(
|
|||||||
[val val] => &self / &rhs;
|
[val val] => &self / &rhs;
|
||||||
[ref val] => self / &rhs;
|
[ref val] => self / &rhs;
|
||||||
[val ref] => &self / rhs;
|
[val ref] => &self / rhs;
|
||||||
[ref ref] => self * UnitComplex::from_rotation_matrix(rhs).inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitComplex::from_rotation_matrix(rhs).inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rotation × UnitComplex
|
// Rotation × UnitComplex
|
||||||
@ -228,7 +232,7 @@ complex_op_impl_all!(
|
|||||||
[val val] => &self / &rhs;
|
[val val] => &self / &rhs;
|
||||||
[ref val] => self / &rhs;
|
[ref val] => self / &rhs;
|
||||||
[val ref] => &self / rhs;
|
[val ref] => &self / rhs;
|
||||||
[ref ref] => UnitComplex::from_rotation_matrix(self) * rhs.inverse();
|
[ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { UnitComplex::from_rotation_matrix(self) * rhs.inverse() };
|
||||||
);
|
);
|
||||||
|
|
||||||
// UnitComplex × Point
|
// UnitComplex × Point
|
||||||
|
@ -15,7 +15,7 @@ Simply add the following to your `Cargo.toml` file:
|
|||||||
|
|
||||||
```.ignore
|
```.ignore
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nalgebra = "0.18"
|
nalgebra = "0.21"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -90,11 +90,9 @@ an optimized set of tools for computer graphics and physics. Those features incl
|
|||||||
#[cfg(feature = "arbitrary")]
|
#[cfg(feature = "arbitrary")]
|
||||||
extern crate quickcheck;
|
extern crate quickcheck;
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde-serialize")]
|
||||||
extern crate serde;
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde;
|
||||||
|
|
||||||
#[cfg(feature = "abomonation-serialize")]
|
#[cfg(feature = "abomonation-serialize")]
|
||||||
extern crate abomonation;
|
extern crate abomonation;
|
||||||
|
@ -8,7 +8,7 @@ use simba::simd::SimdComplexField;
|
|||||||
use crate::allocator::Allocator;
|
use crate::allocator::Allocator;
|
||||||
use crate::base::{DefaultAllocator, Matrix, MatrixMN, MatrixN, SquareMatrix, Vector};
|
use crate::base::{DefaultAllocator, Matrix, MatrixMN, MatrixN, SquareMatrix, Vector};
|
||||||
use crate::constraint::{SameNumberOfRows, ShapeConstraint};
|
use crate::constraint::{SameNumberOfRows, ShapeConstraint};
|
||||||
use crate::dimension::{Dim, DimAdd, DimDiff, DimSub, DimSum, Dynamic, U1};
|
use crate::dimension::{Dim, DimAdd, DimDiff, DimSub, DimSum, U1};
|
||||||
use crate::storage::{Storage, StorageMut};
|
use crate::storage::{Storage, StorageMut};
|
||||||
|
|
||||||
/// The Cholesky decomposition of a symmetric-definite-positive matrix.
|
/// The Cholesky decomposition of a symmetric-definite-positive matrix.
|
||||||
@ -364,7 +364,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: ComplexField, D: DimSub<Dynamic>, S: Storage<N, D, D>> SquareMatrix<N, D, S>
|
impl<N: ComplexField, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S>
|
||||||
where
|
where
|
||||||
DefaultAllocator: Allocator<N, D, D>,
|
DefaultAllocator: Allocator<N, D, D>,
|
||||||
{
|
{
|
||||||
|
@ -10,10 +10,12 @@ use crate::{
|
|||||||
convert, try_convert, ComplexField, MatrixN, RealField,
|
convert, try_convert, ComplexField, MatrixN, RealField,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::num::Zero;
|
||||||
|
|
||||||
// https://github.com/scipy/scipy/blob/c1372d8aa90a73d8a52f135529293ff4edb98fc8/scipy/sparse/linalg/matfuncs.py
|
// https://github.com/scipy/scipy/blob/c1372d8aa90a73d8a52f135529293ff4edb98fc8/scipy/sparse/linalg/matfuncs.py
|
||||||
struct ExpmPadeHelper<N, D>
|
struct ExpmPadeHelper<N, D>
|
||||||
where
|
where
|
||||||
N: RealField,
|
N: ComplexField,
|
||||||
D: DimMin<D>,
|
D: DimMin<D>,
|
||||||
DefaultAllocator: Allocator<N, D, D> + Allocator<(usize, usize), DimMinimum<D, D>>,
|
DefaultAllocator: Allocator<N, D, D> + Allocator<(usize, usize), DimMinimum<D, D>>,
|
||||||
{
|
{
|
||||||
@ -27,20 +29,20 @@ where
|
|||||||
a8: Option<MatrixN<N, D>>,
|
a8: Option<MatrixN<N, D>>,
|
||||||
a10: Option<MatrixN<N, D>>,
|
a10: Option<MatrixN<N, D>>,
|
||||||
|
|
||||||
d4_exact: Option<N>,
|
d4_exact: Option<N::RealField>,
|
||||||
d6_exact: Option<N>,
|
d6_exact: Option<N::RealField>,
|
||||||
d8_exact: Option<N>,
|
d8_exact: Option<N::RealField>,
|
||||||
d10_exact: Option<N>,
|
d10_exact: Option<N::RealField>,
|
||||||
|
|
||||||
d4_approx: Option<N>,
|
d4_approx: Option<N::RealField>,
|
||||||
d6_approx: Option<N>,
|
d6_approx: Option<N::RealField>,
|
||||||
d8_approx: Option<N>,
|
d8_approx: Option<N::RealField>,
|
||||||
d10_approx: Option<N>,
|
d10_approx: Option<N::RealField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N, D> ExpmPadeHelper<N, D>
|
impl<N, D> ExpmPadeHelper<N, D>
|
||||||
where
|
where
|
||||||
N: RealField,
|
N: ComplexField,
|
||||||
D: DimMin<D>,
|
D: DimMin<D>,
|
||||||
DefaultAllocator: Allocator<N, D, D> + Allocator<(usize, usize), DimMinimum<D, D>>,
|
DefaultAllocator: Allocator<N, D, D> + Allocator<(usize, usize), DimMinimum<D, D>>,
|
||||||
{
|
{
|
||||||
@ -110,7 +112,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn d4_tight(&mut self) -> N {
|
fn d4_tight(&mut self) -> N::RealField {
|
||||||
if self.d4_exact.is_none() {
|
if self.d4_exact.is_none() {
|
||||||
self.calc_a4();
|
self.calc_a4();
|
||||||
self.d4_exact = Some(one_norm(self.a4.as_ref().unwrap()).powf(convert(0.25)));
|
self.d4_exact = Some(one_norm(self.a4.as_ref().unwrap()).powf(convert(0.25)));
|
||||||
@ -118,7 +120,7 @@ where
|
|||||||
self.d4_exact.unwrap()
|
self.d4_exact.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn d6_tight(&mut self) -> N {
|
fn d6_tight(&mut self) -> N::RealField {
|
||||||
if self.d6_exact.is_none() {
|
if self.d6_exact.is_none() {
|
||||||
self.calc_a6();
|
self.calc_a6();
|
||||||
self.d6_exact = Some(one_norm(self.a6.as_ref().unwrap()).powf(convert(1.0 / 6.0)));
|
self.d6_exact = Some(one_norm(self.a6.as_ref().unwrap()).powf(convert(1.0 / 6.0)));
|
||||||
@ -126,7 +128,7 @@ where
|
|||||||
self.d6_exact.unwrap()
|
self.d6_exact.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn d8_tight(&mut self) -> N {
|
fn d8_tight(&mut self) -> N::RealField {
|
||||||
if self.d8_exact.is_none() {
|
if self.d8_exact.is_none() {
|
||||||
self.calc_a8();
|
self.calc_a8();
|
||||||
self.d8_exact = Some(one_norm(self.a8.as_ref().unwrap()).powf(convert(1.0 / 8.0)));
|
self.d8_exact = Some(one_norm(self.a8.as_ref().unwrap()).powf(convert(1.0 / 8.0)));
|
||||||
@ -134,7 +136,7 @@ where
|
|||||||
self.d8_exact.unwrap()
|
self.d8_exact.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn d10_tight(&mut self) -> N {
|
fn d10_tight(&mut self) -> N::RealField {
|
||||||
if self.d10_exact.is_none() {
|
if self.d10_exact.is_none() {
|
||||||
self.calc_a10();
|
self.calc_a10();
|
||||||
self.d10_exact = Some(one_norm(self.a10.as_ref().unwrap()).powf(convert(1.0 / 10.0)));
|
self.d10_exact = Some(one_norm(self.a10.as_ref().unwrap()).powf(convert(1.0 / 10.0)));
|
||||||
@ -142,7 +144,7 @@ where
|
|||||||
self.d10_exact.unwrap()
|
self.d10_exact.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn d4_loose(&mut self) -> N {
|
fn d4_loose(&mut self) -> N::RealField {
|
||||||
if self.use_exact_norm {
|
if self.use_exact_norm {
|
||||||
return self.d4_tight();
|
return self.d4_tight();
|
||||||
}
|
}
|
||||||
@ -159,7 +161,7 @@ where
|
|||||||
self.d4_approx.unwrap()
|
self.d4_approx.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn d6_loose(&mut self) -> N {
|
fn d6_loose(&mut self) -> N::RealField {
|
||||||
if self.use_exact_norm {
|
if self.use_exact_norm {
|
||||||
return self.d6_tight();
|
return self.d6_tight();
|
||||||
}
|
}
|
||||||
@ -176,7 +178,7 @@ where
|
|||||||
self.d6_approx.unwrap()
|
self.d6_approx.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn d8_loose(&mut self) -> N {
|
fn d8_loose(&mut self) -> N::RealField {
|
||||||
if self.use_exact_norm {
|
if self.use_exact_norm {
|
||||||
return self.d8_tight();
|
return self.d8_tight();
|
||||||
}
|
}
|
||||||
@ -193,7 +195,7 @@ where
|
|||||||
self.d8_approx.unwrap()
|
self.d8_approx.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn d10_loose(&mut self) -> N {
|
fn d10_loose(&mut self) -> N::RealField {
|
||||||
if self.use_exact_norm {
|
if self.use_exact_norm {
|
||||||
return self.d10_tight();
|
return self.d10_tight();
|
||||||
}
|
}
|
||||||
@ -359,15 +361,20 @@ where
|
|||||||
|
|
||||||
fn ell<N, D>(a: &MatrixN<N, D>, m: u64) -> u64
|
fn ell<N, D>(a: &MatrixN<N, D>, m: u64) -> u64
|
||||||
where
|
where
|
||||||
N: RealField,
|
N: ComplexField,
|
||||||
D: Dim,
|
D: Dim,
|
||||||
DefaultAllocator: Allocator<N, D, D> + Allocator<N, D>,
|
DefaultAllocator: Allocator<N, D, D>
|
||||||
|
+ Allocator<N, D>
|
||||||
|
+ Allocator<N::RealField, D>
|
||||||
|
+ Allocator<N::RealField, D, D>,
|
||||||
{
|
{
|
||||||
// 2m choose m = (2m)!/(m! * (2m-m)!)
|
// 2m choose m = (2m)!/(m! * (2m-m)!)
|
||||||
|
|
||||||
let a_abs_onenorm = onenorm_matrix_power_nonm(&a.abs(), 2 * m + 1);
|
let a_abs = a.map(|x| x.abs());
|
||||||
|
|
||||||
if a_abs_onenorm == N::zero() {
|
let a_abs_onenorm = onenorm_matrix_power_nonm(&a_abs, 2 * m + 1);
|
||||||
|
|
||||||
|
if a_abs_onenorm == <N as ComplexField>::RealField::zero() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,27 +406,33 @@ where
|
|||||||
q.lu().solve(&p).unwrap()
|
q.lu().solve(&p).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn one_norm<N, D>(m: &MatrixN<N, D>) -> N
|
fn one_norm<N, D>(m: &MatrixN<N, D>) -> N::RealField
|
||||||
where
|
where
|
||||||
N: RealField,
|
N: ComplexField,
|
||||||
D: Dim,
|
D: Dim,
|
||||||
DefaultAllocator: Allocator<N, D, D>,
|
DefaultAllocator: Allocator<N, D, D>,
|
||||||
{
|
{
|
||||||
let mut max = N::zero();
|
let mut max = <N as ComplexField>::RealField::zero();
|
||||||
|
|
||||||
for i in 0..m.ncols() {
|
for i in 0..m.ncols() {
|
||||||
let col = m.column(i);
|
let col = m.column(i);
|
||||||
max = max.max(col.iter().fold(N::zero(), |a, b| a + b.abs()));
|
max = max.max(
|
||||||
|
col.iter()
|
||||||
|
.fold(<N as ComplexField>::RealField::zero(), |a, b| a + b.abs()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
max
|
max
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: RealField, D> MatrixN<N, D>
|
impl<N: ComplexField, D> MatrixN<N, D>
|
||||||
where
|
where
|
||||||
D: DimMin<D, Output = D>,
|
D: DimMin<D, Output = D>,
|
||||||
DefaultAllocator:
|
DefaultAllocator: Allocator<N, D, D>
|
||||||
Allocator<N, D, D> + Allocator<(usize, usize), DimMinimum<D, D>> + Allocator<N, D>,
|
+ Allocator<(usize, usize), DimMinimum<D, D>>
|
||||||
|
+ Allocator<N, D>
|
||||||
|
+ Allocator<N::RealField, D>
|
||||||
|
+ Allocator<N::RealField, D, D>,
|
||||||
{
|
{
|
||||||
/// Computes exponential of this matrix
|
/// Computes exponential of this matrix
|
||||||
pub fn exp(&self) -> Self {
|
pub fn exp(&self) -> Self {
|
||||||
@ -430,19 +443,19 @@ where
|
|||||||
|
|
||||||
let mut h = ExpmPadeHelper::new(self.clone(), true);
|
let mut h = ExpmPadeHelper::new(self.clone(), true);
|
||||||
|
|
||||||
let eta_1 = N::max(h.d4_loose(), h.d6_loose());
|
let eta_1 = N::RealField::max(h.d4_loose(), h.d6_loose());
|
||||||
if eta_1 < convert(1.495585217958292e-002) && ell(&h.a, 3) == 0 {
|
if eta_1 < convert(1.495585217958292e-002) && ell(&h.a, 3) == 0 {
|
||||||
let (u, v) = h.pade3();
|
let (u, v) = h.pade3();
|
||||||
return solve_p_q(u, v);
|
return solve_p_q(u, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
let eta_2 = N::max(h.d4_tight(), h.d6_loose());
|
let eta_2 = N::RealField::max(h.d4_tight(), h.d6_loose());
|
||||||
if eta_2 < convert(2.539398330063230e-001) && ell(&h.a, 5) == 0 {
|
if eta_2 < convert(2.539398330063230e-001) && ell(&h.a, 5) == 0 {
|
||||||
let (u, v) = h.pade5();
|
let (u, v) = h.pade5();
|
||||||
return solve_p_q(u, v);
|
return solve_p_q(u, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
let eta_3 = N::max(h.d6_tight(), h.d8_loose());
|
let eta_3 = N::RealField::max(h.d6_tight(), h.d8_loose());
|
||||||
if eta_3 < convert(9.504178996162932e-001) && ell(&h.a, 7) == 0 {
|
if eta_3 < convert(9.504178996162932e-001) && ell(&h.a, 7) == 0 {
|
||||||
let (u, v) = h.pade7();
|
let (u, v) = h.pade7();
|
||||||
return solve_p_q(u, v);
|
return solve_p_q(u, v);
|
||||||
@ -452,11 +465,11 @@ where
|
|||||||
return solve_p_q(u, v);
|
return solve_p_q(u, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
let eta_4 = N::max(h.d8_loose(), h.d10_loose());
|
let eta_4 = N::RealField::max(h.d8_loose(), h.d10_loose());
|
||||||
let eta_5 = N::min(eta_3, eta_4);
|
let eta_5 = N::RealField::min(eta_3, eta_4);
|
||||||
let theta_13 = convert(4.25);
|
let theta_13 = convert(4.25);
|
||||||
|
|
||||||
let mut s = if eta_5 == N::zero() {
|
let mut s = if eta_5 == N::RealField::zero() {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
let l2 = try_convert((eta_5 / theta_13).log2().ceil()).unwrap();
|
let l2 = try_convert((eta_5 / theta_13).log2().ceil()).unwrap();
|
||||||
|
@ -60,11 +60,7 @@ where
|
|||||||
let mut q = PermutationSequence::identity_generic(min_nrows_ncols);
|
let mut q = PermutationSequence::identity_generic(min_nrows_ncols);
|
||||||
|
|
||||||
if min_nrows_ncols.value() == 0 {
|
if min_nrows_ncols.value() == 0 {
|
||||||
return Self {
|
return Self { lu: matrix, p, q };
|
||||||
lu: matrix,
|
|
||||||
p: p,
|
|
||||||
q: q,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..min_nrows_ncols.value() {
|
for i in 0..min_nrows_ncols.value() {
|
||||||
@ -90,11 +86,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self { lu: matrix, p, q }
|
||||||
lu: matrix,
|
|
||||||
p: p,
|
|
||||||
q: q,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -96,7 +96,7 @@ where
|
|||||||
let mut p = PermutationSequence::identity_generic(min_nrows_ncols);
|
let mut p = PermutationSequence::identity_generic(min_nrows_ncols);
|
||||||
|
|
||||||
if min_nrows_ncols.value() == 0 {
|
if min_nrows_ncols.value() == 0 {
|
||||||
return LU { lu: matrix, p: p };
|
return LU { lu: matrix, p };
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..min_nrows_ncols.value() {
|
for i in 0..min_nrows_ncols.value() {
|
||||||
@ -117,7 +117,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LU { lu: matrix, p: p }
|
LU { lu: matrix, p }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -5,6 +5,10 @@ mod bidiagonal;
|
|||||||
mod cholesky;
|
mod cholesky;
|
||||||
mod convolution;
|
mod convolution;
|
||||||
mod determinant;
|
mod determinant;
|
||||||
|
// FIXME: this should not be needed. However, the exp uses
|
||||||
|
// explicit float operations on `f32` and `f64`. We need to
|
||||||
|
// get rid of these to allow exp to be used on a no-std context.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
mod exp;
|
mod exp;
|
||||||
mod full_piv_lu;
|
mod full_piv_lu;
|
||||||
pub mod givens;
|
pub mod givens;
|
||||||
@ -27,6 +31,7 @@ mod symmetric_tridiagonal;
|
|||||||
pub use self::bidiagonal::*;
|
pub use self::bidiagonal::*;
|
||||||
pub use self::cholesky::*;
|
pub use self::cholesky::*;
|
||||||
pub use self::convolution::*;
|
pub use self::convolution::*;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub use self::exp::*;
|
pub use self::exp::*;
|
||||||
pub use self::full_piv_lu::*;
|
pub use self::full_piv_lu::*;
|
||||||
pub use self::hessenberg::*;
|
pub use self::hessenberg::*;
|
||||||
|
@ -57,20 +57,14 @@ where
|
|||||||
let mut diag = unsafe { MatrixMN::new_uninitialized_generic(min_nrows_ncols, U1) };
|
let mut diag = unsafe { MatrixMN::new_uninitialized_generic(min_nrows_ncols, U1) };
|
||||||
|
|
||||||
if min_nrows_ncols.value() == 0 {
|
if min_nrows_ncols.value() == 0 {
|
||||||
return QR {
|
return QR { qr: matrix, diag };
|
||||||
qr: matrix,
|
|
||||||
diag: diag,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for ite in 0..min_nrows_ncols.value() {
|
for ite in 0..min_nrows_ncols.value() {
|
||||||
householder::clear_column_unchecked(&mut matrix, &mut diag[ite], ite, 0, None);
|
householder::clear_column_unchecked(&mut matrix, &mut diag[ite], ite, 0, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
QR {
|
QR { qr: matrix, diag }
|
||||||
qr: matrix,
|
|
||||||
diag: diag,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the upper trapezoidal submatrix `R` of this decomposition.
|
/// Retrieves the upper trapezoidal submatrix `R` of this decomposition.
|
||||||
|
@ -73,10 +73,8 @@ where
|
|||||||
pub fn try_new(m: MatrixN<N, D>, eps: N::RealField, max_niter: usize) -> Option<Self> {
|
pub fn try_new(m: MatrixN<N, D>, eps: N::RealField, max_niter: usize) -> Option<Self> {
|
||||||
let mut work = unsafe { VectorN::new_uninitialized_generic(m.data.shape().0, U1) };
|
let mut work = unsafe { VectorN::new_uninitialized_generic(m.data.shape().0, U1) };
|
||||||
|
|
||||||
Self::do_decompose(m, &mut work, eps, max_niter, true).map(|(q, t)| Schur {
|
Self::do_decompose(m, &mut work, eps, max_niter, true)
|
||||||
q: q.unwrap(),
|
.map(|(q, t)| Schur { q: q.unwrap(), t })
|
||||||
t: t,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_decompose(
|
fn do_decompose(
|
||||||
@ -138,9 +136,9 @@ where
|
|||||||
let m = end - 1;
|
let m = end - 1;
|
||||||
let n = end;
|
let n = end;
|
||||||
|
|
||||||
let h11 = t[(start + 0, start + 0)];
|
let h11 = t[(start, start)];
|
||||||
let h12 = t[(start + 0, start + 1)];
|
let h12 = t[(start, start + 1)];
|
||||||
let h21 = t[(start + 1, start + 0)];
|
let h21 = t[(start + 1, start)];
|
||||||
let h22 = t[(start + 1, start + 1)];
|
let h22 = t[(start + 1, start + 1)];
|
||||||
let h32 = t[(start + 2, start + 1)];
|
let h32 = t[(start + 2, start + 1)];
|
||||||
|
|
||||||
@ -163,7 +161,7 @@ where
|
|||||||
|
|
||||||
if not_zero {
|
if not_zero {
|
||||||
if k > start {
|
if k > start {
|
||||||
t[(k + 0, k - 1)] = norm;
|
t[(k, k - 1)] = norm;
|
||||||
t[(k + 1, k - 1)] = N::zero();
|
t[(k + 1, k - 1)] = N::zero();
|
||||||
t[(k + 2, k - 1)] = N::zero();
|
t[(k + 2, k - 1)] = N::zero();
|
||||||
}
|
}
|
||||||
|
@ -218,9 +218,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diagonal[k + 0] = subm[(0, 0)];
|
diagonal[k] = subm[(0, 0)];
|
||||||
diagonal[k + 1] = subm[(1, 1)];
|
diagonal[k + 1] = subm[(1, 1)];
|
||||||
off_diagonal[k + 0] = subm[(0, 1)];
|
off_diagonal[k] = subm[(0, 1)];
|
||||||
|
|
||||||
if k != n - 1 {
|
if k != n - 1 {
|
||||||
off_diagonal[k + 1] = subm[(1, 2)];
|
off_diagonal[k + 1] = subm[(1, 2)];
|
||||||
@ -244,7 +244,7 @@ where
|
|||||||
let u2 = u2.map(|u2| GivensRotation::new_unchecked(u2.c(), N::from_real(u2.s())));
|
let u2 = u2.map(|u2| GivensRotation::new_unchecked(u2.c(), N::from_real(u2.s())));
|
||||||
let v2 = v2.map(|v2| GivensRotation::new_unchecked(v2.c(), N::from_real(v2.s())));
|
let v2 = v2.map(|v2| GivensRotation::new_unchecked(v2.c(), N::from_real(v2.s())));
|
||||||
|
|
||||||
diagonal[start + 0] = s[0];
|
diagonal[start] = s[0];
|
||||||
diagonal[start + 1] = s[1];
|
diagonal[start + 1] = s[1];
|
||||||
off_diagonal[start] = N::RealField::zero();
|
off_diagonal[start] = N::RealField::zero();
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ where
|
|||||||
let eigvals = m.eigenvalues().unwrap();
|
let eigvals = m.eigenvalues().unwrap();
|
||||||
let basis = Vector2::new(eigvals.x - diag[start + 1], off_diag[start]);
|
let basis = Vector2::new(eigvals.x - diag[start + 1], off_diag[start]);
|
||||||
|
|
||||||
diag[start + 0] = eigvals[0];
|
diag[start] = eigvals[0];
|
||||||
diag[start + 1] = eigvals[1];
|
diag[start + 1] = eigvals[1];
|
||||||
|
|
||||||
if let Some(ref mut q) = q {
|
if let Some(ref mut q) = q {
|
||||||
|
59
tests/core/cg.rs
Normal file
59
tests/core/cg.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use na::{Matrix3, Matrix4, Point2, Point3, Vector2, Vector3};
|
||||||
|
|
||||||
|
/// See Example 3.4 of "Graphics and Visualization: Principles & Algorithms"
|
||||||
|
/// by Theoharis, Papaioannou, Platis, Patrikalakis.
|
||||||
|
#[test]
|
||||||
|
fn test_scaling_wrt_point_1() {
|
||||||
|
let a = Point2::new(0.0, 0.0);
|
||||||
|
let b = Point2::new(1.0, 1.0);
|
||||||
|
let c = Point2::new(5.0, 2.0);
|
||||||
|
|
||||||
|
let scaling = Vector2::new(2.0, 2.0);
|
||||||
|
let scale_about = Matrix3::new_nonuniform_scaling_wrt_point(&scaling, &c);
|
||||||
|
|
||||||
|
let expected_a = Point2::new(-5.0, -2.0);
|
||||||
|
let expected_b = Point2::new(-3.0, 0.0);
|
||||||
|
let result_a = scale_about.transform_point(&a);
|
||||||
|
let result_b = scale_about.transform_point(&b);
|
||||||
|
let result_c = scale_about.transform_point(&c);
|
||||||
|
|
||||||
|
assert!(expected_a == result_a);
|
||||||
|
assert!(expected_b == result_b);
|
||||||
|
assert!(c == result_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Based on the same example as the test above.
|
||||||
|
#[test]
|
||||||
|
fn test_scaling_wrt_point_2() {
|
||||||
|
let a = Point3::new(0.0, 0.0, 1.0);
|
||||||
|
let b = Point3::new(1.0, 1.0, 1.0);
|
||||||
|
let c = Point3::new(5.0, 2.0, 1.0);
|
||||||
|
|
||||||
|
let scaling = Vector3::new(2.0, 2.0, 1.0);
|
||||||
|
let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(&scaling, &c);
|
||||||
|
|
||||||
|
let expected_a = Point3::new(-5.0, -2.0, 1.0);
|
||||||
|
let expected_b = Point3::new(-3.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
let result_a = scale_about.transform_point(&a);
|
||||||
|
let result_b = scale_about.transform_point(&b);
|
||||||
|
let result_c = scale_about.transform_point(&c);
|
||||||
|
|
||||||
|
assert!(expected_a == result_a);
|
||||||
|
assert!(expected_b == result_b);
|
||||||
|
assert!(c == result_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Based on https://github.com/emlowry/AiE/blob/50bae4068edb686cf8ffacdf6fab8e7cb22e7eb1/Year%201%20Classwork/MathTest/Matrix4x4TestGroup.cpp#L145
|
||||||
|
#[test]
|
||||||
|
fn test_scaling_wrt_point_3() {
|
||||||
|
let about = Point3::new(2.0, 1.0, -2.0);
|
||||||
|
let scale = Vector3::new(2.0, 0.5, -1.0);
|
||||||
|
let pt = Point3::new(1.0, 2.0, 3.0);
|
||||||
|
let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(&scale, &about);
|
||||||
|
|
||||||
|
let expected = Point3::new(0.0, 1.5, -7.0);
|
||||||
|
let result = scale_about.transform_point(&pt);
|
||||||
|
|
||||||
|
assert!(result == expected);
|
||||||
|
}
|
85
tests/core/matrixcompare.rs
Normal file
85
tests/core/matrixcompare.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//! Tests for `matrixcompare` integration.
|
||||||
|
//!
|
||||||
|
//! The `matrixcompare` crate itself is responsible for testing the actual comparison.
|
||||||
|
//! The tests here only check that the necessary trait implementations are correctly implemented,
|
||||||
|
//! in addition to some sanity checks with example input.
|
||||||
|
|
||||||
|
use nalgebra::{DMatrix, MatrixMN, U4, U5};
|
||||||
|
|
||||||
|
use matrixcompare::{assert_matrix_eq, DenseAccess};
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
quickcheck! {
|
||||||
|
fn fetch_single_is_equivalent_to_index_f64(matrix: DMatrix<f64>) -> bool {
|
||||||
|
for i in 0 .. matrix.nrows() {
|
||||||
|
for j in 0 .. matrix.ncols() {
|
||||||
|
if matrix.fetch_single(i, j) != *matrix.index((i, j)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matrixcompare_shape_agrees_with_matrix(matrix: DMatrix<f64>) -> bool {
|
||||||
|
matrix.nrows() == <DMatrix<f64> as matrixcompare::Matrix<f64>>::rows(&matrix)
|
||||||
|
&&
|
||||||
|
matrix.ncols() == <DMatrix<f64> as matrixcompare::Matrix<f64>>::cols(&matrix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assert_matrix_eq_dense_positive_comparison() {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let a = MatrixMN::<_, U4, U5>::from_row_slice(&[
|
||||||
|
1210, 1320, 1430, 1540, 1650,
|
||||||
|
2310, 2420, 2530, 2640, 2750,
|
||||||
|
3410, 3520, 3630, 3740, 3850,
|
||||||
|
4510, 4620, 4730, 4840, 4950,
|
||||||
|
]);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let b = MatrixMN::<_, U4, U5>::from_row_slice(&[
|
||||||
|
1210, 1320, 1430, 1540, 1650,
|
||||||
|
2310, 2420, 2530, 2640, 2750,
|
||||||
|
3410, 3520, 3630, 3740, 3850,
|
||||||
|
4510, 4620, 4730, 4840, 4950,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test matrices of static size
|
||||||
|
assert_matrix_eq!(a, b);
|
||||||
|
assert_matrix_eq!(&a, b);
|
||||||
|
assert_matrix_eq!(a, &b);
|
||||||
|
assert_matrix_eq!(&a, &b);
|
||||||
|
|
||||||
|
// Test matrices of dynamic size
|
||||||
|
let a_dyn = a.index((0..4, 0..5));
|
||||||
|
let b_dyn = b.index((0..4, 0..5));
|
||||||
|
assert_matrix_eq!(a_dyn, b_dyn);
|
||||||
|
assert_matrix_eq!(a_dyn, &b_dyn);
|
||||||
|
assert_matrix_eq!(&a_dyn, b_dyn);
|
||||||
|
assert_matrix_eq!(&a_dyn, &b_dyn);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn assert_matrix_eq_dense_negative_comparison() {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let a = MatrixMN::<_, U4, U5>::from_row_slice(&[
|
||||||
|
1210, 1320, 1430, 1540, 1650,
|
||||||
|
2310, 2420, 2530, 2640, 2750,
|
||||||
|
3410, 3520, 3630, 3740, 3850,
|
||||||
|
4510, 4620, -4730, 4840, 4950,
|
||||||
|
]);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let b = MatrixMN::<_, U4, U5>::from_row_slice(&[
|
||||||
|
1210, 1320, 1430, 1540, 1650,
|
||||||
|
2310, 2420, 2530, 2640, 2750,
|
||||||
|
3410, 3520, 3630, 3740, 3850,
|
||||||
|
4510, 4620, 4730, 4840, 4950,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_matrix_eq!(a, b);
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
#[cfg(feature = "abomonation-serialize")]
|
#[cfg(feature = "abomonation-serialize")]
|
||||||
mod abomonation;
|
mod abomonation;
|
||||||
mod blas;
|
mod blas;
|
||||||
|
mod cg;
|
||||||
mod conversion;
|
mod conversion;
|
||||||
mod edition;
|
mod edition;
|
||||||
mod empty;
|
mod empty;
|
||||||
@ -10,5 +11,8 @@ mod matrix_slice;
|
|||||||
mod mint;
|
mod mint;
|
||||||
mod serde;
|
mod serde;
|
||||||
|
|
||||||
|
#[cfg(feature = "compare")]
|
||||||
|
mod matrixcompare;
|
||||||
|
|
||||||
#[cfg(feature = "arbitrary")]
|
#[cfg(feature = "arbitrary")]
|
||||||
pub mod helper;
|
pub mod helper;
|
||||||
|
@ -126,4 +126,51 @@ mod tests {
|
|||||||
|
|
||||||
assert!(relative_eq!(f, m.exp(), epsilon = 1.0e-7));
|
assert!(relative_eq!(f, m.exp(), epsilon = 1.0e-7));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exp_complex() {
|
||||||
|
use nalgebra::{Complex, DMatrix, DVector, Matrix2, RealField};
|
||||||
|
|
||||||
|
{
|
||||||
|
let z = Matrix2::<Complex<f64>>::zeros();
|
||||||
|
|
||||||
|
let identity = Matrix2::<Complex<f64>>::identity();
|
||||||
|
|
||||||
|
assert!((z.exp() - identity).norm() < 1e-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let a = Matrix2::<Complex<f64>>::new(
|
||||||
|
Complex::<f64>::new(0.0, 1.0),
|
||||||
|
Complex::<f64>::new(0.0, 2.0),
|
||||||
|
Complex::<f64>::new(0.0, -1.0),
|
||||||
|
Complex::<f64>::new(0.0, 3.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
let b = Matrix2::<Complex<f64>>::new(
|
||||||
|
Complex::<f64>::new(0.42645929666726, 1.89217550966333),
|
||||||
|
Complex::<f64>::new(-2.13721484276556, -0.97811251808259),
|
||||||
|
Complex::<f64>::new(1.06860742138278, 0.48905625904129),
|
||||||
|
Complex::<f64>::new(-1.7107555460983, 0.91406299158075),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!((a.exp() - b).norm() < 1.0e-07);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let d1 = Complex::<f64>::new(0.0, <f64 as RealField>::pi());
|
||||||
|
let d2 = Complex::<f64>::new(0.0, <f64 as RealField>::frac_pi_2());
|
||||||
|
let d3 = Complex::<f64>::new(0.0, <f64 as RealField>::frac_pi_4());
|
||||||
|
|
||||||
|
let m = DMatrix::<Complex<f64>>::from_diagonal(&DVector::from_row_slice(&[d1, d2, d3]));
|
||||||
|
|
||||||
|
let res = DMatrix::<Complex<f64>>::from_diagonal(&DVector::from_row_slice(&[
|
||||||
|
d1.exp(),
|
||||||
|
d2.exp(),
|
||||||
|
d3.exp(),
|
||||||
|
]));
|
||||||
|
|
||||||
|
assert!((m.exp() - res).norm() < 1e-07);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user