cgmath-0.18.0/.cargo_vcs_info.json0000644000000001121377421364300124270ustar { "git": { "sha1": "637c566cc2141203d8d99c03e7ab770796c44f5f" } } cgmath-0.18.0/.github/workflows/tests.yml010064400007650000024000000041461376272520100165160ustar 00000000000000name: tests on: schedule: # build at least once per week - cron: "0 0 * * 0" pull_request: push: paths-ignore: - "README.md" - "CHANGELOG.md" jobs: # TODO: enable after running cargo fmt again # check: # name: Check formatting # runs-on: ubuntu-latest # steps: # - uses: actions/checkout@v2 # - uses: actions-rs/toolchain@v1 # with: # toolchain: stable # override: true # components: rustfmt # profile: minimal # - name: Check formatting # uses: actions-rs/cargo@v1 # with: # command: fmt # args: --all -- --check test: name: Test runs-on: ${{ matrix.os }} env: RUST_BACKTRACE: 1 strategy: matrix: os: [windows-latest, ubuntu-latest, macos-latest] toolchain: [stable, nightly] features: - "" - "serde,mint" steps: - uses: actions/checkout@v2 - name: Install latest rust uses: actions-rs/toolchain@v1 id: rust with: toolchain: ${{ matrix.toolchain }} override: true profile: minimal - name: Build uses: actions-rs/cargo@v1 with: command: build args: --lib --features "${{ matrix.features }}" - name: Test uses: actions-rs/cargo@v1 with: command: test args: --features "${{ matrix.features }}" benchmark: name: Benchmark runs-on: ${{ matrix.os }} env: RUST_BACKTRACE: 1 strategy: matrix: os: [ubuntu-latest] toolchain: [nightly] features: # The benchmark always needs the 'rand' feature - "rand,serde,mint" steps: - uses: actions/checkout@v2 - name: Install latest rust uses: actions-rs/toolchain@v1 id: rust with: toolchain: ${{ matrix.toolchain }} override: true profile: minimal - name: Benchmark uses: actions-rs/cargo@v1 with: command: bench args: --features "${{ matrix.features }}" cgmath-0.18.0/.gitignore010064400007650000024000000001051377421362200132150ustar 00000000000000.DS_Store .vs/ /target /Cargo.lock /bench/bench .vscode .idea *.iml cgmath-0.18.0/CHANGELOG.md010064400007650000024000000307021376272520100130420ustar 00000000000000# Change Log All notable changes to this project will be documented in this file, following the format defined at [keepachangelog.com](http://keepachangelog.com/). This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Changed - Refactored dependencies of experimental "specialization" feature into default_fn! macro to reduce code duplication and complexity. Currently only needed for non-functional SIMD feature. - Refactored SIMD code into separate source files. See README.md for details. - **Breaking**: Quaternion memory layout changed to `[x, y, z, w]`. The `From` and `Into` impls for `[S; 4]` and `(S, S, S, S)` have been changed accordingly. ### Added - Add `VectorN::zip` and `PointN::zip` ## [v0.17.0] - 2019-01-17 ### Added - Add signed `Angle` normalization ### Changed - Move `lerp()` from `InnerSpace` to `VectorSpace` - `const` constructors ## [v0.16.1] - 2018-03-21 ### Added - Implement `ElementWise` trait for point types - Add `map` function to points and vectors ### Changed - Remove `BaseNum` trait requirement for `PointN::new` functions ## [v0.16.0] - 2018-01-03 ### Added - Add `InnerSpace::project_on` - Add `Array::len` - Re-export `Bounded` and implement for vectors, points, and angles - Add vector subtraction to `EuclideanSpace` - Add swizzle functions behinde that `"swizzle"` feature - Add `Matrix4::look_at_dir` ### Changed - Return `Option` from cast functions ## [v0.15.0] - 2017-07-30 ### Added - Implement `mint` conversions behind a feature - Add `Quaternion::cast` ### Changed - Rename `use_simd` feature to `simd` - Rename `eders` feature to `serde` ### Fixed - Fix matrix inversions for small determinants ## [v0.14.1] - 2017-05-02 ### Fixed - Add a workaround for rust-lang/rust#41478, and in the process cleaned up some type projections for angles ## [v0.14.0] - 2017-04-26 ## Changed - Constrain `VectorSpace`, `Rotation`, and `Angle` by `iter::Sum` - Constrain `SquareMatrix` by `iter::Product` ## [v0.13.1] - 2017-04-22 ### Changed - Update `serde` and `serde_derive` to version `1.0`. ## [v0.13.0] - 2017-04-14 ### Added - Add optional `use_simd` feature to improve the performance of `Vector4`, `Matrix4` and `Quaternion`. According to @DaseinPhaos in #394, under the given benchmark certain operations were able to become up to 60% faster. - Add component wise casting for the matrix and point types ### Changed - Update `serde` to version `0.9`, and use `serde_derive` instead of `serde_macros`. ## [v0.12.0] - 2016-09-14 ### Changed - Use [approx](https://github.com/brendanzab/approx/) for approximate equality comparisons - Remove `#[repr(packed)]` from all structs where it was specified - Update serde to 0.8 ## [v0.11.0] - 2016-08-17 ### Added - `Quaternion::from_arc` ### Changed - Change the angle types to be tuple structs - Make from-angle constructors take generic `Into>` values - Fix `Decomposed::concat` implementation ## [v0.10.0] - 2016-05-11 ### Added - A `MetricSpace` trait for types that have a distance between elements. - `EuclideanSpace::{midpoint, centroid}` functions with default implementations. - `Vector1` and `Point1` structs. - Serde support behind the `eders` feature flag. - An `ApproxEq` implementation for `Decomposed`. ### Changed - Depend on the `num-traits` crate rather than `num`, seeing as we only use the traits in `num`. `num_traits` has also been re-exported so that you can more easily use these in your project. - Use an `Euler` type for euler angle conversions. - Constrain `InnerSpace` by `MetricSpace`. - Constrain `Rotation` by `One` - Implement `Transform` and `Transform3` for `Matrix4`. - Implement `Transform`, `Transform2`, and `Transform3` for `Matrix4`. - Fix `Euler`-`Quaternion` and `Quaternion`-`Euler` conversions. The axes are now correct, and the angles are applied in _x_-_y_-_z_ order. The conversion now matches the conversion from axis angle. - Fix `Euler`-`{Matrix3, Matrix4}` conversions. ## Removed - `Rotation::transform_as_point` - `AffineMatrix3` - `Rotation::invert_self` - `Matrix::invert_self` ## [v0.9.1] - 2016-04-20 ### Changed - Fix angle assignment operators so that they actually mutate `self`. ## [v0.9.0] - 2016-04-19 ### Changed - Assignment operators implementations have been stabilised, to coincide with their [stabilisation in Rust 1.8](http://blog.rust-lang.org/2016/04/14/Rust-1.8.html). - Renames `Vector` trait to `VectorSpace`. - Renames `EuclideanVector` to `InnerSpace`. - Renames `Point` to `EuclideanSpace`, and `Point::Vector` to `EuclideanSpace::Diff`. - `Quaternion`s now implement `VectorSpace` and `InnerSpace` for the functions they share. - The `Matrix` trait is now constraint by `VectorSpace`, with `Matrix::Element` removed in favor of `VectorSpace::Scalar`. ## [v0.8.0] - 2016-04-06 ### Added - Implements `fmt::Debug` for `Basis2`, `Basis3`, and `AffineMatrix3` - A `prelude` module for easy importing of common traits. - Constrained conversion functions for assisting in situations where type inference is difficult. - An `ElementWise` trait for non-mathematical element-wise operations. - A default implementation for `EuclideanVector::angle`. ### Changed - Improves the `fmt::Debug` impls for `Vector`, `Matrix`, `Point`, `Decomposed`, `Quaternion` and `Angle` to make them easier to derive, and have clearer formatting. - Marks vectors, points, matrices, and angles as `#[repr(C, packed)]`. - Renames the `Vector::{length, length2}` functions to `Vector::{magnitude, magnitude2}`. - Move `Angle::new` to be directly implemented on the `Rad` and `Deg` types. - Move `Vector::dot` to `EuclideanVector` trait. - Move `Vector::from_value` to `Array` trait. ### Removed - The non-mathematical operator trait implementations have been removed from the `Vector` trait, in favor of the `ElementWise` trait. - `Angle::equiv`. - Remove `neg_self` method on vectors and matrices. ## [v0.7.0] - 2015-12-23 ### Added - Add missing by-ref and by-val permutations of `Vector`, `Matrix`, `Point`, `Quaternion` and `Angle` operators. - Ease lifetime constraints by removing `'static` from some scalar type parameters. - Weaken type constraints on `perspective` function to take an `Into>`. - Add `Angle::new` for constructing angles from a unitless scalar. - Implement assignment operators for nightly builds, enabled by the `"unstable"` feature. ### Changed - `Vector`, `Matrix`, `Point`, and `Angle` are now constrained to require specific operators to be overloaded. This means that generic code can now use operators, instead of the operator methods. - Take a `Rad` for `ProjectionFov::fovy`, rather than arbitrary `Angle`s. This simplifies the signature of `PerspectiveFov` from `PerspectiveFov` to `PerspectiveFov`. - The following trait constraints were removed from `Angle`: `Debug`, `ScalarConv`, `Into>`, `Into>`. - `Angle` no longer requires `One`, and the implementations have been removed from `Deg` and `Rad`. This is because angles do not close over multiplication, and therefore cannot have a multiplicative identity. If we were truly accurate, `Angle * Angle` would return an `Angle^2` (not supported by the current api). - Make remainder operators on `Angle`s make sense from the perspective of dimensional analysis. - Moved free trigonometric functions onto `Angle`. ### Removed - Remove redundant `Point::{min, max}` methods - these are now covered by the `Array::{min, max}` methods that were introduced in 0.5.0. - Removed `ToComponents`, `ToComponents2`, and `ToComponents3`. If you were relying on `ToComponents::decompose`, you can produce the same effect by accessing the fields on `Decomposed` directly. To create the scale vector, use: `Vector::from_value(transform.scale)`. - Removed `CompositeTransform`, `CompositeTransform2`, and `CompositeTransform3`. - Remove `Vector::one`. Vectors don't really have a multiplicative identity. If you really want a `one` vector, you can do something like: `Vector::from_value(1.0)`. - Remove operator methods from `Vector`, `Matrix`, `Point`, and `Angle` traits in favor of operator overloading. - Remove `*_self` methods from `Vector`, `Matrix`, `Point`, and `Angle`. The operator methods can be used via the unstable assignment operators. - Remove `#[derive(Hash)]` from `Deg` and `Rad`. This could never really be used these types, because they expect to be given a `BaseFloat` under normal circumstances. ## [v0.6.0] - 2015-12-12 ### Added - This CHANGELOG for keeping track of notable changes. - `Matrix4::{from_scale, from_nonuniform_scale}` for easily constructing homogeneous scale matrices. ### Changed - Renamed `SquareMatrix::one` to `SquareMatrix::identity`. `identity` is easier to search for, and the more common name for the multiplicative identity for matrices. - Matrix impls have now been constrained to `S: BaseFloat`. ## [v0.5.0] - 2015-11-20 ### Changed - Take many point and vector parameters by value. - Take point and vector operator overloads by value. - Divide `Matrix` trait into `Matrix` and `SquareMatrix`, opening the door for non-square matrices in the future. - Make many trait type parameters associated types. - Move element-wise methods from `Vector` and `Point` onto the `Array1` trait, and rename it to `Array`. - Make pointer access methods on `Array` match the naming scheme of those in the standard library. ### Removed - Removed collision types: `Ray`, `Plane`, `Frustum`, `Aabb2`, `Aabb3` `Obb2`, `Obb3` `Sphere`, `Cylinder`. These can now be found at [csherratt/collision-rs](https://github.com/csherratt/collision-rs). - Remove `Array2` trait, moving methods onto the `Matrix` trait. ## [v0.4.0] - 2015-10-25 ## [v0.3.1] - 2015-09-20 ## [v0.3.0] - 2015-09-20 ## [v0.2.0] - 2015-05-11 ## [v0.1.6] - 2015-05-10 ## [v0.1.5] - 2015-04-25 ## [v0.1.4] - 2015-04-24 ## [v0.1.3] - 2015-04-06 ## [v0.1.2] - 2015-04-01 ## [v0.1.1] - 2015-03-25 ## [v0.1.0] - 2015-03-15 ## [v0.0.8] - 2015-03-09 ## [v0.0.7] - 2015-03-01 ## [v0.0.6] - 2015-02-21 ## [v0.0.5] - 2015-02-16 ## [v0.0.4] - 2015-02-11 ## [v0.0.3] - 2015-02-08 ## v0.0.1 - 2014-06-24 [Unreleased]: https://github.com/brendanzab/cgmath/compare/v0.16.1...HEAD [v0.16.1]: https://github.com/brendanzab/cgmath/compare/v0.16.0...v0.16.1 [v0.16.0]: https://github.com/brendanzab/cgmath/compare/v0.15.0...v0.16.0 [v0.15.0]: https://github.com/brendanzab/cgmath/compare/v0.14.1...v0.15.0 [v0.14.1]: https://github.com/brendanzab/cgmath/compare/v0.14.0...v0.14.1 [v0.14.0]: https://github.com/brendanzab/cgmath/compare/v0.13.1...v0.14.0 [v0.13.1]: https://github.com/brendanzab/cgmath/compare/v0.13.0...v0.13.1 [v0.12.0]: https://github.com/brendanzab/cgmath/compare/v0.12.0...v0.13.0 [v0.12.0]: https://github.com/brendanzab/cgmath/compare/v0.11.0...v0.12.0 [v0.11.0]: https://github.com/brendanzab/cgmath/compare/v0.10.0...v0.11.0 [v0.10.0]: https://github.com/brendanzab/cgmath/compare/v0.9.1...v0.10.0 [v0.9.1]: https://github.com/brendanzab/cgmath/compare/v0.9.0...v0.9.1 [v0.9.0]: https://github.com/brendanzab/cgmath/compare/v0.8.0...v0.9.0 [v0.8.0]: https://github.com/brendanzab/cgmath/compare/v0.7.0...v0.8.0 [v0.7.0]: https://github.com/brendanzab/cgmath/compare/v0.6.0...v0.7.0 [v0.6.0]: https://github.com/brendanzab/cgmath/compare/v0.5.0...v0.6.0 [v0.5.0]: https://github.com/brendanzab/cgmath/compare/v0.4.0...v0.5.0 [v0.4.0]: https://github.com/brendanzab/cgmath/compare/v0.3.1...v0.4.0 [v0.3.1]: https://github.com/brendanzab/cgmath/compare/v0.3.0...v0.3.1 [v0.3.0]: https://github.com/brendanzab/cgmath/compare/v0.2.0...v0.3.0 [v0.2.0]: https://github.com/brendanzab/cgmath/compare/v0.1.6...v0.2.0 [v0.1.6]: https://github.com/brendanzab/cgmath/compare/v0.1.5...v0.1.6 [v0.1.5]: https://github.com/brendanzab/cgmath/compare/v0.1.4...v0.1.5 [v0.1.4]: https://github.com/brendanzab/cgmath/compare/v0.1.3...v0.1.4 [v0.1.3]: https://github.com/brendanzab/cgmath/compare/v0.1.2...v0.1.3 [v0.1.2]: https://github.com/brendanzab/cgmath/compare/v0.1.1...v0.1.2 [v0.1.1]: https://github.com/brendanzab/cgmath/compare/v0.1.0...v0.1.1 [v0.1.0]: https://github.com/brendanzab/cgmath/compare/v0.0.8...v0.1.0 [v0.0.8]: https://github.com/brendanzab/cgmath/compare/v0.0.7...v0.0.8 [v0.0.7]: https://github.com/brendanzab/cgmath/compare/v0.0.6...v0.0.7 [v0.0.6]: https://github.com/brendanzab/cgmath/compare/v0.0.5...v0.0.6 [v0.0.5]: https://github.com/brendanzab/cgmath/compare/v0.0.4...v0.0.5 [v0.0.4]: https://github.com/brendanzab/cgmath/compare/v0.0.3...v0.0.4 [v0.0.3]: https://github.com/brendanzab/cgmath/compare/v0.0.1...v0.0.3 cgmath-0.18.0/Cargo.toml0000644000000024631377421364300104400ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "cgmath" version = "0.18.0" authors = ["Rust game-developers"] description = "A linear algebra and mathematics library for computer graphics." homepage = "https://github.com/rustgd/cgmath" documentation = "https://docs.rs/cgmath" readme = "README.md" keywords = ["gamedev", "math", "matrix", "vector", "quaternion"] license = "Apache-2.0" repository = "https://github.com/rustgd/cgmath" [lib] name = "cgmath" [dependencies.approx] version = "0.4" [dependencies.mint] version = "0.5" optional = true [dependencies.num-traits] version = "0.2" [dependencies.rand] version = "0.8" features = ["small_rng"] optional = true [dependencies.serde] version = "1.0" features = ["serde_derive"] optional = true [dev-dependencies.serde_json] version = "1.0" [features] swizzle = [] unstable = [] cgmath-0.18.0/Cargo.toml.orig010064400007650000024000000015751377421362200141300ustar 00000000000000[package] name = "cgmath" version = "0.18.0" authors = ["Rust game-developers"] license = "Apache-2.0" description = "A linear algebra and mathematics library for computer graphics." documentation = "https://docs.rs/cgmath" homepage = "https://github.com/rustgd/cgmath" repository = "https://github.com/rustgd/cgmath" readme = "README.md" keywords = ["gamedev", "math", "matrix", "vector", "quaternion"] [lib] name = "cgmath" [features] unstable = [] swizzle = [] [dependencies] approx = "0.4" mint = { version = "0.5", optional = true } num-traits = "0.2" # small_rng used only for benchmarks rand = { version = "0.8", features = ["small_rng"], optional = true } serde = { version = "1.0", features = ["serde_derive"], optional = true } # works only in rust toolchain up to 1.32, disabled indefinitely #simd = { version = "0.2", optional = true } [dev-dependencies] serde_json = "1.0" cgmath-0.18.0/LICENSE010064400007650000024000000261361376272520100122440ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity 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 [yyyy] [name of copyright owner] 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. cgmath-0.18.0/README.md010064400007650000024000000066751377421362200125260ustar 00000000000000# cgmath-rs [![Build Status](https://github.com/rustgd/cgmath/workflows/tests/badge.svg)](https://github.com/rustgd/cgmath/actions) [![Documentation](https://docs.rs/cgmath/badge.svg)](https://docs.rs/cgmath) [![Version](https://img.shields.io/crates/v/cgmath.svg)](https://crates.io/crates/cgmath) [![License](https://img.shields.io/crates/l/cgmath.svg)](https://github.com/rustgd/cgmath/blob/master/LICENSE) [![Downloads](https://img.shields.io/crates/d/cgmath.svg)](https://crates.io/crates/cgmath) [![Gitter](https://badges.gitter.im/brendanzab/cgmath.svg)](https://gitter.im/brendanzab/cgmath) A linear algebra and mathematics library for computer graphics. The library provides: - vectors: `Vector2`, `Vector3`, `Vector4` - square matrices: `Matrix2`, `Matrix3`, `Matrix4` - a quaternion type: `Quaternion` - rotation matrices: `Basis2`, `Basis3` - angle units: `Rad`, `Deg` - points: `Point2`, `Point3` - perspective projections: `Perspective`, `PerspectiveFov`, `Ortho` - spatial transformations: `AffineMatrix3`, `Transform3` Not all of the functionality has been implemented yet, and the existing code is not fully covered by the testsuite. If you encounter any mistakes or omissions please let me know by posting an issue, or even better: send me a pull request with a fix. ## Conventions cgmath interprets its vectors as column matrices (also known as "column vectors"), meaning when transforming a vector with a matrix, the matrix goes on the left. This is reflected in the fact that cgmath implements the multiplication operator for Matrix * Vector, but not Vector * Matrix. ## Features ### Swizzling This library offers an optional feature called ["swizzling"](https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)) widely familiar to GPU programmers. To enable swizzle operators, pass the `--features="swizzle"` option to cargo. Enabling this feature will increase the size of the cgmath library by approximately 0.6MB. This isn't an issue if the library is linked in the "normal" way by adding cgmath as a dependency in Cargo.toml, which will link cgmath statically so all unused swizzle operators will be optimized away by the compiler in release mode. #### Example If we have ```rust let v = Vector3::new(1.0, 2.0, 3.0); ``` then `v.xyxz()` produces a ```rust Vector4 { x: 1.0, y: 2.0, z: 1.0, w: 3.0 } ``` and `v.zy()` produces a ```rust Vector2 { x: 3.0, y: 2.0 } ``` ### SIMD optimizations The current SIMD support depends on the deprecated "simd" package as well as the unstable "specialization" feature. To build this code, a pre-1.33 nightly build of Rust is required, e.g. 2019-01-01-nightly. Though the code is not useful in its present form, it has some worth preserving as starting point for a future migration (see https://github.com/rustgd/cgmath/issues/490). ## Limitations cgmath is _not_ an n-dimensional library and is aimed at computer graphics applications rather than general linear algebra. It only offers the 2, 3, and 4 dimensional structures that are more than enough for most computer graphics applications. This design decision was made in order to simplify the implementation (Rust cannot parameterize over constants at compile time), and to make dimension-specific optimisations easier in the future. ## Contributing Pull requests are most welcome, especially in the realm of performance enhancements and fixing any mistakes I may have made along the way. Unit tests and benchmarks are also required, so help on that front would be most appreciated. cgmath-0.18.0/benches/common/macros.rs010064400007650000024000000047121376272520100157640ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. macro_rules! bench_binop { ($name: ident, $t1: ty, $t2: ty, $binop: ident) => { #[bench] fn $name(bh: &mut Bencher) { const LEN: usize = 1 << 13; let mut rng = SmallRng::from_entropy(); let elems1: Vec<$t1> = (0..LEN).map(|_| rng.gen::<$t1>()).collect(); let elems2: Vec<$t2> = (0..LEN).map(|_| rng.gen::<$t2>()).collect(); let mut i = 0; bh.iter(|| { i = (i + 1) & (LEN - 1); unsafe { test::black_box(elems1.get_unchecked(i).$binop(*elems2.get_unchecked(i))) } }) } }; } macro_rules! bench_unop { ($name: ident, $t: ty, $unop: ident) => { #[bench] fn $name(bh: &mut Bencher) { const LEN: usize = 1 << 13; let mut rng = SmallRng::from_entropy(); let mut elems: Vec<$t> = (0..LEN).map(|_| rng.gen::<$t>()).collect(); let mut i = 0; bh.iter(|| { i = (i + 1) & (LEN - 1); unsafe { test::black_box(elems.get_unchecked_mut(i).$unop()) } }) } }; } macro_rules! bench_construction { ($name: ident, $t: ty, $constructor: path [ $($args: ident: $types: ty),+ ]) => { #[bench] fn $name(bh: &mut Bencher) { const LEN: usize = 1 << 13; let mut rng = SmallRng::from_entropy(); $(let $args: Vec<$types> = (0..LEN).map(|_| rng.gen::<$types>()).collect();)* let mut i = 0; bh.iter(|| { i = (i + 1) & (LEN - 1); unsafe { let res: $t = $constructor($(*$args.get_unchecked(i),)*); test::black_box(res) } }) } }; } cgmath-0.18.0/benches/construction.rs010064400007650000024000000040571376272520100157440ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. #![feature(test)] #![allow(unused_macros)] extern crate cgmath; extern crate rand; extern crate test; use rand::{rngs::SmallRng, Rng, SeedableRng}; use test::Bencher; use cgmath::*; #[path = "common/macros.rs"] #[macro_use] mod macros; fn bench_from_axis_angle>(bh: &mut Bencher) { const LEN: usize = 1 << 13; let mut rng = SmallRng::from_entropy(); let axis: Vec<_> = (0..LEN).map(|_| rng.gen::>()).collect(); let angle: Vec<_> = (0..LEN).map(|_| rng.gen::>()).collect(); let mut i = 0; bh.iter(|| { i = (i + 1) & (LEN - 1); unsafe { let res: T = Rotation3::from_axis_angle(*axis.get_unchecked(i), *angle.get_unchecked(i)); test::black_box(res) } }) } #[bench] fn _bench_quat_from_axisangle(bh: &mut Bencher) { bench_from_axis_angle::>(bh) } #[bench] fn _bench_rot3_from_axisangle(bh: &mut Bencher) { bench_from_axis_angle::>(bh) } bench_construction!( _bench_rot2_from_axisangle, Basis2, Basis2::from_angle[angle: Rad] ); bench_construction!( _bench_quat_from_euler_angles, Quaternion, Quaternion::from[src: Euler>] ); bench_construction!( _bench_rot3_from_euler_angles, Basis3, Basis3::from[src: Euler>] ); cgmath-0.18.0/benches/mat.rs010064400007650000024000000050611376272520100137670ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. #![feature(test)] #![allow(unused_macros)] extern crate cgmath; extern crate rand; extern crate test; use rand::{rngs::SmallRng, Rng, SeedableRng}; use std::ops::*; use test::Bencher; use cgmath::*; #[path = "common/macros.rs"] #[macro_use] mod macros; bench_binop!(_bench_matrix2_mul_m, Matrix2, Matrix2, mul); bench_binop!(_bench_matrix3_mul_m, Matrix3, Matrix3, mul); bench_binop!(_bench_matrix4_mul_m, Matrix4, Matrix4, mul); bench_binop!(_bench_matrix2_add_m, Matrix2, Matrix2, add); bench_binop!(_bench_matrix3_add_m, Matrix3, Matrix3, add); bench_binop!(_bench_matrix4_add_m, Matrix4, Matrix4, add); bench_binop!(_bench_matrix2_sub_m, Matrix2, Matrix2, sub); bench_binop!(_bench_matrix3_sub_m, Matrix3, Matrix3, sub); bench_binop!(_bench_matrix4_sub_m, Matrix4, Matrix4, sub); bench_binop!(_bench_matrix2_mul_v, Matrix2, Vector2, mul); bench_binop!(_bench_matrix3_mul_v, Matrix3, Vector3, mul); bench_binop!(_bench_matrix4_mul_v, Matrix4, Vector4, mul); bench_binop!(_bench_matrix2_mul_s, Matrix2, f32, mul); bench_binop!(_bench_matrix3_mul_s, Matrix3, f32, mul); bench_binop!(_bench_matrix4_mul_s, Matrix4, f32, mul); bench_binop!(_bench_matrix2_div_s, Matrix2, f32, div); bench_binop!(_bench_matrix3_div_s, Matrix3, f32, div); bench_binop!(_bench_matrix4_div_s, Matrix4, f32, div); bench_unop!(_bench_matrix2_invert, Matrix2, invert); bench_unop!(_bench_matrix3_invert, Matrix3, invert); bench_unop!(_bench_matrix4_invert, Matrix4, invert); bench_unop!(_bench_matrix2_transpose, Matrix2, transpose); bench_unop!(_bench_matrix3_transpose, Matrix3, transpose); bench_unop!(_bench_matrix4_transpose, Matrix4, transpose); bench_unop!(_bench_matrix4_determinant, Matrix4, determinant); cgmath-0.18.0/benches/quat.rs010064400007650000024000000030421376272520100141550ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. #![feature(test)] #![allow(unused_macros)] extern crate cgmath; extern crate rand; extern crate test; use rand::{rngs::SmallRng, Rng, SeedableRng}; use std::ops::*; use test::Bencher; use cgmath::*; #[path = "common/macros.rs"] #[macro_use] mod macros; bench_binop!(_bench_quat_add_q, Quaternion, Quaternion, add); bench_binop!(_bench_quat_sub_q, Quaternion, Quaternion, sub); bench_binop!(_bench_quat_mul_q, Quaternion, Quaternion, mul); bench_binop!(_bench_quat_mul_v, Quaternion, Vector3, mul); bench_binop!(_bench_quat_mul_s, Quaternion, f32, mul); bench_binop!(_bench_quat_div_s, Quaternion, f32, div); bench_unop!(_bench_quat_invert, Quaternion, invert); bench_unop!(_bench_quat_conjugate, Quaternion, conjugate); bench_unop!(_bench_quat_normalize, Quaternion, normalize); cgmath-0.18.0/benches/vec.rs010064400007650000024000000050451376272520100137650ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. #![feature(test)] #![allow(unused_macros)] extern crate cgmath; extern crate rand; extern crate test; use rand::{rngs::SmallRng, Rng, SeedableRng}; use std::ops::*; use test::Bencher; use cgmath::*; #[path = "common/macros.rs"] #[macro_use] mod macros; bench_binop!(_bench_vector2_add_v, Vector2, Vector2, add); bench_binop!(_bench_vector3_add_v, Vector3, Vector3, add); bench_binop!(_bench_vector4_add_v, Vector4, Vector4, add); bench_binop!(_bench_vector2_sub_v, Vector2, Vector2, sub); bench_binop!(_bench_vector3_sub_v, Vector3, Vector3, sub); bench_binop!(_bench_vector4_sub_v, Vector4, Vector4, sub); bench_binop!(_bench_vector2_mul_s, Vector2, f32, mul); bench_binop!(_bench_vector3_mul_s, Vector3, f32, mul); bench_binop!(_bench_vector4_mul_s, Vector4, f32, mul); bench_binop!(_bench_vector2_div_s, Vector2, f32, div); bench_binop!(_bench_vector3_div_s, Vector3, f32, div); bench_binop!(_bench_vector4_div_s, Vector4, f32, div); bench_binop!(_bench_vector2_rem_s, Vector2, f32, rem); bench_binop!(_bench_vector3_rem_s, Vector3, f32, rem); bench_binop!(_bench_vector4_rem_s, Vector4, f32, rem); bench_binop!(_bench_vector2_dot, Vector2, Vector2, dot); bench_binop!(_bench_vector3_dot, Vector3, Vector3, dot); bench_binop!(_bench_vector4_dot, Vector4, Vector4, dot); bench_binop!(_bench_vector3_cross, Vector3, Vector3, cross); bench_unop!(_bench_vector2_magnitude, Vector2, magnitude); bench_unop!(_bench_vector3_magnitude, Vector3, magnitude); bench_unop!(_bench_vector4_magnitude, Vector4, magnitude); bench_unop!(_bench_vector2_normalize, Vector2, normalize); bench_unop!(_bench_vector3_normalize, Vector3, normalize); bench_unop!(_bench_vector4_normalize, Vector4, normalize); cgmath-0.18.0/build.rs010064400007650000024000000073521376272520100127030ustar 00000000000000use std::env; use std::fs::File; use std::io::Write; use std::path::Path; use std::string::String; /// Generate the name of the swizzle function and what it returns. /// NOTE: This function assumes that variables are in ASCII format #[cfg(feature = "swizzle")] fn gen_swizzle_nth<'a>(variables: &'a str, mut i: usize, upto: usize) -> Option<(String, String)> { debug_assert!(i > 0); // zeroth permutation is empty let mut swizzle_impl = String::new(); let mut swizzle = String::new(); let n = variables.len() + 1; for _ in 0..upto { if i == 0 { break; } if i % n == 0 { return None; } let c = variables.as_bytes()[i % n - 1] as char; swizzle.push(c); swizzle_impl.push_str(&format!("self.{}, ", c)); i = i / n; } Some((swizzle, swizzle_impl)) } /// A function that generates swizzle functions as a string. /// `variables`: swizzle variables (e.g. "xyz") /// `upto`: largest output vector size (e.g. for `variables = "xy"` and `upto = 4`, `xyxy()` is a /// valid swizzle operator. /// NOTE: This function assumes that variables are in ASCII format #[cfg(feature = "swizzle")] fn gen_swizzle_functions(variables: &'static str, upto: usize) -> String { let mut result = String::new(); let nn = (variables.len() + 1).pow(upto as u32); for i in 1..nn { if let Some((swizzle_name, swizzle_impl)) = gen_swizzle_nth(variables, i, upto) { let dim = format!("{}", swizzle_name.len()); result.push_str(&format!( " /// Swizzle operator that creates a new type with dimension {2} from variables `{0}`. #[inline] pub fn {0}(&self) -> $vector_type{2}<$S> {{ $vector_type{2}::new({1}) }}\n", swizzle_name, swizzle_impl, dim )); } } result } #[cfg(not(feature = "swizzle"))] fn gen_swizzle_functions(_: &'static str, _: usize) -> String { String::new() } /// This script generates the macro for building swizzle operators for multidimensional /// vectors and points. This macro is included in macros.rs fn main() { // save the file to output directory let out_dir = env::var("OUT_DIR").unwrap(); let swizzle_file_path = Path::new(&out_dir).join("swizzle_operator_macro.rs"); // This is the string representing the generated macro let data = format!( "/// Generate glm/glsl style swizzle operators macro_rules! impl_swizzle_functions {{ ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, x) => {{ {x3} }}; ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, xy) => {{ {xy3} }}; ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, xyz) => {{ {xyz3} }}; ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, x) => {{ {x4} }}; ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xy) => {{ {xy4} }}; ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xyz) => {{ {xyz4} }}; ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xyzw) => {{ {xyzw4} }}; }}", x3 = gen_swizzle_functions("x", 3), xy3 = gen_swizzle_functions("xy", 3), xyz3 = gen_swizzle_functions("xyz", 3), x4 = gen_swizzle_functions("x", 4), xy4 = gen_swizzle_functions("xy", 4), xyz4 = gen_swizzle_functions("xyz", 4), xyzw4 = gen_swizzle_functions("xyzw", 4)); let mut f = File::create(swizzle_file_path) .expect("Unable to create file that defines the swizzle operator macro."); f.write_all(data.as_bytes()) .expect("Unable to write swizzle operator macro."); } cgmath-0.18.0/src/angle.rs010064400007650000024000000170461377421362200134640ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. //! Angle units for type-safe, self-documenting code. use std::f64; use std::fmt; use std::iter; use std::ops::*; use num_traits::{cast, Bounded}; #[cfg(feature = "rand")] use rand::{ distributions::{uniform::SampleUniform, Distribution, Standard}, Rng, }; use structure::*; use approx; use num::BaseFloat; /// An angle, in radians. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Rad(pub S); /// An angle, in degrees. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Deg(pub S); impl From> for Deg where S: BaseFloat, { #[inline] fn from(rad: Rad) -> Deg { Deg(rad.0 * cast(180.0 / f64::consts::PI).unwrap()) } } impl From> for Rad where S: BaseFloat, { #[inline] fn from(deg: Deg) -> Rad { Rad(deg.0 * cast(f64::consts::PI / 180.0).unwrap()) } } macro_rules! impl_angle { ($Angle:ident, $fmt:expr, $full_turn:expr, $hi:expr) => { impl Zero for $Angle { #[inline] fn zero() -> $Angle { $Angle(S::zero()) } #[inline] fn is_zero(&self) -> bool { ulps_eq!(self, &Self::zero()) } } impl iter::Sum<$Angle> for $Angle { #[inline] fn sum>>(iter: I) -> $Angle { iter.fold($Angle::zero(), Add::add) } } impl<'a, S: 'a + BaseFloat> iter::Sum<&'a $Angle> for $Angle { #[inline] fn sum>>(iter: I) -> $Angle { iter.fold($Angle::zero(), Add::add) } } impl Angle for $Angle { type Unitless = S; #[inline] fn full_turn() -> $Angle { $Angle(cast($full_turn).unwrap()) } #[inline] fn sin(self) -> S { Rad::from(self).0.sin() } #[inline] fn cos(self) -> S { Rad::from(self).0.cos() } #[inline] fn tan(self) -> S { Rad::from(self).0.tan() } #[inline] fn sin_cos(self) -> (S, S) { Rad::from(self).0.sin_cos() } #[inline] fn asin(a: S) -> $Angle { Rad(a.asin()).into() } #[inline] fn acos(a: S) -> $Angle { Rad(a.acos()).into() } #[inline] fn atan(a: S) -> $Angle { Rad(a.atan()).into() } #[inline] fn atan2(a: S, b: S) -> $Angle { Rad(a.atan2(b)).into() } } impl Neg for $Angle { type Output = $Angle; #[inline] fn neg(self) -> $Angle { $Angle(-self.0) } } impl<'a, S: BaseFloat> Neg for &'a $Angle { type Output = $Angle; #[inline] fn neg(self) -> $Angle { $Angle(-self.0) } } impl Bounded for $Angle { #[inline] fn min_value() -> $Angle { $Angle(S::min_value()) } #[inline] fn max_value() -> $Angle { $Angle(S::max_value()) } } impl_operator!( Add<$Angle > for $Angle { fn add(lhs, rhs) -> $Angle { $Angle(lhs.0 + rhs.0) } }); impl_operator!( Sub<$Angle > for $Angle { fn sub(lhs, rhs) -> $Angle { $Angle(lhs.0 - rhs.0) } }); impl_operator!( Div<$Angle > for $Angle { fn div(lhs, rhs) -> S { lhs.0 / rhs.0 } }); impl_operator!( Rem<$Angle > for $Angle { fn rem(lhs, rhs) -> $Angle { $Angle(lhs.0 % rhs.0) } }); impl_assignment_operator!( AddAssign<$Angle > for $Angle { fn add_assign(&mut self, other) { self.0 += other.0; } }); impl_assignment_operator!( SubAssign<$Angle > for $Angle { fn sub_assign(&mut self, other) { self.0 -= other.0; } }); impl_assignment_operator!( RemAssign<$Angle > for $Angle { fn rem_assign(&mut self, other) { self.0 %= other.0; } }); impl_operator!( Mul for $Angle { fn mul(lhs, scalar) -> $Angle { $Angle(lhs.0 * scalar) } }); impl_operator!( Div for $Angle { fn div(lhs, scalar) -> $Angle { $Angle(lhs.0 / scalar) } }); impl_assignment_operator!( MulAssign for $Angle { fn mul_assign(&mut self, scalar) { self.0 *= scalar; } }); impl_assignment_operator!( DivAssign for $Angle { fn div_assign(&mut self, scalar) { self.0 /= scalar; } }); impl approx::AbsDiffEq for $Angle { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { S::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { S::abs_diff_eq(&self.0, &other.0, epsilon) } } impl approx::RelativeEq for $Angle { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { S::relative_eq(&self.0, &other.0, epsilon, max_relative) } } impl approx::UlpsEq for $Angle { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { S::ulps_eq(&self.0, &other.0, epsilon, max_ulps) } } #[cfg(feature = "rand")] impl Distribution<$Angle> for Standard where Standard: Distribution, S: BaseFloat + SampleUniform { #[inline] fn sample(&self, rng: &mut R) -> $Angle { $Angle(rng.gen_range(cast::<_, S>(-$hi).unwrap() .. cast::<_, S>($hi).unwrap())) } } impl fmt::Debug for $Angle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, $fmt, self.0) } } } } impl_angle!(Rad, "{:?} rad", f64::consts::PI * 2.0, f64::consts::PI); impl_angle!(Deg, "{:?}°", 360, 180); cgmath-0.18.0/src/conv.rs010064400007650000024000000043321376272520100133330ustar 00000000000000//! Constrained conversion functions for assisting in situations where type //! inference is difficult. //! //! For example, when declaring `glium` uniforms, we need to convert to fixed //! length arrays. We can use the `Into` trait directly, but it is rather ugly! //! //! --- Doc-test disabled because glium causes problems with nightly-2019-01-01 needed for "simd" //! ` ` `rust //! #[macro_use] //! extern crate glium; //! extern crate cgmath; //! //! use cgmath::{Matrix4, Point2}; //! use cgmath::prelude::*; //! //! # fn main() { //! let point = Point2::new(1, 2); //! let matrix = Matrix4::from_scale(2.0); //! //! let uniforms = uniform! { //! point: Into::<[_; 2]>::into(point), //! matrix: Into::<[[_; 4]; 4]>::into(matrix), //! // Yuck!! (ノಥ益ಥ)ノ ┻━┻ //! }; //! # } //! ` ` ` //! //! Instead, we can use the conversion functions from the `conv` module: //! //! --- Doc-test disabled because glium causes problems nightly-2019-01-01 needed for "simd" //! ` ` `rust //! #[macro_use] //! extern crate glium; //! extern crate cgmath; //! //! use cgmath::{Matrix4, Point2}; //! use cgmath::prelude::*; //! use cgmath::conv::*; //! //! # fn main() { //! let point = Point2::new(1, 2); //! let matrix = Matrix4::from_scale(2.0); //! //! let uniforms = uniform! { //! point: array2(point), //! matrix: array4x4(matrix), //! // ┬─┬ノ( º _ ºノ) //! }; //! # } //! ` ` ` /// Force a conversion into a 2-element array. #[inline] pub fn array2>(value: A) -> [T; 2] { value.into() } /// Force a conversion into a 3-element array. #[inline] pub fn array3>(value: A) -> [T; 3] { value.into() } /// Force a conversion into a 4-element array. #[inline] pub fn array4>(value: A) -> [T; 4] { value.into() } /// Force a conversion into a 2x2-element array. #[inline] pub fn array2x2>(value: A) -> [[T; 2]; 2] { value.into() } /// Force a conversion into a 3x3-element array. #[inline] pub fn array3x3>(value: A) -> [[T; 3]; 3] { value.into() } /// Force a conversion into a 4x4-element array. #[inline] pub fn array4x4>(value: A) -> [[T; 4]; 4] { value.into() } cgmath-0.18.0/src/euler.rs010064400007650000024000000162771376272520100135150ustar 00000000000000// Copyright 2016 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use num_traits::cast; #[cfg(feature = "rand")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use structure::*; use angle::Rad; use approx; #[cfg(feature = "mint")] use mint; use num::BaseFloat; use quaternion::Quaternion; /// A set of [Euler angles] representing a rotation in three-dimensional space. /// /// This type is marked as `#[repr(C)]`. /// /// The axis rotation sequence is XYZ. That is, the rotation is first around /// the X axis, then the Y axis, and lastly the Z axis (using intrinsic /// rotations). Since all three rotation axes are used, the angles are /// Tait–Bryan angles rather than proper Euler angles. /// /// # Ranges /// /// - x: [-pi, pi] /// - y: [-pi/2, pi/2] /// - z: [-pi, pi] /// /// # Defining rotations using Euler angles /// /// Note that while [Euler angles] are intuitive to define, they are prone to /// [gimbal lock] and are challenging to interpolate between. Instead we /// recommend that you convert them to a more robust representation, such as a /// quaternion or a rotation matrix. To this end, `From>` conversions /// are provided for the following types: /// /// - [`Basis3`](struct.Basis3.html) /// - [`Matrix3`](struct.Matrix3.html) /// - [`Matrix4`](struct.Matrix4.html) /// - [`Quaternion`](struct.Quaternion.html) /// /// For example, to define a quaternion that applies the following: /// /// 1. a 90° rotation around the _x_ axis /// 2. a 45° rotation around the _y_ axis /// 3. a 15° rotation around the _z_ axis /// /// you can use the following code: /// /// ``` /// use cgmath::{Deg, Euler, Quaternion}; /// /// let rotation = Quaternion::from(Euler { /// x: Deg(90.0), /// y: Deg(45.0), /// z: Deg(15.0), /// }); /// ``` /// /// [Euler angles]: https://en.wikipedia.org/wiki/Euler_angles /// [gimbal lock]: https://en.wikipedia.org/wiki/Gimbal_lock#Gimbal_lock_in_applied_mathematics /// [convert]: #defining-rotations-using-euler-angles #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Euler { /// The angle to apply around the _x_ axis. Also known at the _pitch_. pub x: A, /// The angle to apply around the _y_ axis. Also known at the _yaw_. pub y: A, /// The angle to apply around the _z_ axis. Also known at the _roll_. pub z: A, } impl Euler { /// Construct a set of euler angles. /// /// # Arguments /// /// * `x` - The angle to apply around the _x_ axis. Also known at the _pitch_. /// * `y` - The angle to apply around the _y_ axis. Also known at the _yaw_. /// * `z` - The angle to apply around the _z_ axis. Also known at the _roll_. pub const fn new(x: A, y: A, z: A) -> Euler { Euler { x: x, y: y, z: z } } } impl From> for Euler> { fn from(src: Quaternion) -> Euler> { let sig: S = cast(0.499).unwrap(); let two: S = cast(2).unwrap(); let one: S = cast(1).unwrap(); let (qw, qx, qy, qz) = (src.s, src.v.x, src.v.y, src.v.z); let (sqw, sqx, sqy, sqz) = (qw * qw, qx * qx, qy * qy, qz * qz); let unit = sqx + sqz + sqy + sqw; let test = qx * qz + qy * qw; // We set x to zero and z to the value, but the other way would work too. if test > sig * unit { // x + z = 2 * atan(x / w) Euler { x: Rad::zero(), y: Rad::turn_div_4(), z: Rad::atan2(qx, qw) * two, } } else if test < -sig * unit { // x - z = 2 * atan(x / w) Euler { x: Rad::zero(), y: -Rad::turn_div_4(), z: -Rad::atan2(qx, qw) * two, } } else { // Using the quat-to-matrix equation from either // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm // or equation 15 on page 7 of // http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf // to fill in the equations on page A-2 of the NASA document gives the below. Euler { x: Rad::atan2(two * (-qy * qz + qx * qw), one - two * (sqx + sqy)), y: Rad::asin(two * (qx * qz + qy * qw)), z: Rad::atan2(two * (-qx * qy + qz * qw), one - two * (sqy + sqz)), } } } } impl approx::AbsDiffEq for Euler { type Epsilon = A::Epsilon; #[inline] fn default_epsilon() -> A::Epsilon { A::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: A::Epsilon) -> bool { A::abs_diff_eq(&self.x, &other.x, epsilon) && A::abs_diff_eq(&self.y, &other.y, epsilon) && A::abs_diff_eq(&self.z, &other.z, epsilon) } } impl approx::RelativeEq for Euler { #[inline] fn default_max_relative() -> A::Epsilon { A::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool { A::relative_eq(&self.x, &other.x, epsilon, max_relative) && A::relative_eq(&self.y, &other.y, epsilon, max_relative) && A::relative_eq(&self.z, &other.z, epsilon, max_relative) } } impl approx::UlpsEq for Euler { #[inline] fn default_max_ulps() -> u32 { A::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: A::Epsilon, max_ulps: u32) -> bool { A::ulps_eq(&self.x, &other.x, epsilon, max_ulps) && A::ulps_eq(&self.y, &other.y, epsilon, max_ulps) && A::ulps_eq(&self.z, &other.z, epsilon, max_ulps) } } #[cfg(feature = "rand")] impl Distribution> for Standard where Standard: Distribution, A: Angle, { fn sample(&self, rng: &mut R) -> Euler { Euler { x: rng.gen(), y: rng.gen(), z: rng.gen(), } } } #[cfg(feature = "mint")] type MintEuler = mint::EulerAngles; #[cfg(feature = "mint")] impl> From> for Euler { fn from(mint: MintEuler) -> Self { Euler { x: mint.a.into(), y: mint.b.into(), z: mint.c.into(), } } } #[cfg(feature = "mint")] impl> Into> for Euler { fn into(self) -> MintEuler { MintEuler::from([self.x.into(), self.y.into(), self.z.into()]) } } cgmath-0.18.0/src/lib.rs010064400007650000024000000063251376272520100131400ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. //! A low-dimensional linear algebra library, targeted at computer graphics. //! //! # Trait overview //! //! In order to make a clean, composable API, we divide operations into traits //! that are roughly based on mathematical properties. The main ones that we //! concern ourselves with are listed below: //! //! - `VectorSpace`: Specifies the main operators for vectors, quaternions, and //! matrices. //! - `MetricSpace`: For types that have a distance function implemented. //! - `InnerSpace`: For types that have a dot (or inner) product - ie. vectors or //! quaternions. This also allows for the definition of operations that are //! based on the dot product, like finding the magnitude or normalizing. //! - `EuclideanSpace`: Points in euclidean space, with an associated space of //! displacement vectors. //! - `Matrix`: Common operations for matrices of arbitrary dimensions. //! - `SquareMatrix`: A special trait for matrices where the number of columns //! equal the number of rows. //! //! Other traits are included for practical convenience, for example: //! //! - `Array`: For contiguous, indexable arrays of elements, specifically //! vectors. //! - `ElementWise`: For element-wise addition, subtraction, multiplication, //! division, and remainder operations. //! //! # The prelude //! //! Importing each trait individually can become a chore, so we provide a //! `prelude` module to allow you to import the main traits all at once. For //! example: //! //! ```rust //! use cgmath::prelude::*; //! ``` #![cfg_attr(feature = "simd", feature(specialization))] #[macro_use] extern crate approx; #[cfg(feature = "mint")] pub extern crate mint; pub extern crate num_traits; #[cfg(feature = "rand")] extern crate rand; #[cfg(feature = "serde")] #[macro_use] extern crate serde; #[cfg(feature = "simd")] extern crate simd; // Re-exports pub use approx::*; pub use num::*; pub use structure::*; pub use matrix::{Matrix2, Matrix3, Matrix4}; pub use quaternion::Quaternion; pub use vector::{dot, vec1, vec2, vec3, vec4, Vector1, Vector2, Vector3, Vector4}; pub use angle::{Deg, Rad}; pub use euler::Euler; pub use point::{point1, point2, point3, Point1, Point2, Point3}; pub use rotation::*; pub use transform::*; pub use projection::*; // Modules pub mod conv; pub mod prelude; mod macros; mod num; mod structure; mod matrix; mod quaternion; #[cfg(feature = "simd")] mod quaternion_simd; mod vector; #[cfg(feature = "simd")] mod vector_simd; mod angle; mod euler; mod point; mod rotation; mod transform; mod projection; cgmath-0.18.0/src/macros.rs010064400007650000024000000310141376272520100136470ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. //! Utility macros for code generation #![macro_use] #[cfg(feature = "simd")] macro_rules! default_fn { { $($tt:tt)* } => { default fn $( $tt )* }; } #[cfg(not(feature = "simd"))] macro_rules! default_fn { { $($tt:tt)* } => { fn $( $tt )* }; } /// Generates a binary operator implementation for the permutations of by-ref and by-val macro_rules! impl_operator { // When it is an unary operator (<$S:ident: $Constraint:ident> $Op:ident for $Lhs:ty { fn $op:ident($x:ident) -> $Output:ty { $body:expr } }) => { impl<$S: $Constraint> $Op for $Lhs { type Output = $Output; #[inline] default_fn!($op(self) -> $Output { let $x = self; $body }); } impl<'a, $S: $Constraint> $Op for &'a $Lhs { type Output = $Output; #[inline] default_fn!($op(self) -> $Output { let $x = self; $body }); } }; // When the right operand is a scalar (<$S:ident: $Constraint:ident> $Op:ident<$Rhs:ident> for $Lhs:ty { fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } }) => { impl<$S: $Constraint> $Op<$Rhs> for $Lhs { type Output = $Output; #[inline] default_fn!($op(self, other: $Rhs) -> $Output { let ($lhs, $rhs) = (self, other); $body }); } impl<'a, $S: $Constraint> $Op<$Rhs> for &'a $Lhs { type Output = $Output; #[inline] default_fn!($op(self, other: $Rhs) -> $Output { let ($lhs, $rhs) = (self, other); $body }); } }; // When the right operand is a compound type (<$S:ident: $Constraint:ident> $Op:ident<$Rhs:ty> for $Lhs:ty { fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } }) => { impl<$S: $Constraint> $Op<$Rhs> for $Lhs { type Output = $Output; #[inline] default_fn!( $op(self, other: $Rhs) -> $Output { let ($lhs, $rhs) = (self, other); $body }); } impl<'a, $S: $Constraint> $Op<&'a $Rhs> for $Lhs { type Output = $Output; #[inline] default_fn!( $op(self, other: &'a $Rhs) -> $Output { let ($lhs, $rhs) = (self, other); $body }); } impl<'a, $S: $Constraint> $Op<$Rhs> for &'a $Lhs { type Output = $Output; #[inline] default_fn!( $op(self, other: $Rhs) -> $Output { let ($lhs, $rhs) = (self, other); $body }); } impl<'a, 'b, $S: $Constraint> $Op<&'a $Rhs> for &'b $Lhs { type Output = $Output; #[inline] default_fn!( $op(self, other: &'a $Rhs) -> $Output { let ($lhs, $rhs) = (self, other); $body }); } }; // When the left operand is a scalar ($Op:ident<$Rhs:ident<$S:ident>> for $Lhs:ty { fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } }) => { impl $Op<$Rhs<$S>> for $Lhs { type Output = $Output; #[inline] default_fn!( $op(self, other: $Rhs<$S>) -> $Output { let ($lhs, $rhs) = (self, other); $body }); } impl<'a> $Op<&'a $Rhs<$S>> for $Lhs { type Output = $Output; #[inline] default_fn!( $op(self, other: &'a $Rhs<$S>) -> $Output { let ($lhs, $rhs) = (self, other); $body }); } }; } macro_rules! impl_assignment_operator { (<$S:ident: $Constraint:ident> $Op:ident<$Rhs:ty> for $Lhs:ty { fn $op:ident(&mut $lhs:ident, $rhs:ident) $body:block }) => { impl<$S: $Constraint + $Op<$S>> $Op<$Rhs> for $Lhs { #[inline] default_fn!( $op(&mut $lhs, $rhs: $Rhs) $body ); } }; } macro_rules! fold_array { (&$method:ident, { $x:expr }) => { *$x }; (&$method:ident, { $x:expr, $y:expr }) => { $x.$method(&$y) }; (&$method:ident, { $x:expr, $y:expr, $z:expr }) => { $x.$method(&$y).$method(&$z) }; (&$method:ident, { $x:expr, $y:expr, $z:expr, $w:expr }) => { $x.$method(&$y).$method(&$z).$method(&$w) }; ($method:ident, { $x:expr }) => { $x }; ($method:ident, { $x:expr, $y:expr }) => { $x.$method($y) }; ($method:ident, { $x:expr, $y:expr, $z:expr }) => { $x.$method($y).$method($z) }; ($method:ident, { $x:expr, $y:expr, $z:expr, $w:expr }) => { $x.$method($y).$method($z).$method($w) }; } /// Generate array conversion implementations for a compound array type macro_rules! impl_fixed_array_conversions { ($ArrayN:ident <$S:ident> { $($field:ident : $index:expr),+ }, $n:expr) => { impl<$S> Into<[$S; $n]> for $ArrayN<$S> { #[inline] fn into(self) -> [$S; $n] { match self { $ArrayN { $($field),+ } => [$($field),+] } } } impl<$S> AsRef<[$S; $n]> for $ArrayN<$S> { #[inline] fn as_ref(&self) -> &[$S; $n] { unsafe { mem::transmute(self) } } } impl<$S> AsMut<[$S; $n]> for $ArrayN<$S> { #[inline] fn as_mut(&mut self) -> &mut [$S; $n] { unsafe { mem::transmute(self) } } } impl<$S: Clone> From<[$S; $n]> for $ArrayN<$S> { #[inline] fn from(v: [$S; $n]) -> $ArrayN<$S> { // We need to use a clone here because we can't pattern match on arrays yet $ArrayN { $($field: v[$index].clone()),+ } } } impl<'a, $S> From<&'a [$S; $n]> for &'a $ArrayN<$S> { #[inline] fn from(v: &'a [$S; $n]) -> &'a $ArrayN<$S> { unsafe { mem::transmute(v) } } } impl<'a, $S> From<&'a mut [$S; $n]> for &'a mut $ArrayN<$S> { #[inline] fn from(v: &'a mut [$S; $n]) -> &'a mut $ArrayN<$S> { unsafe { mem::transmute(v) } } } } } /// Generate homogeneous tuple conversion implementations for a compound array type macro_rules! impl_tuple_conversions { ($ArrayN:ident <$S:ident> { $($field:ident),+ }, $Tuple:ty) => { impl<$S> Into<$Tuple> for $ArrayN<$S> { #[inline] fn into(self) -> $Tuple { match self { $ArrayN { $($field),+ } => ($($field),+,) } } } impl<$S> AsRef<$Tuple> for $ArrayN<$S> { #[inline] fn as_ref(&self) -> &$Tuple { unsafe { mem::transmute(self) } } } impl<$S> AsMut<$Tuple> for $ArrayN<$S> { #[inline] fn as_mut(&mut self) -> &mut $Tuple { unsafe { mem::transmute(self) } } } impl<$S> From<$Tuple> for $ArrayN<$S> { #[inline] fn from(v: $Tuple) -> $ArrayN<$S> { match v { ($($field),+,) => $ArrayN { $($field: $field),+ } } } } impl<'a, $S> From<&'a $Tuple> for &'a $ArrayN<$S> { #[inline] fn from(v: &'a $Tuple) -> &'a $ArrayN<$S> { unsafe { mem::transmute(v) } } } impl<'a, $S> From<&'a mut $Tuple> for &'a mut $ArrayN<$S> { #[inline] fn from(v: &'a mut $Tuple) -> &'a mut $ArrayN<$S> { unsafe { mem::transmute(v) } } } } } /// Generates index operators for a compound type macro_rules! impl_index_operators { ($VectorN:ident<$S:ident>, $n:expr, $Output:ty, $I:ty) => { impl<$S> Index<$I> for $VectorN<$S> { type Output = $Output; #[inline] fn index<'a>(&'a self, i: $I) -> &'a $Output { let v: &[$S; $n] = self.as_ref(); &v[i] } } impl<$S> IndexMut<$I> for $VectorN<$S> { #[inline] fn index_mut<'a>(&'a mut self, i: $I) -> &'a mut $Output { let v: &mut [$S; $n] = self.as_mut(); &mut v[i] } } }; } /// Generates a binary operator implementation for the permutations of by-ref and by-val, for simd #[cfg(feature = "simd")] macro_rules! impl_operator_simd { // When it is an unary operator ([$Simd:ident]; $Op:ident for $Lhs:ty { fn $op:ident($x:ident) -> $Output:ty { $body:expr } }) => { impl $Op for $Lhs { #[inline] fn $op(self) -> $Output { let $x: $Simd = self.into(); $body } } }; // When the right operand is a scalar (@rs [$Simd:ident]; $Op:ident<$Rhs:ty> for $Lhs:ty { fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } }) => { impl $Op<$Rhs> for $Lhs { #[inline] fn $op(self, other: $Rhs) -> $Output { let ($lhs, $rhs): ($Simd, $Simd) = (self.into(), $Simd::splat(other)); $body } } impl<'a> $Op<$Rhs> for &'a $Lhs { #[inline] fn $op(self, other: $Rhs) -> $Output { let ($lhs, $rhs): ($Simd, $Simd) = ((*self).into(), $Simd::splat(other)); $body } } }; // When the right operand is a compound type ([$Simd:ident]; $Op:ident<$Rhs:ty> for $Lhs:ty { fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } }) => { impl $Op<$Rhs> for $Lhs { #[inline] fn $op(self, other: $Rhs) -> $Output { let ($lhs, $rhs): ($Simd, $Simd) = (self.into(), other.into()); $body } } impl<'a> $Op<&'a $Rhs> for $Lhs { #[inline] fn $op(self, other: &'a $Rhs) -> $Output { let ($lhs, $rhs): ($Simd, $Simd) = (self.into(), (*other).into()); $body } } impl<'a> $Op<$Rhs> for &'a $Lhs { #[inline] fn $op(self, other: $Rhs) -> $Output { let ($lhs, $rhs): ($Simd, $Simd) = ((*self).into(), other.into()); $body } } impl<'a, 'b> $Op<&'a $Rhs> for &'b $Lhs { #[inline] fn $op(self, other: &'a $Rhs) -> $Output { let ($lhs, $rhs): ($Simd, $Simd) = ((*self).into(), (*other).into()); $body } } }; // When the left operand is a scalar (@ls [$Simd:ident]; $Op:ident<$Rhs:ty> for $Lhs:ident { fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } }) => { impl $Op<$Rhs> for $Lhs { #[inline] fn $op(self, other: $Rhs) -> $Output { let ($lhs, $rhs): ($Simd, $Simd) = ($Simd::splat(self), other.into()); $body } } impl<'a> $Op<&'a $Rhs> for $Lhs { #[inline] fn $op(self, other: &'a $Rhs) -> $Output { let ($lhs, $rhs): ($Simd, $Simd) = ($Simd::splat(self), (*other).into()); $body } } }; } /// Generate `mint` types conversion implementations #[cfg(feature = "mint")] macro_rules! impl_mint_conversions { ($ArrayN:ident { $($field:ident),+ }, $Mint:ident) => { impl Into> for $ArrayN { #[inline] fn into(self) -> mint::$Mint { mint::$Mint::from([$(self.$field),+]) } } impl From> for $ArrayN { #[inline] fn from(v: mint::$Mint) -> Self { $ArrayN { $( $field: v.$field, )+ } } } } } include!(concat!(env!("OUT_DIR"), "/swizzle_operator_macro.rs")); cgmath-0.18.0/src/matrix.rs010064400007650000024000001630611377421362200137010ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use num_traits::{cast, NumCast}; #[cfg(feature = "rand")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use std::fmt; use std::iter; use std::mem; use std::ops::*; use std::ptr; use structure::*; use angle::Rad; use approx; use euler::Euler; use num::BaseFloat; use point::{Point2, Point3}; use quaternion::Quaternion; use transform::{Transform, Transform2, Transform3}; use vector::{Vector2, Vector3, Vector4}; #[cfg(feature = "mint")] use mint; /// A 2 x 2, column major matrix /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Matrix2 { /// The first column of the matrix. pub x: Vector2, /// The second column of the matrix. pub y: Vector2, } /// A 3 x 3, column major matrix /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Matrix3 { /// The first column of the matrix. pub x: Vector3, /// The second column of the matrix. pub y: Vector3, /// The third column of the matrix. pub z: Vector3, } /// A 4 x 4, column major matrix /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Matrix4 { /// The first column of the matrix. pub x: Vector4, /// The second column of the matrix. pub y: Vector4, /// The third column of the matrix. pub z: Vector4, /// The fourth column of the matrix. pub w: Vector4, } impl Matrix2 { /// Create a new matrix, providing values for each index. #[inline] pub const fn new(c0r0: S, c0r1: S, c1r0: S, c1r1: S) -> Matrix2 { Matrix2::from_cols(Vector2::new(c0r0, c0r1), Vector2::new(c1r0, c1r1)) } /// Create a new matrix, providing columns. #[inline] pub const fn from_cols(c0: Vector2, c1: Vector2) -> Matrix2 { Matrix2 { x: c0, y: c1 } } } impl Matrix2 { /// Create a transformation matrix that will cause `unit_x()` to point at /// `dir`. `unit_y()` will be perpendicular to `dir`, and the closest to `up`. pub fn look_at(dir: Vector2, up: Vector2) -> Matrix2 { Matrix2::look_at_stable(dir, up.x * dir.y >= up.y * dir.x) } /// Crate a transformation that will cause `unit_x()` to point at /// `dir`. This is similar to `look_at`, but does not take an `up` vector. /// This will not cause `unit_y()` to flip when `dir` crosses over the `up` vector. pub fn look_at_stable(dir: Vector2, flip: bool) -> Matrix2 { let basis1 = dir.normalize(); let basis2 = if flip { Vector2::new(basis1.y, -basis1.x) } else { Vector2::new(-basis1.y, basis1.x) }; Matrix2::from_cols(basis1, basis2) } #[inline] pub fn from_angle>>(theta: A) -> Matrix2 { let (s, c) = Rad::sin_cos(theta.into()); Matrix2::new(c, s, -s, c) } /// Are all entries in the matrix finite. pub fn is_finite(&self) -> bool { self.x.is_finite() && self.y.is_finite() } } impl Matrix3 { /// Create a new matrix, providing values for each index. #[inline] #[cfg_attr(rustfmt, rustfmt_skip)] pub const fn new( c0r0:S, c0r1:S, c0r2:S, c1r0:S, c1r1:S, c1r2:S, c2r0:S, c2r1:S, c2r2:S, ) -> Matrix3 { Matrix3::from_cols( Vector3::new(c0r0, c0r1, c0r2), Vector3::new(c1r0, c1r1, c1r2), Vector3::new(c2r0, c2r1, c2r2), ) } /// Create a new matrix, providing columns. #[inline] pub const fn from_cols(c0: Vector3, c1: Vector3, c2: Vector3) -> Matrix3 { Matrix3 { x: c0, y: c1, z: c2, } } } impl Matrix3 { /// Create a homogeneous transformation matrix from a translation vector. #[inline] pub fn from_translation(v: Vector2) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( S::one(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), v.x, v.y, S::one(), ) } /// Create a homogeneous transformation matrix from a scale value. #[inline] pub fn from_scale(value: S) -> Matrix3 { Matrix3::from_nonuniform_scale(value, value) } /// Create a homogeneous transformation matrix from a set of scale values. #[inline] pub fn from_nonuniform_scale(x: S, y: S) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( x, S::zero(), S::zero(), S::zero(), y, S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a rotation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. #[deprecated = "Use Matrix3::look_to_lh"] pub fn look_at(dir: Vector3, up: Vector3) -> Matrix3 { Matrix3::look_to_lh(dir, up) } /// Create a rotation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. pub fn look_to_lh(dir: Vector3, up: Vector3) -> Matrix3 { let dir = dir.normalize(); let side = up.cross(dir).normalize(); let up = dir.cross(side).normalize(); Matrix3::from_cols(side, up, dir).transpose() } /// Create a rotation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. pub fn look_to_rh(dir: Vector3, up: Vector3) -> Matrix3 { Matrix3::look_to_lh(-dir, up) } /// Create a rotation matrix from a rotation around the `x` axis (pitch). pub fn from_angle_x>>(theta: A) -> Matrix3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( S::one(), S::zero(), S::zero(), S::zero(), c, s, S::zero(), -s, c, ) } /// Create a rotation matrix from a rotation around the `y` axis (yaw). pub fn from_angle_y>>(theta: A) -> Matrix3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( c, S::zero(), -s, S::zero(), S::one(), S::zero(), s, S::zero(), c, ) } /// Create a rotation matrix from a rotation around the `z` axis (roll). pub fn from_angle_z>>(theta: A) -> Matrix3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( c, s, S::zero(), -s, c, S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a rotation matrix from an angle around an arbitrary axis. /// /// The specified axis **must be normalized**, or it represents an invalid rotation. pub fn from_axis_angle>>(axis: Vector3, angle: A) -> Matrix3 { let (s, c) = Rad::sin_cos(angle.into()); let _1subc = S::one() - c; #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( _1subc * axis.x * axis.x + c, _1subc * axis.x * axis.y + s * axis.z, _1subc * axis.x * axis.z - s * axis.y, _1subc * axis.x * axis.y - s * axis.z, _1subc * axis.y * axis.y + c, _1subc * axis.y * axis.z + s * axis.x, _1subc * axis.x * axis.z + s * axis.y, _1subc * axis.y * axis.z - s * axis.x, _1subc * axis.z * axis.z + c, ) } /// Are all entries in the matrix finite. pub fn is_finite(&self) -> bool { self.x.is_finite() && self.y.is_finite() && self.z.is_finite() } } impl Matrix4 { /// Create a new matrix, providing values for each index. #[inline] #[cfg_attr(rustfmt, rustfmt_skip)] pub const fn new( c0r0: S, c0r1: S, c0r2: S, c0r3: S, c1r0: S, c1r1: S, c1r2: S, c1r3: S, c2r0: S, c2r1: S, c2r2: S, c2r3: S, c3r0: S, c3r1: S, c3r2: S, c3r3: S, ) -> Matrix4 { Matrix4::from_cols( Vector4::new(c0r0, c0r1, c0r2, c0r3), Vector4::new(c1r0, c1r1, c1r2, c1r3), Vector4::new(c2r0, c2r1, c2r2, c2r3), Vector4::new(c3r0, c3r1, c3r2, c3r3), ) } /// Create a new matrix, providing columns. #[inline] pub const fn from_cols( c0: Vector4, c1: Vector4, c2: Vector4, c3: Vector4, ) -> Matrix4 { Matrix4 { x: c0, y: c1, z: c2, w: c3, } } } impl Matrix4 { /// Create a homogeneous transformation matrix from a translation vector. #[inline] pub fn from_translation(v: Vector3) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( S::one(), S::zero(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), v.x, v.y, v.z, S::one(), ) } /// Create a homogeneous transformation matrix from a scale value. #[inline] pub fn from_scale(value: S) -> Matrix4 { Matrix4::from_nonuniform_scale(value, value, value) } /// Create a homogeneous transformation matrix from a set of scale values. #[inline] pub fn from_nonuniform_scale(x: S, y: S, z: S) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( x, S::zero(), S::zero(), S::zero(), S::zero(), y, S::zero(), S::zero(), S::zero(), S::zero(), z, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a homogeneous transformation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. #[deprecated = "Use Matrix4::look_to_rh"] pub fn look_at_dir(eye: Point3, dir: Vector3, up: Vector3) -> Matrix4 { let f = dir.normalize(); let s = f.cross(up).normalize(); let u = s.cross(f); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( s.x.clone(), u.x.clone(), -f.x.clone(), S::zero(), s.y.clone(), u.y.clone(), -f.y.clone(), S::zero(), s.z.clone(), u.z.clone(), -f.z.clone(), S::zero(), -eye.dot(s), -eye.dot(u), eye.dot(f), S::one(), ) } /// Create a homogeneous transformation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. pub fn look_to_rh(eye: Point3, dir: Vector3, up: Vector3) -> Matrix4 { let f = dir.normalize(); let s = f.cross(up).normalize(); let u = s.cross(f); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( s.x.clone(), u.x.clone(), -f.x.clone(), S::zero(), s.y.clone(), u.y.clone(), -f.y.clone(), S::zero(), s.z.clone(), u.z.clone(), -f.z.clone(), S::zero(), -eye.dot(s), -eye.dot(u), eye.dot(f), S::one(), ) } /// Create a homogeneous transformation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. pub fn look_to_lh(eye: Point3, dir: Vector3, up: Vector3) -> Matrix4 { Matrix4::look_to_rh(eye, -dir, up) } /// Create a homogeneous transformation matrix that will cause a vector to point at /// `center`, using `up` for orientation. #[deprecated = "Use Matrix4::look_at_rh"] pub fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_at_rh(eye, center, up) } /// Create a homogeneous transformation matrix that will cause a vector to point at /// `center`, using `up` for orientation. pub fn look_at_rh(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_to_rh(eye, center - eye, up) } /// Create a homogeneous transformation matrix that will cause a vector to point at /// `center`, using `up` for orientation. pub fn look_at_lh(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_to_lh(eye, center - eye, up) } /// Create a homogeneous transformation matrix from a rotation around the `x` axis (pitch). pub fn from_angle_x>>(theta: A) -> Matrix4 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( S::one(), S::zero(), S::zero(), S::zero(), S::zero(), c, s, S::zero(), S::zero(), -s, c, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a homogeneous transformation matrix from a rotation around the `y` axis (yaw). pub fn from_angle_y>>(theta: A) -> Matrix4 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( c, S::zero(), -s, S::zero(), S::zero(), S::one(), S::zero(), S::zero(), s, S::zero(), c, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a homogeneous transformation matrix from a rotation around the `z` axis (roll). pub fn from_angle_z>>(theta: A) -> Matrix4 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( c, s, S::zero(), S::zero(), -s, c, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a homogeneous transformation matrix from an angle around an arbitrary axis. /// /// The specified axis **must be normalized**, or it represents an invalid rotation. pub fn from_axis_angle>>(axis: Vector3, angle: A) -> Matrix4 { let (s, c) = Rad::sin_cos(angle.into()); let _1subc = S::one() - c; #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( _1subc * axis.x * axis.x + c, _1subc * axis.x * axis.y + s * axis.z, _1subc * axis.x * axis.z - s * axis.y, S::zero(), _1subc * axis.x * axis.y - s * axis.z, _1subc * axis.y * axis.y + c, _1subc * axis.y * axis.z + s * axis.x, S::zero(), _1subc * axis.x * axis.z + s * axis.y, _1subc * axis.y * axis.z - s * axis.x, _1subc * axis.z * axis.z + c, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Are all entries in the matrix finite. pub fn is_finite(&self) -> bool { self.w.is_finite() && self.x.is_finite() && self.y.is_finite() && self.z.is_finite() } } impl Zero for Matrix2 { #[inline] fn zero() -> Matrix2 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix2::new( S::zero(), S::zero(), S::zero(), S::zero(), ) } #[inline] fn is_zero(&self) -> bool { ulps_eq!(self, &Self::zero()) } } impl Zero for Matrix3 { #[inline] fn zero() -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), ) } #[inline] fn is_zero(&self) -> bool { ulps_eq!(self, &Self::zero()) } } impl Zero for Matrix4 { #[inline] fn zero() -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), ) } #[inline] fn is_zero(&self) -> bool { ulps_eq!(self, &Self::zero()) } } impl One for Matrix2 { #[inline] fn one() -> Matrix2 { Matrix2::from_value(S::one()) } } impl One for Matrix3 { #[inline] fn one() -> Matrix3 { Matrix3::from_value(S::one()) } } impl One for Matrix4 { #[inline] fn one() -> Matrix4 { Matrix4::from_value(S::one()) } } impl VectorSpace for Matrix2 { type Scalar = S; } impl VectorSpace for Matrix3 { type Scalar = S; } impl VectorSpace for Matrix4 { type Scalar = S; } impl Matrix for Matrix2 { type Column = Vector2; type Row = Vector2; type Transpose = Matrix2; #[inline] fn row(&self, r: usize) -> Vector2 { Vector2::new(self[0][r], self[1][r]) } #[inline] fn swap_rows(&mut self, a: usize, b: usize) { self[0].swap_elements(a, b); self[1].swap_elements(a, b); } #[inline] fn swap_columns(&mut self, a: usize, b: usize) { unsafe { ptr::swap(&mut self[a], &mut self[b]) }; } #[inline] fn swap_elements(&mut self, a: (usize, usize), b: (usize, usize)) { let (ac, ar) = a; let (bc, br) = b; unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; } fn transpose(&self) -> Matrix2 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix2::new( self[0][0], self[1][0], self[0][1], self[1][1], ) } } impl SquareMatrix for Matrix2 { type ColumnRow = Vector2; #[inline] fn from_value(value: S) -> Matrix2 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix2::new( value, S::zero(), S::zero(), value, ) } #[inline] fn from_diagonal(value: Vector2) -> Matrix2 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix2::new( value.x, S::zero(), S::zero(), value.y, ) } #[inline] fn transpose_self(&mut self) { self.swap_elements((0, 1), (1, 0)); } #[inline] fn determinant(&self) -> S { self[0][0] * self[1][1] - self[1][0] * self[0][1] } #[inline] fn diagonal(&self) -> Vector2 { Vector2::new(self[0][0], self[1][1]) } #[inline] fn invert(&self) -> Option> { let det = self.determinant(); if det == S::zero() { None } else { #[cfg_attr(rustfmt, rustfmt_skip)] Some(Matrix2::new( self[1][1] / det, -self[0][1] / det, -self[1][0] / det, self[0][0] / det, )) } } #[inline] fn is_diagonal(&self) -> bool { ulps_eq!(self[0][1], &S::zero()) && ulps_eq!(self[1][0], &S::zero()) } #[inline] fn is_symmetric(&self) -> bool { ulps_eq!(self[0][1], &self[1][0]) && ulps_eq!(self[1][0], &self[0][1]) } } impl Matrix for Matrix3 { type Column = Vector3; type Row = Vector3; type Transpose = Matrix3; #[inline] fn row(&self, r: usize) -> Vector3 { Vector3::new(self[0][r], self[1][r], self[2][r]) } #[inline] fn swap_rows(&mut self, a: usize, b: usize) { self[0].swap_elements(a, b); self[1].swap_elements(a, b); self[2].swap_elements(a, b); } #[inline] fn swap_columns(&mut self, a: usize, b: usize) { unsafe { ptr::swap(&mut self[a], &mut self[b]) }; } #[inline] fn swap_elements(&mut self, a: (usize, usize), b: (usize, usize)) { let (ac, ar) = a; let (bc, br) = b; unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; } fn transpose(&self) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( self[0][0], self[1][0], self[2][0], self[0][1], self[1][1], self[2][1], self[0][2], self[1][2], self[2][2], ) } } impl SquareMatrix for Matrix3 { type ColumnRow = Vector3; #[inline] fn from_value(value: S) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( value, S::zero(), S::zero(), S::zero(), value, S::zero(), S::zero(), S::zero(), value, ) } #[inline] fn from_diagonal(value: Vector3) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( value.x, S::zero(), S::zero(), S::zero(), value.y, S::zero(), S::zero(), S::zero(), value.z, ) } #[inline] fn transpose_self(&mut self) { self.swap_elements((0, 1), (1, 0)); self.swap_elements((0, 2), (2, 0)); self.swap_elements((1, 2), (2, 1)); } fn determinant(&self) -> S { self[0][0] * (self[1][1] * self[2][2] - self[2][1] * self[1][2]) - self[1][0] * (self[0][1] * self[2][2] - self[2][1] * self[0][2]) + self[2][0] * (self[0][1] * self[1][2] - self[1][1] * self[0][2]) } #[inline] fn diagonal(&self) -> Vector3 { Vector3::new(self[0][0], self[1][1], self[2][2]) } fn invert(&self) -> Option> { let det = self.determinant(); if det == S::zero() { None } else { Some( Matrix3::from_cols( self[1].cross(self[2]) / det, self[2].cross(self[0]) / det, self[0].cross(self[1]) / det, ) .transpose(), ) } } fn is_diagonal(&self) -> bool { ulps_eq!(self[0][1], &S::zero()) && ulps_eq!(self[0][2], &S::zero()) && ulps_eq!(self[1][0], &S::zero()) && ulps_eq!(self[1][2], &S::zero()) && ulps_eq!(self[2][0], &S::zero()) && ulps_eq!(self[2][1], &S::zero()) } fn is_symmetric(&self) -> bool { ulps_eq!(self[0][1], &self[1][0]) && ulps_eq!(self[0][2], &self[2][0]) && ulps_eq!(self[1][0], &self[0][1]) && ulps_eq!(self[1][2], &self[2][1]) && ulps_eq!(self[2][0], &self[0][2]) && ulps_eq!(self[2][1], &self[1][2]) } } impl Matrix for Matrix4 { type Column = Vector4; type Row = Vector4; type Transpose = Matrix4; #[inline] fn row(&self, r: usize) -> Vector4 { Vector4::new(self[0][r], self[1][r], self[2][r], self[3][r]) } #[inline] fn swap_rows(&mut self, a: usize, b: usize) { self[0].swap_elements(a, b); self[1].swap_elements(a, b); self[2].swap_elements(a, b); self[3].swap_elements(a, b); } #[inline] fn swap_columns(&mut self, a: usize, b: usize) { unsafe { ptr::swap(&mut self[a], &mut self[b]) }; } #[inline] fn swap_elements(&mut self, a: (usize, usize), b: (usize, usize)) { let (ac, ar) = a; let (bc, br) = b; unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; } fn transpose(&self) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( self[0][0], self[1][0], self[2][0], self[3][0], self[0][1], self[1][1], self[2][1], self[3][1], self[0][2], self[1][2], self[2][2], self[3][2], self[0][3], self[1][3], self[2][3], self[3][3], ) } } impl SquareMatrix for Matrix4 { type ColumnRow = Vector4; #[inline] fn from_value(value: S) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( value, S::zero(), S::zero(), S::zero(), S::zero(), value, S::zero(), S::zero(), S::zero(), S::zero(), value, S::zero(), S::zero(), S::zero(), S::zero(), value, ) } #[inline] fn from_diagonal(value: Vector4) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( value.x, S::zero(), S::zero(), S::zero(), S::zero(), value.y, S::zero(), S::zero(), S::zero(), S::zero(), value.z, S::zero(), S::zero(), S::zero(), S::zero(), value.w, ) } fn transpose_self(&mut self) { self.swap_elements((0, 1), (1, 0)); self.swap_elements((0, 2), (2, 0)); self.swap_elements((0, 3), (3, 0)); self.swap_elements((1, 2), (2, 1)); self.swap_elements((1, 3), (3, 1)); self.swap_elements((2, 3), (3, 2)); } fn determinant(&self) -> S { let tmp = unsafe { det_sub_proc_unsafe(self, 1, 2, 3) }; tmp.dot(Vector4::new(self[0][0], self[1][0], self[2][0], self[3][0])) } #[inline] fn diagonal(&self) -> Vector4 { Vector4::new(self[0][0], self[1][1], self[2][2], self[3][3]) } // The new implementation results in negative optimization when used // without SIMD. so we opt them in with configuration. // A better option would be using specialization. But currently somewhat // specialization is too buggy, and it won't apply here. I'm getting // weird error msgs. Help wanted. #[cfg(not(feature = "simd"))] fn invert(&self) -> Option> { let det = self.determinant(); if det == S::zero() { None } else { let inv_det = S::one() / det; let t = self.transpose(); let cf = |i, j| { let mat = match i { 0 => { Matrix3::from_cols(t.y.truncate_n(j), t.z.truncate_n(j), t.w.truncate_n(j)) } 1 => { Matrix3::from_cols(t.x.truncate_n(j), t.z.truncate_n(j), t.w.truncate_n(j)) } 2 => { Matrix3::from_cols(t.x.truncate_n(j), t.y.truncate_n(j), t.w.truncate_n(j)) } 3 => { Matrix3::from_cols(t.x.truncate_n(j), t.y.truncate_n(j), t.z.truncate_n(j)) } _ => panic!("out of range"), }; let sign = if (i + j) & 1 == 1 { -S::one() } else { S::one() }; mat.determinant() * sign * inv_det }; #[cfg_attr(rustfmt, rustfmt_skip)] Some(Matrix4::new( cf(0, 0), cf(0, 1), cf(0, 2), cf(0, 3), cf(1, 0), cf(1, 1), cf(1, 2), cf(1, 3), cf(2, 0), cf(2, 1), cf(2, 2), cf(2, 3), cf(3, 0), cf(3, 1), cf(3, 2), cf(3, 3), )) } } #[cfg(feature = "simd")] fn invert(&self) -> Option> { let tmp0 = unsafe { det_sub_proc_unsafe(self, 1, 2, 3) }; let det = tmp0.dot(Vector4::new(self[0][0], self[1][0], self[2][0], self[3][0])); if det == S::zero() { None } else { let inv_det = S::one() / det; let tmp0 = tmp0 * inv_det; let tmp1 = unsafe { det_sub_proc_unsafe(self, 0, 3, 2) * inv_det }; let tmp2 = unsafe { det_sub_proc_unsafe(self, 0, 1, 3) * inv_det }; let tmp3 = unsafe { det_sub_proc_unsafe(self, 0, 2, 1) * inv_det }; Some(Matrix4::from_cols(tmp0, tmp1, tmp2, tmp3)) } } fn is_diagonal(&self) -> bool { ulps_eq!(self[0][1], &S::zero()) && ulps_eq!(self[0][2], &S::zero()) && ulps_eq!(self[0][3], &S::zero()) && ulps_eq!(self[1][0], &S::zero()) && ulps_eq!(self[1][2], &S::zero()) && ulps_eq!(self[1][3], &S::zero()) && ulps_eq!(self[2][0], &S::zero()) && ulps_eq!(self[2][1], &S::zero()) && ulps_eq!(self[2][3], &S::zero()) && ulps_eq!(self[3][0], &S::zero()) && ulps_eq!(self[3][1], &S::zero()) && ulps_eq!(self[3][2], &S::zero()) } fn is_symmetric(&self) -> bool { ulps_eq!(self[0][1], &self[1][0]) && ulps_eq!(self[0][2], &self[2][0]) && ulps_eq!(self[0][3], &self[3][0]) && ulps_eq!(self[1][0], &self[0][1]) && ulps_eq!(self[1][2], &self[2][1]) && ulps_eq!(self[1][3], &self[3][1]) && ulps_eq!(self[2][0], &self[0][2]) && ulps_eq!(self[2][1], &self[1][2]) && ulps_eq!(self[2][3], &self[3][2]) && ulps_eq!(self[3][0], &self[0][3]) && ulps_eq!(self[3][1], &self[1][3]) && ulps_eq!(self[3][2], &self[2][3]) } } impl approx::AbsDiffEq for Matrix2 { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { cast(1.0e-6f64).unwrap() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { Vector2::abs_diff_eq(&self[0], &other[0], epsilon) && Vector2::abs_diff_eq(&self[1], &other[1], epsilon) } } impl approx::RelativeEq for Matrix2 { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { Vector2::relative_eq(&self[0], &other[0], epsilon, max_relative) && Vector2::relative_eq(&self[1], &other[1], epsilon, max_relative) } } impl approx::UlpsEq for Matrix2 { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { Vector2::ulps_eq(&self[0], &other[0], epsilon, max_ulps) && Vector2::ulps_eq(&self[1], &other[1], epsilon, max_ulps) } } impl approx::AbsDiffEq for Matrix3 { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { cast(1.0e-6f64).unwrap() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { Vector3::abs_diff_eq(&self[0], &other[0], epsilon) && Vector3::abs_diff_eq(&self[1], &other[1], epsilon) && Vector3::abs_diff_eq(&self[2], &other[2], epsilon) } } impl approx::RelativeEq for Matrix3 { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { Vector3::relative_eq(&self[0], &other[0], epsilon, max_relative) && Vector3::relative_eq(&self[1], &other[1], epsilon, max_relative) && Vector3::relative_eq(&self[2], &other[2], epsilon, max_relative) } } impl approx::UlpsEq for Matrix3 { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { Vector3::ulps_eq(&self[0], &other[0], epsilon, max_ulps) && Vector3::ulps_eq(&self[1], &other[1], epsilon, max_ulps) && Vector3::ulps_eq(&self[2], &other[2], epsilon, max_ulps) } } impl approx::AbsDiffEq for Matrix4 { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { cast(1.0e-6f64).unwrap() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { Vector4::abs_diff_eq(&self[0], &other[0], epsilon) && Vector4::abs_diff_eq(&self[1], &other[1], epsilon) && Vector4::abs_diff_eq(&self[2], &other[2], epsilon) && Vector4::abs_diff_eq(&self[3], &other[3], epsilon) } } impl approx::RelativeEq for Matrix4 { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { Vector4::relative_eq(&self[0], &other[0], epsilon, max_relative) && Vector4::relative_eq(&self[1], &other[1], epsilon, max_relative) && Vector4::relative_eq(&self[2], &other[2], epsilon, max_relative) && Vector4::relative_eq(&self[3], &other[3], epsilon, max_relative) } } impl approx::UlpsEq for Matrix4 { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { Vector4::ulps_eq(&self[0], &other[0], epsilon, max_ulps) && Vector4::ulps_eq(&self[1], &other[1], epsilon, max_ulps) && Vector4::ulps_eq(&self[2], &other[2], epsilon, max_ulps) && Vector4::ulps_eq(&self[3], &other[3], epsilon, max_ulps) } } impl Transform> for Matrix3 { fn look_at(eye: Point2, center: Point2, up: Vector2) -> Matrix3 { let dir = center - eye; Matrix3::from(Matrix2::look_at(dir, up)) } fn look_at_lh(eye: Point2, center: Point2, up: Vector2) -> Matrix3 { let dir = center - eye; Matrix3::from(Matrix2::look_at(dir, up)) } fn look_at_rh(eye: Point2, center: Point2, up: Vector2) -> Matrix3 { let dir = eye - center; Matrix3::from(Matrix2::look_at(dir, up)) } fn transform_vector(&self, vec: Vector2) -> Vector2 { (self * vec.extend(S::zero())).truncate() } fn transform_point(&self, point: Point2) -> Point2 { Point2::from_vec((self * Point3::new(point.x, point.y, S::one()).to_vec()).truncate()) } fn concat(&self, other: &Matrix3) -> Matrix3 { self * other } fn inverse_transform(&self) -> Option> { SquareMatrix::invert(self) } } impl Transform> for Matrix3 { fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix3 { let dir = center - eye; Matrix3::look_to_lh(dir, up) } fn look_at_lh(eye: Point3, center: Point3, up: Vector3) -> Matrix3 { let dir = center - eye; Matrix3::look_to_lh(dir, up) } fn look_at_rh(eye: Point3, center: Point3, up: Vector3) -> Matrix3 { let dir = center - eye; Matrix3::look_to_rh(dir, up) } fn transform_vector(&self, vec: Vector3) -> Vector3 { self * vec } fn transform_point(&self, point: Point3) -> Point3 { Point3::from_vec(self * point.to_vec()) } fn concat(&self, other: &Matrix3) -> Matrix3 { self * other } fn inverse_transform(&self) -> Option> { SquareMatrix::invert(self) } } impl Transform> for Matrix4 { fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_at_rh(eye, center, up) } fn look_at_lh(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_at_lh(eye, center, up) } fn look_at_rh(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_at_rh(eye, center, up) } fn transform_vector(&self, vec: Vector3) -> Vector3 { (self * vec.extend(S::zero())).truncate() } fn transform_point(&self, point: Point3) -> Point3 { Point3::from_homogeneous(self * point.to_homogeneous()) } fn concat(&self, other: &Matrix4) -> Matrix4 { self * other } fn inverse_transform(&self) -> Option> { SquareMatrix::invert(self) } } impl Transform2 for Matrix3 { type Scalar = S; } impl Transform3 for Matrix3 { type Scalar = S; } impl Transform3 for Matrix4 { type Scalar = S; } macro_rules! impl_matrix { ($MatrixN:ident, $VectorN:ident { $($field:ident : $row_index:expr),+ }) => { impl_operator!( Neg for $MatrixN { fn neg(matrix) -> $MatrixN { $MatrixN { $($field: -matrix.$field),+ } } }); impl_operator!( Mul for $MatrixN { fn mul(matrix, scalar) -> $MatrixN { $MatrixN { $($field: matrix.$field * scalar),+ } } }); impl_operator!( Div for $MatrixN { fn div(matrix, scalar) -> $MatrixN { $MatrixN { $($field: matrix.$field / scalar),+ } } }); impl_operator!( Rem for $MatrixN { fn rem(matrix, scalar) -> $MatrixN { $MatrixN { $($field: matrix.$field % scalar),+ } } }); impl_assignment_operator!( MulAssign for $MatrixN { fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ } }); impl_assignment_operator!( DivAssign for $MatrixN { fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ } }); impl_assignment_operator!( RemAssign for $MatrixN { fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ } }); impl_operator!( Add<$MatrixN > for $MatrixN { fn add(lhs, rhs) -> $MatrixN { $MatrixN { $($field: lhs.$field + rhs.$field),+ } } }); impl_operator!( Sub<$MatrixN > for $MatrixN { fn sub(lhs, rhs) -> $MatrixN { $MatrixN { $($field: lhs.$field - rhs.$field),+ } } }); impl> AddAssign<$MatrixN> for $MatrixN { fn add_assign(&mut self, other: $MatrixN) { $(self.$field += other.$field);+ } } impl> SubAssign<$MatrixN> for $MatrixN { fn sub_assign(&mut self, other: $MatrixN) { $(self.$field -= other.$field);+ } } impl iter::Sum<$MatrixN> for $MatrixN { #[inline] fn sum>>(iter: I) -> $MatrixN { iter.fold($MatrixN::zero(), Add::add) } } impl<'a, S: 'a + BaseFloat> iter::Sum<&'a $MatrixN> for $MatrixN { #[inline] fn sum>>(iter: I) -> $MatrixN { iter.fold($MatrixN::zero(), Add::add) } } impl iter::Product for $MatrixN { #[inline] fn product>>(iter: I) -> $MatrixN { iter.fold($MatrixN::identity(), Mul::mul) } } impl<'a, S: 'a + BaseFloat> iter::Product<&'a $MatrixN> for $MatrixN { #[inline] fn product>>(iter: I) -> $MatrixN { iter.fold($MatrixN::identity(), Mul::mul) } } impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl $MatrixN { /// Component-wise casting to another type #[inline] pub fn cast(&self) -> Option<$MatrixN> { $( let $field = match self.$field.cast() { Some(field) => field, None => return None }; )+ Some($MatrixN { $($field),+ }) } } } } macro_rules! impl_scalar_ops { ($MatrixN:ident<$S:ident> { $($field:ident),+ }) => { impl_operator!(Mul<$MatrixN<$S>> for $S { fn mul(scalar, matrix) -> $MatrixN<$S> { $MatrixN { $($field: scalar * matrix.$field),+ } } }); impl_operator!(Div<$MatrixN<$S>> for $S { fn div(scalar, matrix) -> $MatrixN<$S> { $MatrixN { $($field: scalar / matrix.$field),+ } } }); impl_operator!(Rem<$MatrixN<$S>> for $S { fn rem(scalar, matrix) -> $MatrixN<$S> { $MatrixN { $($field: scalar % matrix.$field),+ } } }); }; } impl_matrix!(Matrix2, Vector2 { x: 0, y: 1 }); impl_matrix!(Matrix3, Vector3 { x: 0, y: 1, z: 2 }); #[cfg_attr(rustfmt, rustfmt_skip)] impl_matrix!(Matrix4, Vector4 { x: 0, y: 1, z: 2, w: 3 }); macro_rules! impl_mv_operator { ($MatrixN:ident, $VectorN:ident { $($field:ident : $row_index:expr),+ }) => { impl_operator!( Mul<$VectorN > for $MatrixN { fn mul(matrix, vector) -> $VectorN {$VectorN::new($(matrix.row($row_index).dot(vector.clone())),+)} }); } } impl_mv_operator!(Matrix2, Vector2 { x: 0, y: 1 }); impl_mv_operator!(Matrix3, Vector3 { x: 0, y: 1, z: 2 }); #[cfg(not(feature = "simd"))] #[cfg_attr(rustfmt, rustfmt_skip)] impl_mv_operator!(Matrix4, Vector4 { x: 0, y: 1, z: 2, w: 3 }); #[cfg(feature = "simd")] impl_operator!( Mul > for Matrix4 { fn mul(matrix, vector) -> Vector4 { matrix[0] * vector[0] + matrix[1] * vector[1] + matrix[2] * vector[2] + matrix[3] * vector[3] } }); impl_operator!( Mul > for Matrix2 { fn mul(lhs, rhs) -> Matrix2 { Matrix2::new(lhs.row(0).dot(rhs[0]), lhs.row(1).dot(rhs[0]), lhs.row(0).dot(rhs[1]), lhs.row(1).dot(rhs[1])) } }); impl_operator!( Mul > for Matrix3 { fn mul(lhs, rhs) -> Matrix3 { Matrix3::new(lhs.row(0).dot(rhs[0]), lhs.row(1).dot(rhs[0]), lhs.row(2).dot(rhs[0]), lhs.row(0).dot(rhs[1]), lhs.row(1).dot(rhs[1]), lhs.row(2).dot(rhs[1]), lhs.row(0).dot(rhs[2]), lhs.row(1).dot(rhs[2]), lhs.row(2).dot(rhs[2])) } }); // Using self.row(0).dot(other[0]) like the other matrix multiplies // causes the LLVM to miss identical loads and multiplies. This optimization // causes the code to be auto vectorized properly increasing the performance // around ~4 times. // Update: this should now be a bit more efficient impl_operator!( Mul > for Matrix4 { fn mul(lhs, rhs) -> Matrix4 { { let a = lhs[0]; let b = lhs[1]; let c = lhs[2]; let d = lhs[3]; #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::from_cols( a*rhs[0][0] + b*rhs[0][1] + c*rhs[0][2] + d*rhs[0][3], a*rhs[1][0] + b*rhs[1][1] + c*rhs[1][2] + d*rhs[1][3], a*rhs[2][0] + b*rhs[2][1] + c*rhs[2][2] + d*rhs[2][3], a*rhs[3][0] + b*rhs[3][1] + c*rhs[3][2] + d*rhs[3][3], ) } } }); macro_rules! index_operators { ($MatrixN:ident<$S:ident>, $n:expr, $Output:ty, $I:ty) => { impl<$S> Index<$I> for $MatrixN<$S> { type Output = $Output; #[inline] fn index<'a>(&'a self, i: $I) -> &'a $Output { let v: &[[$S; $n]; $n] = self.as_ref(); From::from(&v[i]) } } impl<$S> IndexMut<$I> for $MatrixN<$S> { #[inline] fn index_mut<'a>(&'a mut self, i: $I) -> &'a mut $Output { let v: &mut [[$S; $n]; $n] = self.as_mut(); From::from(&mut v[i]) } } }; } index_operators!(Matrix2, 2, Vector2, usize); index_operators!(Matrix3, 3, Vector3, usize); index_operators!(Matrix4, 4, Vector4, usize); // index_operators!(Matrix2, 2, [Vector2], Range); // index_operators!(Matrix3, 3, [Vector3], Range); // index_operators!(Matrix4, 4, [Vector4], Range); // index_operators!(Matrix2, 2, [Vector2], RangeTo); // index_operators!(Matrix3, 3, [Vector3], RangeTo); // index_operators!(Matrix4, 4, [Vector4], RangeTo); // index_operators!(Matrix2, 2, [Vector2], RangeFrom); // index_operators!(Matrix3, 3, [Vector3], RangeFrom); // index_operators!(Matrix4, 4, [Vector4], RangeFrom); // index_operators!(Matrix2, 2, [Vector2], RangeFull); // index_operators!(Matrix3, 3, [Vector3], RangeFull); // index_operators!(Matrix4, 4, [Vector4], RangeFull); impl From> for Matrix3 where A: Angle + Into::Unitless>>, { fn from(src: Euler) -> Matrix3 { // Page A-2: http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf let (sx, cx) = Rad::sin_cos(src.x.into()); let (sy, cy) = Rad::sin_cos(src.y.into()); let (sz, cz) = Rad::sin_cos(src.z.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( cy * cz, cx * sz + sx * sy * cz, sx * sz - cx * sy * cz, -cy * sz, cx * cz - sx * sy * sz, sx * cz + cx * sy * sz, sy, -sx * cy, cx * cy, ) } } impl From> for Matrix4 where A: Angle + Into::Unitless>>, { fn from(src: Euler) -> Matrix4 { // Page A-2: http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf let (sx, cx) = Rad::sin_cos(src.x.into()); let (sy, cy) = Rad::sin_cos(src.y.into()); let (sz, cz) = Rad::sin_cos(src.z.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( cy * cz, cx * sz + sx * sy * cz, sx * sz - cx * sy * cz, A::Unitless::zero(), -cy * sz, cx * cz - sx * sy * sz, sx * cz + cx * sy * sz, A::Unitless::zero(), sy, -sx * cy, cx * cy, A::Unitless::zero(), A::Unitless::zero(), A::Unitless::zero(), A::Unitless::zero(), A::Unitless::one(), ) } } macro_rules! fixed_array_conversions { ($MatrixN:ident <$S:ident> { $($field:ident : $index:expr),+ }, $n:expr) => { impl<$S> Into<[[$S; $n]; $n]> for $MatrixN<$S> { #[inline] fn into(self) -> [[$S; $n]; $n] { match self { $MatrixN { $($field),+ } => [$($field.into()),+] } } } impl<$S> AsRef<[[$S; $n]; $n]> for $MatrixN<$S> { #[inline] fn as_ref(&self) -> &[[$S; $n]; $n] { unsafe { mem::transmute(self) } } } impl<$S> AsMut<[[$S; $n]; $n]> for $MatrixN<$S> { #[inline] fn as_mut(&mut self) -> &mut [[$S; $n]; $n] { unsafe { mem::transmute(self) } } } impl<$S: Copy> From<[[$S; $n]; $n]> for $MatrixN<$S> { #[inline] fn from(m: [[$S; $n]; $n]) -> $MatrixN<$S> { // We need to use a copy here because we can't pattern match on arrays yet $MatrixN { $($field: From::from(m[$index])),+ } } } impl<'a, $S> From<&'a [[$S; $n]; $n]> for &'a $MatrixN<$S> { #[inline] fn from(m: &'a [[$S; $n]; $n]) -> &'a $MatrixN<$S> { unsafe { mem::transmute(m) } } } impl<'a, $S> From<&'a mut [[$S; $n]; $n]> for &'a mut $MatrixN<$S> { #[inline] fn from(m: &'a mut [[$S; $n]; $n]) -> &'a mut $MatrixN<$S> { unsafe { mem::transmute(m) } } } // impl<$S> Into<[$S; ($n * $n)]> for $MatrixN<$S> { // #[inline] // fn into(self) -> [[$S; $n]; $n] { // // TODO: Not sure how to implement this... // unimplemented!() // } // } impl<$S> AsRef<[$S; ($n * $n)]> for $MatrixN<$S> { #[inline] fn as_ref(&self) -> &[$S; ($n * $n)] { unsafe { mem::transmute(self) } } } impl<$S> AsMut<[$S; ($n * $n)]> for $MatrixN<$S> { #[inline] fn as_mut(&mut self) -> &mut [$S; ($n * $n)] { unsafe { mem::transmute(self) } } } // impl<$S> From<[$S; ($n * $n)]> for $MatrixN<$S> { // #[inline] // fn from(m: [$S; ($n * $n)]) -> $MatrixN<$S> { // // TODO: Not sure how to implement this... // unimplemented!() // } // } impl<'a, $S> From<&'a [$S; ($n * $n)]> for &'a $MatrixN<$S> { #[inline] fn from(m: &'a [$S; ($n * $n)]) -> &'a $MatrixN<$S> { unsafe { mem::transmute(m) } } } impl<'a, $S> From<&'a mut [$S; ($n * $n)]> for &'a mut $MatrixN<$S> { #[inline] fn from(m: &'a mut [$S; ($n * $n)]) -> &'a mut $MatrixN<$S> { unsafe { mem::transmute(m) } } } } } fixed_array_conversions!(Matrix2 { x:0, y:1 }, 2); fixed_array_conversions!(Matrix3 { x:0, y:1, z:2 }, 3); fixed_array_conversions!(Matrix4 { x:0, y:1, z:2, w:3 }, 4); #[cfg(feature = "mint")] macro_rules! mint_conversions { ($MatrixN:ident { $($field:ident),+ }, $MintN:ident) => { impl Into> for $MatrixN { #[inline] fn into(self) -> mint::$MintN { mint::$MintN { $($field: self.$field.into()),+ } } } impl From> for $MatrixN { #[inline] fn from(m: mint::$MintN) -> Self { $MatrixN { $($field: m.$field.into()),+ } } } } } #[cfg(feature = "mint")] mint_conversions!(Matrix2 { x, y }, ColumnMatrix2); #[cfg(feature = "mint")] mint_conversions!(Matrix3 { x, y, z }, ColumnMatrix3); #[cfg(feature = "mint")] mint_conversions!(Matrix4 { x, y, z, w }, ColumnMatrix4); impl From> for Matrix3 { /// Clone the elements of a 2-dimensional matrix into the top-left corner /// of a 3-dimensional identity matrix. fn from(m: Matrix2) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( m[0][0], m[0][1], S::zero(), m[1][0], m[1][1], S::zero(), S::zero(), S::zero(), S::one(), ) } } impl From> for Matrix4 { /// Clone the elements of a 2-dimensional matrix into the top-left corner /// of a 4-dimensional identity matrix. fn from(m: Matrix2) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( m[0][0], m[0][1], S::zero(), S::zero(), m[1][0], m[1][1], S::zero(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } } impl From> for Matrix4 { /// Clone the elements of a 3-dimensional matrix into the top-left corner /// of a 4-dimensional identity matrix. fn from(m: Matrix3) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( m[0][0], m[0][1], m[0][2], S::zero(), m[1][0], m[1][1], m[1][2], S::zero(), m[2][0], m[2][1], m[2][2], S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } } impl From> for Quaternion { /// Convert the matrix to a quaternion fn from(mat: Matrix3) -> Quaternion { // http://www.cs.ucr.edu/~vbz/resources/quatut.pdf let trace = mat.trace(); let half: S = cast(0.5f64).unwrap(); if trace >= S::zero() { let s = (S::one() + trace).sqrt(); let w = half * s; let s = half / s; let x = (mat[1][2] - mat[2][1]) * s; let y = (mat[2][0] - mat[0][2]) * s; let z = (mat[0][1] - mat[1][0]) * s; Quaternion::new(w, x, y, z) } else if (mat[0][0] > mat[1][1]) && (mat[0][0] > mat[2][2]) { let s = ((mat[0][0] - mat[1][1] - mat[2][2]) + S::one()).sqrt(); let x = half * s; let s = half / s; let y = (mat[1][0] + mat[0][1]) * s; let z = (mat[0][2] + mat[2][0]) * s; let w = (mat[1][2] - mat[2][1]) * s; Quaternion::new(w, x, y, z) } else if mat[1][1] > mat[2][2] { let s = ((mat[1][1] - mat[0][0] - mat[2][2]) + S::one()).sqrt(); let y = half * s; let s = half / s; let z = (mat[2][1] + mat[1][2]) * s; let x = (mat[1][0] + mat[0][1]) * s; let w = (mat[2][0] - mat[0][2]) * s; Quaternion::new(w, x, y, z) } else { let s = ((mat[2][2] - mat[0][0] - mat[1][1]) + S::one()).sqrt(); let z = half * s; let s = half / s; let x = (mat[0][2] + mat[2][0]) * s; let y = (mat[2][1] + mat[1][2]) * s; let w = (mat[0][1] - mat[1][0]) * s; Quaternion::new(w, x, y, z) } } } impl fmt::Debug for Matrix2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Matrix2 ")?; <[[S; 2]; 2] as fmt::Debug>::fmt(self.as_ref(), f) } } impl fmt::Debug for Matrix3 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Matrix3 ")?; <[[S; 3]; 3] as fmt::Debug>::fmt(self.as_ref(), f) } } impl fmt::Debug for Matrix4 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Matrix4 ")?; <[[S; 4]; 4] as fmt::Debug>::fmt(self.as_ref(), f) } } #[cfg(feature = "rand")] impl Distribution> for Standard where Standard: Distribution>, S: BaseFloat, { #[inline] fn sample(&self, rng: &mut R) -> Matrix2 { Matrix2 { x: self.sample(rng), y: self.sample(rng), } } } #[cfg(feature = "rand")] impl Distribution> for Standard where Standard: Distribution>, S: BaseFloat, { #[inline] fn sample(&self, rng: &mut R) -> Matrix3 { Matrix3 { x: rng.gen(), y: rng.gen(), z: rng.gen(), } } } #[cfg(feature = "rand")] impl Distribution> for Standard where Standard: Distribution>, S: BaseFloat, { #[inline] fn sample(&self, rng: &mut R) -> Matrix4 { Matrix4 { x: rng.gen(), y: rng.gen(), z: rng.gen(), w: rng.gen(), } } } // Sub procedure for SIMD when dealing with determinant and inversion #[inline] unsafe fn det_sub_proc_unsafe( m: &Matrix4, x: usize, y: usize, z: usize, ) -> Vector4 { let s: &[S; 16] = m.as_ref(); let a = Vector4::new( *s.get_unchecked(4 + x), *s.get_unchecked(12 + x), *s.get_unchecked(x), *s.get_unchecked(8 + x), ); let b = Vector4::new( *s.get_unchecked(8 + y), *s.get_unchecked(8 + y), *s.get_unchecked(4 + y), *s.get_unchecked(4 + y), ); let c = Vector4::new( *s.get_unchecked(12 + z), *s.get_unchecked(z), *s.get_unchecked(12 + z), *s.get_unchecked(z), ); let d = Vector4::new( *s.get_unchecked(8 + x), *s.get_unchecked(8 + x), *s.get_unchecked(4 + x), *s.get_unchecked(4 + x), ); let e = Vector4::new( *s.get_unchecked(12 + y), *s.get_unchecked(y), *s.get_unchecked(12 + y), *s.get_unchecked(y), ); let f = Vector4::new( *s.get_unchecked(4 + z), *s.get_unchecked(12 + z), *s.get_unchecked(z), *s.get_unchecked(8 + z), ); let g = Vector4::new( *s.get_unchecked(12 + x), *s.get_unchecked(x), *s.get_unchecked(12 + x), *s.get_unchecked(x), ); let h = Vector4::new( *s.get_unchecked(4 + y), *s.get_unchecked(12 + y), *s.get_unchecked(y), *s.get_unchecked(8 + y), ); let i = Vector4::new( *s.get_unchecked(8 + z), *s.get_unchecked(8 + z), *s.get_unchecked(4 + z), *s.get_unchecked(4 + z), ); let mut tmp = a.mul_element_wise(b.mul_element_wise(c)); tmp += d.mul_element_wise(e.mul_element_wise(f)); tmp += g.mul_element_wise(h.mul_element_wise(i)); tmp -= a.mul_element_wise(e.mul_element_wise(i)); tmp -= d.mul_element_wise(h.mul_element_wise(c)); tmp -= g.mul_element_wise(b.mul_element_wise(f)); tmp } cgmath-0.18.0/src/num.rs010064400007650000024000000031761376272520100131720ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use approx; use std::fmt; use std::ops::*; use num_traits::{Float, Num, NumCast}; /// Base numeric types with partial ordering pub trait BaseNum: Copy + Clone + fmt::Debug + Num + NumCast + PartialOrd + AddAssign + SubAssign + MulAssign + DivAssign + RemAssign { } impl BaseNum for T where T: Copy + Clone + fmt::Debug + Num + NumCast + PartialOrd + AddAssign + SubAssign + MulAssign + DivAssign + RemAssign { } /// Base floating point types pub trait BaseFloat: BaseNum + Float + approx::AbsDiffEq + approx::RelativeEq + approx::UlpsEq { } impl BaseFloat for T where T: BaseNum + Float + approx::AbsDiffEq + approx::RelativeEq + approx::UlpsEq { } cgmath-0.18.0/src/point.rs010064400007650000024000000503451376272520100135240ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. //! Points are fixed positions in affine space with no length or direction. This //! distinguishes them from vectors, which have a length and direction, but do //! not have a fixed position. use num_traits::{Bounded, Float, NumCast}; use std::fmt; use std::mem; use std::ops::*; use structure::*; use approx; use num::{BaseFloat, BaseNum}; use vector::{Vector1, Vector2, Vector3, Vector4}; #[cfg(feature = "mint")] use mint; /// A point in 1-dimensional space. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(PartialEq, Eq, Copy, Clone, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Point1 { pub x: S, } /// A point in 2-dimensional space. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(PartialEq, Eq, Copy, Clone, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Point2 { pub x: S, pub y: S, } /// A point in 3-dimensional space. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(PartialEq, Eq, Copy, Clone, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Point3 { pub x: S, pub y: S, pub z: S, } impl Point3 { #[inline] pub fn from_homogeneous(v: Vector4) -> Point3 { let e = v.truncate() * (S::one() / v.w); Point3::new(e.x, e.y, e.z) //FIXME } #[inline] pub fn to_homogeneous(self) -> Vector4 { Vector4::new(self.x, self.y, self.z, S::one()) } } macro_rules! impl_point { ($PointN:ident { $($field:ident),+ }, $VectorN:ident, $n:expr, $constructor:ident) => { impl $PointN { /// Construct a new point, using the provided values. #[inline] pub const fn new($($field: S),+) -> $PointN { $PointN { $($field: $field),+ } } /// Perform the given operation on each field in the point, returning a new point /// constructed from the operations. #[inline] pub fn map(self, mut f: F) -> $PointN where F: FnMut(S) -> U { $PointN { $($field: f(self.$field)),+ } } /// Construct a new point where each component is the result of /// applying the given operation to each pair of components of the /// given points. #[inline] pub fn zip(self, p2: $PointN, mut f: F) -> $PointN where F: FnMut(S, S2) -> S3 { $PointN { $($field: f(self.$field, p2.$field)),+ } } } /// The short constructor. #[inline] pub const fn $constructor($($field: S),+) -> $PointN { $PointN::new($($field),+) } impl Array for $PointN { type Element = S; #[inline] fn len() -> usize { $n } #[inline] fn from_value(scalar: S) -> $PointN { $PointN { $($field: scalar),+ } } #[inline] fn sum(self) -> S where S: Add { fold_array!(add, { $(self.$field),+ }) } #[inline] fn product(self) -> S where S: Mul { fold_array!(mul, { $(self.$field),+ }) } fn is_finite(&self) -> bool where S: Float { $(self.$field.is_finite())&&+ } } impl $PointN { /// Component-wise casting to another type #[inline] pub fn cast(&self) -> Option<$PointN> { $( let $field = match NumCast::from(self.$field) { Some(field) => field, None => return None }; )+ Some($PointN { $($field),+ }) } } impl MetricSpace for $PointN { type Metric = S; #[inline] fn distance2(self, other: Self) -> S { (other - self).magnitude2() } } impl EuclideanSpace for $PointN { type Scalar = S; type Diff = $VectorN; #[inline] fn origin() -> $PointN { $PointN { $($field: S::zero()),+ } } #[inline] fn from_vec(v: $VectorN) -> $PointN { $PointN::new($(v.$field),+) } #[inline] fn to_vec(self) -> $VectorN { $VectorN::new($(self.$field),+) } #[inline] fn dot(self, v: $VectorN) -> S { $VectorN::new($(self.$field * v.$field),+).sum() } } impl approx::AbsDiffEq for $PointN { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { S::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { $(S::abs_diff_eq(&self.$field, &other.$field, epsilon))&&+ } } impl approx::RelativeEq for $PointN { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { $(S::relative_eq(&self.$field, &other.$field, epsilon, max_relative))&&+ } } impl approx::UlpsEq for $PointN { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { $(S::ulps_eq(&self.$field, &other.$field, epsilon, max_ulps))&&+ } } impl Bounded for $PointN { #[inline] fn min_value() -> $PointN { $PointN { $($field: S::min_value()),+ } } #[inline] fn max_value() -> $PointN { $PointN { $($field: S::max_value()),+ } } } impl_operator!( Add<$VectorN > for $PointN { fn add(lhs, rhs) -> $PointN { $PointN::new($(lhs.$field + rhs.$field),+) } }); impl_operator!( Sub<$VectorN> for $PointN { fn sub(lhs, rhs) -> $PointN { $PointN::new($(lhs.$field - rhs.$field),+) } }); impl_assignment_operator!( AddAssign<$VectorN > for $PointN { fn add_assign(&mut self, vector) { $(self.$field += vector.$field);+ } }); impl_assignment_operator!( SubAssign<$VectorN> for $PointN { fn sub_assign(&mut self, vector) { $(self.$field -= vector.$field);+ } }); impl_operator!( Sub<$PointN > for $PointN { fn sub(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field - rhs.$field),+) } }); impl_operator!( Mul for $PointN { fn mul(point, scalar) -> $PointN { $PointN::new($(point.$field * scalar),+) } }); impl_operator!( Div for $PointN { fn div(point, scalar) -> $PointN { $PointN::new($(point.$field / scalar),+) } }); impl_operator!( Rem for $PointN { fn rem(point, scalar) -> $PointN { $PointN::new($(point.$field % scalar),+) } }); impl_assignment_operator!( MulAssign for $PointN { fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ } }); impl_assignment_operator!( DivAssign for $PointN { fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ } }); impl_assignment_operator!( RemAssign for $PointN { fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ } }); impl ElementWise for $PointN { #[inline] fn add_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field + rhs.$field),+) } #[inline] fn sub_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field - rhs.$field),+) } #[inline] fn mul_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field * rhs.$field),+) } #[inline] fn div_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field / rhs.$field),+) } #[inline] fn rem_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field % rhs.$field),+) } #[inline] fn add_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field += rhs.$field);+ } #[inline] fn sub_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field -= rhs.$field);+ } #[inline] fn mul_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field *= rhs.$field);+ } #[inline] fn div_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field /= rhs.$field);+ } #[inline] fn rem_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field %= rhs.$field);+ } } impl ElementWise for $PointN { #[inline] fn add_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field + rhs),+) } #[inline] fn sub_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field - rhs),+) } #[inline] fn mul_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field * rhs),+) } #[inline] fn div_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field / rhs),+) } #[inline] fn rem_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field % rhs),+) } #[inline] fn add_assign_element_wise(&mut self, rhs: S) { $(self.$field += rhs);+ } #[inline] fn sub_assign_element_wise(&mut self, rhs: S) { $(self.$field -= rhs);+ } #[inline] fn mul_assign_element_wise(&mut self, rhs: S) { $(self.$field *= rhs);+ } #[inline] fn div_assign_element_wise(&mut self, rhs: S) { $(self.$field /= rhs);+ } #[inline] fn rem_assign_element_wise(&mut self, rhs: S) { $(self.$field %= rhs);+ } } impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_scalar_ops!($PointN { $($field),+ }); impl_index_operators!($PointN, $n, S, usize); impl_index_operators!($PointN, $n, [S], Range); impl_index_operators!($PointN, $n, [S], RangeTo); impl_index_operators!($PointN, $n, [S], RangeFrom); impl_index_operators!($PointN, $n, [S], RangeFull); } } macro_rules! impl_scalar_ops { ($PointN:ident<$S:ident> { $($field:ident),+ }) => { impl_operator!(Mul<$PointN<$S>> for $S { fn mul(scalar, point) -> $PointN<$S> { $PointN::new($(scalar * point.$field),+) } }); impl_operator!(Div<$PointN<$S>> for $S { fn div(scalar, point) -> $PointN<$S> { $PointN::new($(scalar / point.$field),+) } }); impl_operator!(Rem<$PointN<$S>> for $S { fn rem(scalar, point) -> $PointN<$S> { $PointN::new($(scalar % point.$field),+) } }); }; } impl_point!(Point1 { x }, Vector1, 1, point1); impl_point!(Point2 { x, y }, Vector2, 2, point2); impl_point!(Point3 { x, y, z }, Vector3, 3, point3); impl Point1 { impl_swizzle_functions!(Point1, Point2, Point3, S, x); } impl Point2 { impl_swizzle_functions!(Point1, Point2, Point3, S, xy); } impl Point3 { impl_swizzle_functions!(Point1, Point2, Point3, S, xyz); } impl_fixed_array_conversions!(Point1 { x: 0 }, 1); impl_fixed_array_conversions!(Point2 { x: 0, y: 1 }, 2); impl_fixed_array_conversions!(Point3 { x: 0, y: 1, z: 2 }, 3); impl_tuple_conversions!(Point1 { x }, (S,)); impl_tuple_conversions!(Point2 { x, y }, (S, S)); impl_tuple_conversions!(Point3 { x, y, z }, (S, S, S)); #[cfg(feature = "mint")] impl_mint_conversions!(Point2 { x, y }, Point2); #[cfg(feature = "mint")] impl_mint_conversions!(Point3 { x, y, z }, Point3); impl fmt::Debug for Point1 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Point1 ")?; <[S; 1] as fmt::Debug>::fmt(self.as_ref(), f) } } impl fmt::Debug for Point2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Point2 ")?; <[S; 2] as fmt::Debug>::fmt(self.as_ref(), f) } } impl fmt::Debug for Point3 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Point3 ")?; <[S; 3] as fmt::Debug>::fmt(self.as_ref(), f) } } #[cfg(test)] mod tests { mod point2 { use point::*; const POINT2: Point2 = Point2 { x: 1, y: 2 }; #[test] fn test_index() { assert_eq!(POINT2[0], POINT2.x); assert_eq!(POINT2[1], POINT2.y); } #[test] fn test_index_mut() { let mut p = POINT2; *&mut p[0] = 0; assert_eq!(p, [0, 2].into()); } #[test] #[should_panic] fn test_index_out_of_bounds() { POINT2[2]; } #[test] fn test_index_range() { assert_eq!(&POINT2[..0], &[]); assert_eq!(&POINT2[..1], &[1]); assert_eq!(POINT2[..0].len(), 0); assert_eq!(POINT2[..1].len(), 1); assert_eq!(&POINT2[2..], &[]); assert_eq!(&POINT2[1..], &[2]); assert_eq!(POINT2[2..].len(), 0); assert_eq!(POINT2[1..].len(), 1); assert_eq!(&POINT2[..], &[1, 2]); assert_eq!(POINT2[..].len(), 2); } #[test] fn test_into() { let p = POINT2; { let p: [i32; 2] = p.into(); assert_eq!(p, [1, 2]); } { let p: (i32, i32) = p.into(); assert_eq!(p, (1, 2)); } } #[test] fn test_as_ref() { let p = POINT2; { let p: &[i32; 2] = p.as_ref(); assert_eq!(p, &[1, 2]); } { let p: &(i32, i32) = p.as_ref(); assert_eq!(p, &(1, 2)); } } #[test] fn test_as_mut() { let mut p = POINT2; { let p: &mut [i32; 2] = p.as_mut(); assert_eq!(p, &mut [1, 2]); } { let p: &mut (i32, i32) = p.as_mut(); assert_eq!(p, &mut (1, 2)); } } #[test] fn test_from() { assert_eq!(Point2::from([1, 2]), POINT2); { let p = &[1, 2]; let p: &Point2<_> = From::from(p); assert_eq!(p, &POINT2); } { let p = &mut [1, 2]; let p: &mut Point2<_> = From::from(p); assert_eq!(p, &POINT2); } assert_eq!(Point2::from((1, 2)), POINT2); { let p = &(1, 2); let p: &Point2<_> = From::from(p); assert_eq!(p, &POINT2); } { let p = &mut (1, 2); let p: &mut Point2<_> = From::from(p); assert_eq!(p, &POINT2); } } #[test] fn test_zip() { assert_eq!( Point2::new(true, false), Point2::new(-2, 1).zip(Point2::new(-1, -1), |a, b| a < b) ); } } mod point3 { use point::*; const POINT3: Point3 = Point3 { x: 1, y: 2, z: 3 }; #[test] fn test_index() { assert_eq!(POINT3[0], POINT3.x); assert_eq!(POINT3[1], POINT3.y); assert_eq!(POINT3[2], POINT3.z); } #[test] fn test_index_mut() { let mut p = POINT3; *&mut p[1] = 0; assert_eq!(p, [1, 0, 3].into()); } #[test] #[should_panic] fn test_index_out_of_bounds() { POINT3[3]; } #[test] fn test_index_range() { assert_eq!(&POINT3[..1], &[1]); assert_eq!(&POINT3[..2], &[1, 2]); assert_eq!(POINT3[..1].len(), 1); assert_eq!(POINT3[..2].len(), 2); assert_eq!(&POINT3[2..], &[3]); assert_eq!(&POINT3[1..], &[2, 3]); assert_eq!(POINT3[2..].len(), 1); assert_eq!(POINT3[1..].len(), 2); assert_eq!(&POINT3[..], &[1, 2, 3]); assert_eq!(POINT3[..].len(), 3); } #[test] fn test_into() { let p = POINT3; { let p: [i32; 3] = p.into(); assert_eq!(p, [1, 2, 3]); } { let p: (i32, i32, i32) = p.into(); assert_eq!(p, (1, 2, 3)); } } #[test] fn test_as_ref() { let p = POINT3; { let p: &[i32; 3] = p.as_ref(); assert_eq!(p, &[1, 2, 3]); } { let p: &(i32, i32, i32) = p.as_ref(); assert_eq!(p, &(1, 2, 3)); } } #[test] fn test_as_mut() { let mut p = POINT3; { let p: &mut [i32; 3] = p.as_mut(); assert_eq!(p, &mut [1, 2, 3]); } { let p: &mut (i32, i32, i32) = p.as_mut(); assert_eq!(p, &mut (1, 2, 3)); } } #[test] fn test_from() { assert_eq!(Point3::from([1, 2, 3]), POINT3); { let p = &[1, 2, 3]; let p: &Point3<_> = From::from(p); assert_eq!(p, &POINT3); } { let p = &mut [1, 2, 3]; let p: &mut Point3<_> = From::from(p); assert_eq!(p, &POINT3); } assert_eq!(Point3::from((1, 2, 3)), POINT3); { let p = &(1, 2, 3); let p: &Point3<_> = From::from(p); assert_eq!(p, &POINT3); } { let p = &mut (1, 2, 3); let p: &mut Point3<_> = From::from(p); assert_eq!(p, &POINT3); } } #[test] fn test_zip() { assert_eq!( Point3::new(true, false, false), Point3::new(-2, 1, 0).zip(Point3::new(-1, -1, -1), |a, b| a < b) ); } } } cgmath-0.18.0/src/prelude.rs010064400007650000024000000006471376272520100140330ustar 00000000000000//! This module contains the most common traits used in `cgmath`. By //! glob-importing this module, you can avoid the need to import each trait //! individually, while still being selective about what types you import. pub use structure::*; pub use rotation::Rotation; pub use rotation::Rotation2; pub use rotation::Rotation3; pub use transform::Transform; pub use transform::Transform2; pub use transform::Transform3; cgmath-0.18.0/src/projection.rs010064400007650000024000000204671376272520100145510ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use num_traits::cast; use num_traits::Zero; use structure::Angle; use angle::Rad; use matrix::Matrix4; use num::BaseFloat; /// Create a perspective projection matrix. /// /// This is the equivalent to the [`gluPerspective`] function. /// /// [`gluPerspective`]: https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml pub fn perspective>>( fovy: A, aspect: S, near: S, far: S, ) -> Matrix4 { PerspectiveFov { fovy: fovy.into(), aspect: aspect, near: near, far: far, } .into() } /// Create a perspective matrix from a view frustum. /// /// This is the equivalent of the now deprecated [`glFrustum`] function. /// /// [`glFrustum`]: http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml pub fn frustum(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4 { Perspective { left: left, right: right, bottom: bottom, top: top, near: near, far: far, } .into() } /// Create an orthographic projection matrix. /// /// This is the equivalent of the now deprecated [`glOrtho`] function. /// /// [`glOrtho`]: http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml pub fn ortho(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4 { Ortho { left: left, right: right, bottom: bottom, top: top, near: near, far: far, } .into() } /// A perspective projection based on a vertical field-of-view angle. #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct PerspectiveFov { pub fovy: Rad, pub aspect: S, pub near: S, pub far: S, } impl PerspectiveFov { pub fn to_perspective(&self) -> Perspective { let two: S = cast(2).unwrap(); let angle = self.fovy / two; let ymax = self.near * Rad::tan(angle); let xmax = ymax * self.aspect; Perspective { left: -xmax, right: xmax, bottom: -ymax, top: ymax, near: self.near.clone(), far: self.far.clone(), } } } impl From> for Matrix4 { fn from(persp: PerspectiveFov) -> Matrix4 { assert!( persp.fovy > Rad::zero(), "The vertical field of view cannot be below zero, found: {:?}", persp.fovy ); assert!( persp.fovy < Rad::turn_div_2(), "The vertical field of view cannot be greater than a half turn, found: {:?}", persp.fovy ); assert!( abs_diff_ne!(persp.aspect.abs(), S::zero()), "The absolute aspect ratio cannot be zero, found: {:?}", persp.aspect.abs() ); assert!( persp.near > S::zero(), "The near plane distance cannot be below zero, found: {:?}", persp.near ); assert!( persp.far > S::zero(), "The far plane distance cannot be below zero, found: {:?}", persp.far ); assert!( abs_diff_ne!(persp.far, persp.near), "The far plane and near plane are too close, found: far: {:?}, near: {:?}", persp.far, persp.near ); let two: S = cast(2).unwrap(); let f = Rad::cot(persp.fovy / two); let c0r0 = f / persp.aspect; let c0r1 = S::zero(); let c0r2 = S::zero(); let c0r3 = S::zero(); let c1r0 = S::zero(); let c1r1 = f; let c1r2 = S::zero(); let c1r3 = S::zero(); let c2r0 = S::zero(); let c2r1 = S::zero(); let c2r2 = (persp.far + persp.near) / (persp.near - persp.far); let c2r3 = -S::one(); let c3r0 = S::zero(); let c3r1 = S::zero(); let c3r2 = (two * persp.far * persp.near) / (persp.near - persp.far); let c3r3 = S::zero(); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( c0r0, c0r1, c0r2, c0r3, c1r0, c1r1, c1r2, c1r3, c2r0, c2r1, c2r2, c2r3, c3r0, c3r1, c3r2, c3r3, ) } } /// A perspective projection with arbitrary left/right/bottom/top distances #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Perspective { pub left: S, pub right: S, pub bottom: S, pub top: S, pub near: S, pub far: S, } impl From> for Matrix4 { fn from(persp: Perspective) -> Matrix4 { assert!( persp.left <= persp.right, "`left` cannot be greater than `right`, found: left: {:?} right: {:?}", persp.left, persp.right ); assert!( persp.bottom <= persp.top, "`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}", persp.bottom, persp.top ); assert!( persp.near <= persp.far, "`near` cannot be greater than `far`, found: near: {:?} far: {:?}", persp.near, persp.far ); let two: S = cast(2i8).unwrap(); let c0r0 = (two * persp.near) / (persp.right - persp.left); let c0r1 = S::zero(); let c0r2 = S::zero(); let c0r3 = S::zero(); let c1r0 = S::zero(); let c1r1 = (two * persp.near) / (persp.top - persp.bottom); let c1r2 = S::zero(); let c1r3 = S::zero(); let c2r0 = (persp.right + persp.left) / (persp.right - persp.left); let c2r1 = (persp.top + persp.bottom) / (persp.top - persp.bottom); let c2r2 = -(persp.far + persp.near) / (persp.far - persp.near); let c2r3 = -S::one(); let c3r0 = S::zero(); let c3r1 = S::zero(); let c3r2 = -(two * persp.far * persp.near) / (persp.far - persp.near); let c3r3 = S::zero(); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( c0r0, c0r1, c0r2, c0r3, c1r0, c1r1, c1r2, c1r3, c2r0, c2r1, c2r2, c2r3, c3r0, c3r1, c3r2, c3r3, ) } } /// An orthographic projection with arbitrary left/right/bottom/top distances #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ortho { pub left: S, pub right: S, pub bottom: S, pub top: S, pub near: S, pub far: S, } impl From> for Matrix4 { fn from(ortho: Ortho) -> Matrix4 { let two: S = cast(2).unwrap(); let c0r0 = two / (ortho.right - ortho.left); let c0r1 = S::zero(); let c0r2 = S::zero(); let c0r3 = S::zero(); let c1r0 = S::zero(); let c1r1 = two / (ortho.top - ortho.bottom); let c1r2 = S::zero(); let c1r3 = S::zero(); let c2r0 = S::zero(); let c2r1 = S::zero(); let c2r2 = -two / (ortho.far - ortho.near); let c2r3 = S::zero(); let c3r0 = -(ortho.right + ortho.left) / (ortho.right - ortho.left); let c3r1 = -(ortho.top + ortho.bottom) / (ortho.top - ortho.bottom); let c3r2 = -(ortho.far + ortho.near) / (ortho.far - ortho.near); let c3r3 = S::one(); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( c0r0, c0r1, c0r2, c0r3, c1r0, c1r1, c1r2, c1r3, c2r0, c2r1, c2r2, c2r3, c3r0, c3r1, c3r2, c3r3, ) } } cgmath-0.18.0/src/quaternion.rs010064400007650000024000000675011377421362200145640ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use std::iter; use std::mem; use std::ops::*; use num_traits::{cast, NumCast}; #[cfg(feature = "rand")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use structure::*; use angle::Rad; use approx; use euler::Euler; use matrix::{Matrix3, Matrix4}; use num::BaseFloat; use point::Point3; use rotation::{Basis3, Rotation, Rotation3}; use vector::Vector3; #[cfg(feature = "mint")] use mint; /// A [quaternion](https://en.wikipedia.org/wiki/Quaternion) in scalar/vector /// form. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Quaternion { /// The vector part of the quaternion. pub v: Vector3, /// The scalar part of the quaternion. pub s: S, } impl Quaternion { /// Construct a new quaternion from one scalar component and three /// imaginary components. #[inline] pub const fn new(w: S, xi: S, yj: S, zk: S) -> Quaternion { Quaternion::from_sv(w, Vector3::new(xi, yj, zk)) } /// Construct a new quaternion from a scalar and a vector. #[inline] pub const fn from_sv(s: S, v: Vector3) -> Quaternion { Quaternion { s: s, v: v } } } impl Quaternion { /// Construct a new quaternion as a closest arc between two vectors /// /// Return the closest rotation that turns `src` vector into `dst`. /// /// - [Related StackOverflow question] /// (http://stackoverflow.com/questions/1171849/finding-quaternion-representing-the-rotation-from-one-vector-to-another) /// - [Ogre implementation for normalized vectors] /// (https://bitbucket.org/sinbad/ogre/src/9db75e3ba05c/OgreMain/include/OgreVector3.h?fileviewer=file-view-default#cl-651) pub fn from_arc( src: Vector3, dst: Vector3, fallback: Option>, ) -> Quaternion { let mag_avg = (src.magnitude2() * dst.magnitude2()).sqrt(); let dot = src.dot(dst); if ulps_eq!(dot, &mag_avg) { Quaternion::::one() } else if ulps_eq!(dot, &-mag_avg) { let axis = fallback.unwrap_or_else(|| { let mut v = Vector3::unit_x().cross(src); if ulps_eq!(v, &Zero::zero()) { v = Vector3::unit_y().cross(src); } v.normalize() }); Quaternion::from_axis_angle(axis, Rad::turn_div_2()) } else { Quaternion::from_sv(mag_avg + dot, src.cross(dst)).normalize() } } /// The conjugate of the quaternion. #[inline] pub fn conjugate(self) -> Quaternion { Quaternion::from_sv(self.s, -self.v) } /// Do a normalized linear interpolation with `other`, by `amount`. /// /// This takes the shortest path, so if the quaternions have a negative /// dot product, the interpolation will be between `self` and `-other`. pub fn nlerp(self, mut other: Quaternion, amount: S) -> Quaternion { if self.dot(other) < S::zero() { other = -other; } (self * (S::one() - amount) + other * amount).normalize() } /// Spherical Linear Interpolation /// /// Return the spherical linear interpolation between the quaternion and /// `other`. Both quaternions should be normalized first. /// /// This takes the shortest path, so if the quaternions have a negative /// dot product, the interpolation will be between `self` and `-other`. /// /// # Performance notes /// /// The `acos` operation used in `slerp` is an expensive operation, so /// unless your quaternions are far away from each other it's generally /// more advisable to use `nlerp` when you know your rotations are going /// to be small. /// /// - [Understanding Slerp, Then Not Using It] /// (http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/) /// - [Arcsynthesis OpenGL tutorial] /// (http://www.arcsynthesis.org/gltut/Positioning/Tut08%20Interpolation.html) pub fn slerp(self, mut other: Quaternion, amount: S) -> Quaternion { let mut dot = self.dot(other); let dot_threshold: S = cast(0.9995f64).unwrap(); if dot < S::zero() { other = -other; dot = -dot; } // if quaternions are close together use `nlerp` if dot > dot_threshold { self.nlerp(other, amount) } else { // stay within the domain of acos() let robust_dot = dot.min(S::one()).max(-S::one()); let theta = Rad::acos(robust_dot); let scale1 = Rad::sin(theta * (S::one() - amount)); let scale2 = Rad::sin(theta * amount); (self * scale1 + other * scale2).normalize() } } pub fn is_finite(&self) -> bool { self.s.is_finite() && self.v.is_finite() } } impl Zero for Quaternion { #[inline] fn zero() -> Quaternion { Quaternion::from_sv(S::zero(), Vector3::zero()) } #[inline] fn is_zero(&self) -> bool { ulps_eq!(self, &Quaternion::::zero()) } } impl One for Quaternion { #[inline] fn one() -> Quaternion { Quaternion::from_sv(S::one(), Vector3::zero()) } } impl iter::Sum> for Quaternion { #[inline] fn sum>>(iter: I) -> Quaternion { iter.fold(Quaternion::::zero(), Add::add) } } impl<'a, S: 'a + BaseFloat> iter::Sum<&'a Quaternion> for Quaternion { #[inline] fn sum>>(iter: I) -> Quaternion { iter.fold(Quaternion::::zero(), Add::add) } } impl iter::Product> for Quaternion { #[inline] fn product>>(iter: I) -> Quaternion { iter.fold(Quaternion::::one(), Mul::mul) } } impl<'a, S: 'a + BaseFloat> iter::Product<&'a Quaternion> for Quaternion { #[inline] fn product>>(iter: I) -> Quaternion { iter.fold(Quaternion::::one(), Mul::mul) } } impl VectorSpace for Quaternion { type Scalar = S; } impl MetricSpace for Quaternion { type Metric = S; #[inline] fn distance2(self, other: Self) -> S { (other - self).magnitude2() } } impl Quaternion { /// Component-wise casting to another type. pub fn cast(&self) -> Option> { let s = match NumCast::from(self.s) { Some(s) => s, None => return None, }; let v = match self.v.cast() { Some(v) => v, None => return None, }; Some(Quaternion::from_sv(s, v)) } } impl InnerSpace for Quaternion { #[inline] default_fn!( dot(self, other: Quaternion) -> S { self.s * other.s + self.v.dot(other.v) } ); } impl From> for Quaternion where A: Angle + Into::Unitless>>, { fn from(src: Euler) -> Quaternion { // Euclidean Space has an Euler to quat equation, but it is for a different order (YXZ): // http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm // Page A-2 here has the formula for XYZ: // http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf let half = cast(0.5f64).unwrap(); let (s_x, c_x) = Rad::sin_cos(src.x.into() * half); let (s_y, c_y) = Rad::sin_cos(src.y.into() * half); let (s_z, c_z) = Rad::sin_cos(src.z.into() * half); Quaternion::new( -s_x * s_y * s_z + c_x * c_y * c_z, s_x * c_y * c_z + s_y * s_z * c_x, -s_x * s_z * c_y + s_y * c_x * c_z, s_x * s_y * c_z + s_z * c_x * c_y, ) } } impl_operator!( Neg for Quaternion { fn neg(quat) -> Quaternion { Quaternion::from_sv(-quat.s, -quat.v) } }); impl_operator!( Mul for Quaternion { fn mul(lhs, rhs) -> Quaternion { Quaternion::from_sv(lhs.s * rhs, lhs.v * rhs) } }); impl_assignment_operator!( MulAssign for Quaternion { fn mul_assign(&mut self, scalar) { self.s *= scalar; self.v *= scalar; } }); impl_operator!( Div for Quaternion { fn div(lhs, rhs) -> Quaternion { Quaternion::from_sv(lhs.s / rhs, lhs.v / rhs) } }); impl_assignment_operator!( DivAssign for Quaternion { fn div_assign(&mut self, scalar) { self.s /= scalar; self.v /= scalar; } }); impl_operator!( Rem for Quaternion { fn rem(lhs, rhs) -> Quaternion { Quaternion::from_sv(lhs.s % rhs, lhs.v % rhs) } }); impl_assignment_operator!( RemAssign for Quaternion { fn rem_assign(&mut self, scalar) { self.s %= scalar; self.v %= scalar; } }); impl_operator!( Mul > for Quaternion { fn mul(lhs, rhs) -> Vector3 {{ let rhs = rhs.clone(); let two: S = cast(2i8).unwrap(); let tmp = lhs.v.cross(rhs) + (rhs * lhs.s); (lhs.v.cross(tmp) * two) + rhs }} }); impl_operator!( Add > for Quaternion { fn add(lhs, rhs) -> Quaternion { Quaternion::from_sv(lhs.s + rhs.s, lhs.v + rhs.v) } }); impl_assignment_operator!( AddAssign > for Quaternion { fn add_assign(&mut self, other) { self.s += other.s; self.v += other.v; } }); impl_operator!( Sub > for Quaternion { fn sub(lhs, rhs) -> Quaternion { Quaternion::from_sv(lhs.s - rhs.s, lhs.v - rhs.v) } }); impl_assignment_operator!( SubAssign > for Quaternion { fn sub_assign(&mut self, other) { self.s -= other.s; self.v -= other.v; } }); impl_operator!( Mul > for Quaternion { fn mul(lhs, rhs) -> Quaternion { Quaternion::new( lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z, lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y, lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z, lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x, ) } }); macro_rules! impl_scalar_mul { ($S:ident) => { impl_operator!(Mul> for $S { fn mul(scalar, quat) -> Quaternion<$S> { Quaternion::from_sv(scalar * quat.s, scalar * quat.v) } }); }; } macro_rules! impl_scalar_div { ($S:ident) => { impl_operator!(Div> for $S { fn div(scalar, quat) -> Quaternion<$S> { Quaternion::from_sv(scalar / quat.s, scalar / quat.v) } }); }; } impl_scalar_mul!(f32); impl_scalar_mul!(f64); impl_scalar_div!(f32); impl_scalar_div!(f64); impl approx::AbsDiffEq for Quaternion { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { S::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { S::abs_diff_eq(&self.s, &other.s, epsilon) && Vector3::abs_diff_eq(&self.v, &other.v, epsilon) } } impl approx::RelativeEq for Quaternion { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { S::relative_eq(&self.s, &other.s, epsilon, max_relative) && Vector3::relative_eq(&self.v, &other.v, epsilon, max_relative) } } impl approx::UlpsEq for Quaternion { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { S::ulps_eq(&self.s, &other.s, epsilon, max_ulps) && Vector3::ulps_eq(&self.v, &other.v, epsilon, max_ulps) } } impl From> for Matrix3 { /// Convert the quaternion to a 3 x 3 rotation matrix. fn from(quat: Quaternion) -> Matrix3 { let x2 = quat.v.x + quat.v.x; let y2 = quat.v.y + quat.v.y; let z2 = quat.v.z + quat.v.z; let xx2 = x2 * quat.v.x; let xy2 = x2 * quat.v.y; let xz2 = x2 * quat.v.z; let yy2 = y2 * quat.v.y; let yz2 = y2 * quat.v.z; let zz2 = z2 * quat.v.z; let sy2 = y2 * quat.s; let sz2 = z2 * quat.s; let sx2 = x2 * quat.s; #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2, xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2, xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2, ) } } impl From> for Matrix4 { /// Convert the quaternion to a 4 x 4 rotation matrix. fn from(quat: Quaternion) -> Matrix4 { let x2 = quat.v.x + quat.v.x; let y2 = quat.v.y + quat.v.y; let z2 = quat.v.z + quat.v.z; let xx2 = x2 * quat.v.x; let xy2 = x2 * quat.v.y; let xz2 = x2 * quat.v.z; let yy2 = y2 * quat.v.y; let yz2 = y2 * quat.v.z; let zz2 = z2 * quat.v.z; let sy2 = y2 * quat.s; let sz2 = z2 * quat.s; let sx2 = x2 * quat.s; #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2, S::zero(), xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2, S::zero(), xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } } // Quaternion Rotation impls impl From> for Basis3 { #[inline] fn from(quat: Quaternion) -> Basis3 { Basis3::from_quaternion(&quat) } } impl Rotation for Quaternion { type Space = Point3; #[inline] fn look_at(dir: Vector3, up: Vector3) -> Quaternion { Matrix3::look_to_lh(dir, up).into() } #[inline] fn between_vectors(a: Vector3, b: Vector3) -> Quaternion { // http://stackoverflow.com/a/11741520/2074937 see 'Half-Way Quaternion Solution' let k_cos_theta = a.dot(b); // same direction if ulps_eq!(k_cos_theta, S::one()) { return Quaternion::::one(); } let k = (a.magnitude2() * b.magnitude2()).sqrt(); // opposite direction if ulps_eq!(k_cos_theta / k, -S::one()) { let mut orthogonal = a.cross(Vector3::unit_x()); if ulps_eq!(orthogonal.magnitude2(), S::zero()) { orthogonal = a.cross(Vector3::unit_y()); } return Quaternion::from_sv(S::zero(), orthogonal.normalize()); } // any other direction Quaternion::from_sv(k + k_cos_theta, a.cross(b)).normalize() } /// Evaluate the conjugation of `vec` by `self`. /// /// Note that `self` should be a unit quaternion (i.e. normalized) to represent a 3D rotation. #[inline] fn rotate_vector(&self, vec: Vector3) -> Vector3 { self * vec } #[inline] fn invert(&self) -> Quaternion { self.conjugate() / self.magnitude2() } } impl Rotation3 for Quaternion { type Scalar = S; #[inline] fn from_axis_angle>>(axis: Vector3, angle: A) -> Quaternion { let (s, c) = Rad::sin_cos(angle.into() * cast(0.5f64).unwrap()); Quaternion::from_sv(c, axis * s) } } impl Into<[S; 4]> for Quaternion { #[inline] fn into(self) -> [S; 4] { match self.into() { (xi, yj, zk, w) => [xi, yj, zk, w], } } } impl AsRef<[S; 4]> for Quaternion { #[inline] fn as_ref(&self) -> &[S; 4] { unsafe { mem::transmute(self) } } } impl AsMut<[S; 4]> for Quaternion { #[inline] fn as_mut(&mut self) -> &mut [S; 4] { unsafe { mem::transmute(self) } } } impl From<[S; 4]> for Quaternion { #[inline] fn from(v: [S; 4]) -> Quaternion { Quaternion::new(v[3], v[0], v[1], v[2]) } } impl<'a, S: BaseFloat> From<&'a [S; 4]> for &'a Quaternion { #[inline] fn from(v: &'a [S; 4]) -> &'a Quaternion { unsafe { mem::transmute(v) } } } impl<'a, S: BaseFloat> From<&'a mut [S; 4]> for &'a mut Quaternion { #[inline] fn from(v: &'a mut [S; 4]) -> &'a mut Quaternion { unsafe { mem::transmute(v) } } } impl Into<(S, S, S, S)> for Quaternion { #[inline] fn into(self) -> (S, S, S, S) { match self { Quaternion { s, v: Vector3 { x, y, z }, } => (x, y, z, s), } } } impl AsRef<(S, S, S, S)> for Quaternion { #[inline] fn as_ref(&self) -> &(S, S, S, S) { unsafe { mem::transmute(self) } } } impl AsMut<(S, S, S, S)> for Quaternion { #[inline] fn as_mut(&mut self) -> &mut (S, S, S, S) { unsafe { mem::transmute(self) } } } impl From<(S, S, S, S)> for Quaternion { #[inline] fn from(v: (S, S, S, S)) -> Quaternion { match v { (xi, yj, zk, w) => Quaternion::new(w, xi, yj, zk), } } } impl<'a, S: BaseFloat> From<&'a (S, S, S, S)> for &'a Quaternion { #[inline] fn from(v: &'a (S, S, S, S)) -> &'a Quaternion { unsafe { mem::transmute(v) } } } impl<'a, S: BaseFloat> From<&'a mut (S, S, S, S)> for &'a mut Quaternion { #[inline] fn from(v: &'a mut (S, S, S, S)) -> &'a mut Quaternion { unsafe { mem::transmute(v) } } } macro_rules! index_operators { ($S:ident, $Output:ty, $I:ty) => { impl<$S: BaseFloat> Index<$I> for Quaternion<$S> { type Output = $Output; #[inline] fn index<'a>(&'a self, i: $I) -> &'a $Output { let v: &[$S; 4] = self.as_ref(); &v[i] } } impl<$S: BaseFloat> IndexMut<$I> for Quaternion<$S> { #[inline] fn index_mut<'a>(&'a mut self, i: $I) -> &'a mut $Output { let v: &mut [$S; 4] = self.as_mut(); &mut v[i] } } }; } index_operators!(S, S, usize); index_operators!(S, [S], Range); index_operators!(S, [S], RangeTo); index_operators!(S, [S], RangeFrom); index_operators!(S, [S], RangeFull); #[cfg(feature = "rand")] impl Distribution> for Standard where Standard: Distribution, Standard: Distribution>, S: BaseFloat, { #[inline] fn sample(&self, rng: &mut R) -> Quaternion { Quaternion::from_sv(rng.gen(), rng.gen()) } } #[cfg(feature = "mint")] impl From> for Quaternion { fn from(q: mint::Quaternion) -> Self { Quaternion { s: q.s, v: q.v.into(), } } } #[cfg(feature = "mint")] impl Into> for Quaternion { fn into(self) -> mint::Quaternion { mint::Quaternion { s: self.s, v: self.v.into(), } } } #[cfg(test)] mod tests { use quaternion::*; use vector::*; const QUATERNION: Quaternion = Quaternion { v: Vector3 { x: 1.0, y: 2.0, z: 3.0, }, s: 4.0, }; #[test] fn test_into() { let v = QUATERNION; { let v: [f32; 4] = v.into(); assert_eq!(v, [1.0, 2.0, 3.0, 4.0]); } { let v: (f32, f32, f32, f32) = v.into(); assert_eq!(v, (1.0, 2.0, 3.0, 4.0)); } } #[test] fn test_as_ref() { let v = QUATERNION; { let v: &[f32; 4] = v.as_ref(); assert_eq!(v, &[1.0, 2.0, 3.0, 4.0]); } { let v: &(f32, f32, f32, f32) = v.as_ref(); assert_eq!(v, &(1.0, 2.0, 3.0, 4.0)); } } #[test] fn test_as_mut() { let mut v = QUATERNION; { let v: &mut [f32; 4] = v.as_mut(); assert_eq!(v, &mut [1.0, 2.0, 3.0, 4.0]); } { let v: &mut (f32, f32, f32, f32) = v.as_mut(); assert_eq!(v, &mut (1.0, 2.0, 3.0, 4.0)); } } #[test] fn test_from() { assert_eq!(Quaternion::from([1.0, 2.0, 3.0, 4.0]), QUATERNION); { let v = &[1.0, 2.0, 3.0, 4.0]; let v: &Quaternion<_> = From::from(v); assert_eq!(v, &QUATERNION); } { let v = &mut [1.0, 2.0, 3.0, 4.0]; let v: &mut Quaternion<_> = From::from(v); assert_eq!(v, &QUATERNION); } assert_eq!(Quaternion::from((1.0, 2.0, 3.0, 4.0)), QUATERNION); { let v = &(1.0, 2.0, 3.0, 4.0); let v: &Quaternion<_> = From::from(v); assert_eq!(v, &QUATERNION); } { let v = &mut (1.0, 2.0, 3.0, 4.0); let v: &mut Quaternion<_> = From::from(v); assert_eq!(v, &QUATERNION); } } #[test] fn test_nlerp_same() { let q = Quaternion::from([0.5, 0.5, 0.5, 0.5]); assert_ulps_eq!(q, q.nlerp(q, 0.1234)); } #[test] fn test_nlerp_start() { let q = Quaternion::from([0.5f64.sqrt(), 0.0, 0.5f64.sqrt(), 0.0]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); assert_ulps_eq!(q, q.nlerp(r, 0.0)); } #[test] fn test_nlerp_end() { let q = Quaternion::from([0.5f64.sqrt(), 0.0, 0.5f64.sqrt(), 0.0]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); assert_ulps_eq!(r, q.nlerp(r, 1.0)); } #[test] fn test_nlerp_half() { let q = Quaternion::from([-0.5, 0.5, 0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([0.0, 1.0 / 3f64.sqrt(), 1.0 / 3f64.sqrt(), 1.0 / 3f64.sqrt()]); assert_ulps_eq!(expected, q.nlerp(r, 0.5)); } #[test] fn test_nlerp_quarter() { let q = Quaternion::from([-0.5, 0.5, 0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([ -1.0 / 13f64.sqrt(), 2.0 / 13f64.sqrt(), 2.0 / 13f64.sqrt(), 2.0 / 13f64.sqrt(), ]); assert_ulps_eq!(expected, q.nlerp(r, 0.25)); } #[test] fn test_nlerp_zero_dot() { let q = Quaternion::from([-0.5, -0.5, 0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([ -1.0 / 10f64.sqrt(), -1.0 / 10f64.sqrt(), 2.0 / 10f64.sqrt(), 2.0 / 10f64.sqrt(), ]); assert_ulps_eq!(expected, q.nlerp(r, 0.25)); } #[test] fn test_nlerp_negative_dot() { let q = Quaternion::from([-0.5, -0.5, -0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([ -2.0 / 13f64.sqrt(), -2.0 / 13f64.sqrt(), -2.0 / 13f64.sqrt(), 1.0 / 13f64.sqrt(), ]); assert_ulps_eq!(expected, q.nlerp(r, 0.25)); } #[test] fn test_nlerp_opposite() { let q = Quaternion::from([-0.5, -0.5, -0.5, -0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); assert_ulps_eq!(q, q.nlerp(r, 0.25)); assert_ulps_eq!(q, q.nlerp(r, 0.75)); } #[test] fn test_nlerp_extrapolate() { let q = Quaternion::from([-0.5, -0.5, -0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([ -1.0 / 12f64.sqrt(), -1.0 / 12f64.sqrt(), -1.0 / 12f64.sqrt(), 3.0 / 12f64.sqrt(), ]); assert_ulps_eq!(expected, q.nlerp(r, -1.0)); } #[test] fn test_slerp_same() { let q = Quaternion::from([0.5, 0.5, 0.5, 0.5]); assert_ulps_eq!(q, q.slerp(q, 0.1234)); } #[test] fn test_slerp_start() { let q = Quaternion::from([0.5f64.sqrt(), 0.0, 0.5f64.sqrt(), 0.0]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); assert_ulps_eq!(q, q.slerp(r, 0.0)); } #[test] fn test_slerp_end() { let q = Quaternion::from([0.5f64.sqrt(), 0.0, 0.5f64.sqrt(), 0.0]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); assert_ulps_eq!(r, q.slerp(r, 1.0)); } #[test] fn test_slerp_half() { let q = Quaternion::from([-0.5, 0.5, 0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([0.0, 1.0 / 3f64.sqrt(), 1.0 / 3f64.sqrt(), 1.0 / 3f64.sqrt()]); assert_ulps_eq!(expected, q.slerp(r, 0.5)); } #[test] fn test_slerp_quarter() { let q = Quaternion::from([-0.5, 0.5, 0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([ -0.2588190451025208, 0.5576775358252053, 0.5576775358252053, 0.5576775358252053, ]); assert_ulps_eq!(expected, q.slerp(r, 0.25)); } #[test] fn test_slerp_zero_dot() { let q = Quaternion::from([-0.5, -0.5, 0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([ -0.27059805007309845, -0.27059805007309845, 0.6532814824381883, 0.6532814824381883, ]); assert_ulps_eq!(expected, q.slerp(r, 0.25)); } #[test] fn test_slerp_negative_dot() { let q = Quaternion::from([-0.5, -0.5, -0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([ -0.5576775358252053, -0.5576775358252053, -0.5576775358252053, 0.2588190451025208 ]); assert_ulps_eq!(expected, q.slerp(r, 0.25)); } #[test] fn test_slerp_opposite() { let q = Quaternion::from([-0.5, -0.5, -0.5, -0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); assert_ulps_eq!(q, q.slerp(r, 0.25)); assert_ulps_eq!(q, q.slerp(r, 0.75)); } #[test] fn test_slerp_extrapolate() { let q = Quaternion::from([-0.5, -0.5, -0.5, 0.5]); let r = Quaternion::from([0.5, 0.5, 0.5, 0.5]); let expected = Quaternion::from([0.0, 0.0, 0.0, 1.0]); assert_ulps_eq!(expected, q.slerp(r, -1.0)); } #[test] fn test_slerp_regression() { let a = Quaternion::::new(0.00052311074, 0.9999999, 0.00014682197, -0.000016342687); let b = Quaternion::::new(0.019973433, -0.99980056, -0.00015678025, 0.000013882192); assert_ulps_eq!(a.slerp(b, 0.5).magnitude(), 1.0); } } cgmath-0.18.0/src/quaternion_simd.rs010064400007650000024000000101471376272520100155700ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use quaternion::*; use structure::*; use std::mem; use std::ops::*; use simd::f32x4 as Simdf32x4; impl From for Quaternion { #[inline] fn from(f: Simdf32x4) -> Self { unsafe { let mut ret: Self = mem::uninitialized(); { let ret_mut: &mut [f32; 4] = ret.as_mut(); f.store(ret_mut.as_mut(), 0 as usize); } ret } } } impl Into for Quaternion { #[inline] fn into(self) -> Simdf32x4 { let self_ref: &[f32; 4] = self.as_ref(); Simdf32x4::load(self_ref.as_ref(), 0 as usize) } } impl InnerSpace for Quaternion { #[inline] fn dot(self, other: Quaternion) -> f32 { let lhs: Simdf32x4 = self.into(); let rhs: Simdf32x4 = other.into(); let r = lhs * rhs; r.extract(0) + r.extract(1) + r.extract(2) + r.extract(3) } } impl_operator_simd! { [Simdf32x4]; Neg for Quaternion { fn neg(lhs) -> Quaternion { (-lhs).into() } } } impl_operator_simd! {@rs [Simdf32x4]; Mul for Quaternion { fn mul(lhs, rhs) -> Quaternion { (lhs * rhs).into() } } } impl MulAssign for Quaternion { fn mul_assign(&mut self, other: f32) { let s: Simdf32x4 = (*self).into(); let other = Simdf32x4::splat(other); *self = (s * other).into(); } } impl_operator_simd! {@rs [Simdf32x4]; Div for Quaternion { fn div(lhs, rhs) -> Quaternion { (lhs / rhs).into() } } } impl DivAssign for Quaternion { fn div_assign(&mut self, other: f32) { let s: Simdf32x4 = (*self).into(); let other = Simdf32x4::splat(other); *self = (s / other).into(); } } impl_operator_simd! { [Simdf32x4]; Add> for Quaternion { fn add(lhs, rhs) -> Quaternion { (lhs + rhs).into() } } } impl AddAssign for Quaternion { #[inline] fn add_assign(&mut self, rhs: Self) { let s: Simdf32x4 = (*self).into(); let rhs: Simdf32x4 = rhs.into(); *self = (s + rhs).into(); } } impl_operator_simd! { [Simdf32x4]; Sub> for Quaternion { fn sub(lhs, rhs) -> Quaternion { (lhs - rhs).into() } } } impl SubAssign for Quaternion { #[inline] fn sub_assign(&mut self, rhs: Self) { let s: Simdf32x4 = (*self).into(); let rhs: Simdf32x4 = rhs.into(); *self = (s - rhs).into(); } } impl_operator_simd! { [Simdf32x4]; Mul> for Quaternion { fn mul(lhs, rhs) -> Quaternion { { let p0 = Simdf32x4::splat(lhs.extract(0)) * rhs; let p1 = Simdf32x4::splat(lhs.extract(1)) * Simdf32x4::new( -rhs.extract(1), rhs.extract(0), -rhs.extract(3), rhs.extract(2) ); let p2 = Simdf32x4::splat(lhs.extract(2)) * Simdf32x4::new( -rhs.extract(2), rhs.extract(3), rhs.extract(0), -rhs.extract(1) ); let p3 = Simdf32x4::splat(lhs.extract(3)) * Simdf32x4::new( -rhs.extract(3), -rhs.extract(2), rhs.extract(1), rhs.extract(0) ); (p0 + p1 + p2 + p3).into() } } } } cgmath-0.18.0/src/rotation.rs010064400007650000024000000340141377421362200142270ustar 00000000000000// Copyright 2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use std::fmt; use std::iter; use std::ops::*; use structure::*; use angle::Rad; use approx; use euler::Euler; use matrix::{Matrix2, Matrix3}; use num::BaseFloat; use point::{Point2, Point3}; use quaternion::Quaternion; use vector::{Vector2, Vector3}; /// A trait for a generic rotation. A rotation is a transformation that /// creates a circular motion, and preserves at least one point in the space. pub trait Rotation: Sized + Copy + One where // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: approx::AbsDiffEq::Space as EuclideanSpace>::Scalar>, Self: approx::RelativeEq::Space as EuclideanSpace>::Scalar>, Self: approx::UlpsEq::Space as EuclideanSpace>::Scalar>, ::Scalar: BaseFloat, Self: iter::Product, { type Space: EuclideanSpace; /// Create a rotation to a given direction with an 'up' vector. fn look_at( dir: ::Diff, up: ::Diff, ) -> Self; /// Create a shortest rotation to transform vector 'a' into 'b'. /// Both given vectors are assumed to have unit length. fn between_vectors( a: ::Diff, b: ::Diff, ) -> Self; /// Rotate a vector using this rotation. fn rotate_vector( &self, vec: ::Diff, ) -> ::Diff; /// Rotate a point using this rotation, by converting it to its /// representation as a vector. #[inline] fn rotate_point(&self, point: Self::Space) -> Self::Space { Self::Space::from_vec(self.rotate_vector(point.to_vec())) } /// Create a new rotation which "un-does" this rotation. That is, /// `r * r.invert()` is the identity. fn invert(&self) -> Self; } /// A two-dimensional rotation. pub trait Rotation2: Rotation::Scalar>> + Into::Scalar>> + Into::Scalar>> { type Scalar: BaseFloat; /// Create a rotation by a given angle. Thus is a redundant case of both /// from_axis_angle() and from_euler() for 2D space. fn from_angle>>(theta: A) -> Self; } /// A three-dimensional rotation. pub trait Rotation3: Rotation::Scalar>> + Into::Scalar>> + Into::Scalar>> + Into::Scalar>> + From::Scalar>>> { type Scalar: BaseFloat; /// Create a rotation using an angle around a given axis. /// /// The specified axis **must be normalized**, or it represents an invalid rotation. fn from_axis_angle>>(axis: Vector3, angle: A) -> Self; /// Create a rotation from an angle around the `x` axis (pitch). #[inline] fn from_angle_x>>(theta: A) -> Self { Rotation3::from_axis_angle(Vector3::unit_x(), theta) } /// Create a rotation from an angle around the `y` axis (yaw). #[inline] fn from_angle_y>>(theta: A) -> Self { Rotation3::from_axis_angle(Vector3::unit_y(), theta) } /// Create a rotation from an angle around the `z` axis (roll). #[inline] fn from_angle_z>>(theta: A) -> Self { Rotation3::from_axis_angle(Vector3::unit_z(), theta) } } /// A two-dimensional rotation matrix. /// /// The matrix is guaranteed to be orthogonal, so some operations can be /// implemented more efficiently than the implementations for `math::Matrix2`. To /// enforce orthogonality at the type level the operations have been restricted /// to a subset of those implemented on `Matrix2`. /// /// ## Example /// /// Suppose we want to rotate a vector that lies in the x-y plane by some /// angle. We can accomplish this quite easily with a two-dimensional rotation /// matrix: /// /// ```no_run /// use cgmath::Rad; /// use cgmath::Vector2; /// use cgmath::{Matrix, Matrix2}; /// use cgmath::{Rotation, Rotation2, Basis2}; /// use cgmath::UlpsEq; /// use std::f64; /// /// // For simplicity, we will rotate the unit x vector to the unit y vector -- /// // so the angle is 90 degrees, or π/2. /// let unit_x: Vector2 = Vector2::unit_x(); /// let rot: Basis2 = Rotation2::from_angle(Rad(0.5f64 * f64::consts::PI)); /// /// // Rotate the vector using the two-dimensional rotation matrix: /// let unit_y = rot.rotate_vector(unit_x); /// /// // Since sin(π/2) may not be exactly zero due to rounding errors, we can /// // use approx's assert_ulps_eq!() feature to show that it is close enough. /// // assert_ulps_eq!(&unit_y, &Vector2::unit_y()); // TODO: Figure out how to use this /// /// // This is exactly equivalent to using the raw matrix itself: /// let unit_y2: Matrix2<_> = rot.into(); /// let unit_y2 = unit_y2 * unit_x; /// assert_eq!(unit_y2, unit_y); /// /// // Note that we can also concatenate rotations: /// let rot_half: Basis2 = Rotation2::from_angle(Rad(0.25f64 * f64::consts::PI)); /// let unit_y3 = (rot_half * rot_half).rotate_vector(unit_x); /// // assert_ulps_eq!(&unit_y3, &unit_y2); // TODO: Figure out how to use this /// ``` #[derive(PartialEq, Copy, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Basis2 { mat: Matrix2, } impl Basis2 { pub fn look_at_stable(dir: Vector2, flip: bool) -> Basis2 { Basis2 { mat: Matrix2::look_at_stable(dir, flip), } } } impl AsRef> for Basis2 { #[inline] fn as_ref(&self) -> &Matrix2 { &self.mat } } impl From> for Matrix2 { #[inline] fn from(b: Basis2) -> Matrix2 { b.mat } } impl iter::Product> for Basis2 { #[inline] fn product>>(iter: I) -> Basis2 { iter.fold(Basis2::one(), Mul::mul) } } impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis2> for Basis2 { #[inline] fn product>>(iter: I) -> Basis2 { iter.fold(Basis2::one(), Mul::mul) } } impl Rotation for Basis2 { type Space = Point2; #[inline] fn look_at(dir: Vector2, up: Vector2) -> Basis2 { Basis2 { mat: Matrix2::look_at(dir, up), } } #[inline] fn between_vectors(a: Vector2, b: Vector2) -> Basis2 { Rotation2::from_angle(Rad::acos(a.dot(b))) } #[inline] fn rotate_vector(&self, vec: Vector2) -> Vector2 { self.mat * vec } // TODO: we know the matrix is orthogonal, so this could be re-written // to be faster #[inline] fn invert(&self) -> Basis2 { Basis2 { mat: self.mat.invert().unwrap(), } } } impl One for Basis2 { #[inline] fn one() -> Basis2 { Basis2 { mat: Matrix2::one(), } } } impl_operator!( Mul > for Basis2 { fn mul(lhs, rhs) -> Basis2 { Basis2 { mat: lhs.mat * rhs.mat } } }); impl approx::AbsDiffEq for Basis2 { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { S::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { Matrix2::abs_diff_eq(&self.mat, &other.mat, epsilon) } } impl approx::RelativeEq for Basis2 { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { Matrix2::relative_eq(&self.mat, &other.mat, epsilon, max_relative) } } impl approx::UlpsEq for Basis2 { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { Matrix2::ulps_eq(&self.mat, &other.mat, epsilon, max_ulps) } } impl Rotation2 for Basis2 { type Scalar = S; fn from_angle>>(theta: A) -> Basis2 { Basis2 { mat: Matrix2::from_angle(theta), } } } impl fmt::Debug for Basis2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Basis2 ")?; <[[S; 2]; 2] as fmt::Debug>::fmt(self.mat.as_ref(), f) } } /// A three-dimensional rotation matrix. /// /// The matrix is guaranteed to be orthogonal, so some operations, specifically /// inversion, can be implemented more efficiently than the implementations for /// `math::Matrix3`. To ensure orthogonality is maintained, the operations have /// been restricted to a subset of those implemented on `Matrix3`. #[derive(PartialEq, Copy, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Basis3 { mat: Matrix3, } impl Basis3 { /// Create a new rotation matrix from a quaternion. #[inline] pub fn from_quaternion(quaternion: &Quaternion) -> Basis3 { Basis3 { mat: quaternion.clone().into(), } } } impl AsRef> for Basis3 { #[inline] fn as_ref(&self) -> &Matrix3 { &self.mat } } impl From> for Matrix3 { #[inline] fn from(b: Basis3) -> Matrix3 { b.mat } } impl From> for Quaternion { #[inline] fn from(b: Basis3) -> Quaternion { b.mat.into() } } impl iter::Product> for Basis3 { #[inline] fn product>>(iter: I) -> Basis3 { iter.fold(Basis3::one(), Mul::mul) } } impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3> for Basis3 { #[inline] fn product>>(iter: I) -> Basis3 { iter.fold(Basis3::one(), Mul::mul) } } impl Rotation for Basis3 { type Space = Point3; #[inline] fn look_at(dir: Vector3, up: Vector3) -> Basis3 { Basis3 { mat: Matrix3::look_to_lh(dir, up), } } #[inline] fn between_vectors(a: Vector3, b: Vector3) -> Basis3 { let q: Quaternion = Rotation::between_vectors(a, b); q.into() } #[inline] fn rotate_vector(&self, vec: Vector3) -> Vector3 { self.mat * vec } // TODO: we know the matrix is orthogonal, so this could be re-written // to be faster #[inline] fn invert(&self) -> Basis3 { Basis3 { mat: self.mat.invert().unwrap(), } } } impl One for Basis3 { #[inline] fn one() -> Basis3 { Basis3 { mat: Matrix3::one(), } } } impl_operator!( Mul > for Basis3 { fn mul(lhs, rhs) -> Basis3 { Basis3 { mat: lhs.mat * rhs.mat } } }); impl approx::AbsDiffEq for Basis3 { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { S::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { Matrix3::abs_diff_eq(&self.mat, &other.mat, epsilon) } } impl approx::RelativeEq for Basis3 { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { Matrix3::relative_eq(&self.mat, &other.mat, epsilon, max_relative) } } impl approx::UlpsEq for Basis3 { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { Matrix3::ulps_eq(&self.mat, &other.mat, epsilon, max_ulps) } } impl Rotation3 for Basis3 { type Scalar = S; fn from_axis_angle>>(axis: Vector3, angle: A) -> Basis3 { Basis3 { mat: Matrix3::from_axis_angle(axis, angle), } } fn from_angle_x>>(theta: A) -> Basis3 { Basis3 { mat: Matrix3::from_angle_x(theta), } } fn from_angle_y>>(theta: A) -> Basis3 { Basis3 { mat: Matrix3::from_angle_y(theta), } } fn from_angle_z>>(theta: A) -> Basis3 { Basis3 { mat: Matrix3::from_angle_z(theta), } } } impl From> for Basis3 where A: Into::Unitless>>, { /// Create a three-dimensional rotation matrix from a set of euler angles. fn from(src: Euler) -> Basis3 { Basis3 { mat: Matrix3::from(src), } } } impl fmt::Debug for Basis3 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Basis3 ")?; <[[S; 3]; 3] as fmt::Debug>::fmt(self.mat.as_ref(), f) } } cgmath-0.18.0/src/structure.rs010064400007650000024000000576711376272520100144440ustar 00000000000000// Copyright 2016 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. //! Generic algebraic structures use num_traits::{cast, Float}; use std::cmp; use std::iter; use std::ops::*; use approx; use angle::Rad; use num::{BaseFloat, BaseNum}; pub use num_traits::{Bounded, One, Zero}; /// An array containing elements of type `Element` pub trait Array where // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: Index::Element>, Self: IndexMut::Element>, { type Element: Copy; /// Get the number of elements in the array type /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Vector3; /// /// assert_eq!(Vector3::::len(), 3); /// ``` fn len() -> usize; /// Construct a vector from a single value, replicating it. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Vector3; /// /// assert_eq!(Vector3::from_value(1), /// Vector3::new(1, 1, 1)); /// ``` fn from_value(value: Self::Element) -> Self; /// Get the pointer to the first element of the array. #[inline] fn as_ptr(&self) -> *const Self::Element { &self[0] } /// Get a mutable pointer to the first element of the array. #[inline] fn as_mut_ptr(&mut self) -> *mut Self::Element { &mut self[0] } /// Swap the elements at indices `i` and `j` in-place. #[inline] fn swap_elements(&mut self, i: usize, j: usize) { use std::ptr; // Yeah, ok borrow checker – I know what I'm doing here unsafe { ptr::swap(&mut self[i], &mut self[j]) }; } /// The sum of the elements of the array. fn sum(self) -> Self::Element where Self::Element: Add::Element>; /// The product of the elements of the array. fn product(self) -> Self::Element where Self::Element: Mul::Element>; /// Whether all elements of the array are finite fn is_finite(&self) -> bool where Self::Element: Float; } /// Element-wise arithmetic operations. These are supplied for pragmatic /// reasons, but will usually fall outside of traditional algebraic properties. pub trait ElementWise { fn add_element_wise(self, rhs: Rhs) -> Self; fn sub_element_wise(self, rhs: Rhs) -> Self; fn mul_element_wise(self, rhs: Rhs) -> Self; fn div_element_wise(self, rhs: Rhs) -> Self; fn rem_element_wise(self, rhs: Rhs) -> Self; fn add_assign_element_wise(&mut self, rhs: Rhs); fn sub_assign_element_wise(&mut self, rhs: Rhs); fn mul_assign_element_wise(&mut self, rhs: Rhs); fn div_assign_element_wise(&mut self, rhs: Rhs); fn rem_assign_element_wise(&mut self, rhs: Rhs); } /// Vectors that can be [added](http://mathworld.wolfram.com/VectorAddition.html) /// together and [multiplied](https://en.wikipedia.org/wiki/Scalar_multiplication) /// by scalars. /// /// Examples include vectors, matrices, and quaternions. /// /// # Required operators /// /// ## Vector addition /// /// Vectors can be added, subtracted, or negated via the following traits: /// /// - `Add` /// - `Sub` /// - `Neg` /// /// ```rust /// use cgmath::Vector3; /// /// let velocity0 = Vector3::new(1, 2, 0); /// let velocity1 = Vector3::new(1, 1, 0); /// /// let total_velocity = velocity0 + velocity1; /// let velocity_diff = velocity1 - velocity0; /// let reversed_velocity0 = -velocity0; /// ``` /// /// Vector spaces are also required to implement the additive identity trait, /// `Zero`. Adding this to another vector should have no effect: /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Vector2; /// /// let v = Vector2::new(1, 2); /// assert_eq!(v + Vector2::zero(), v); /// ``` /// /// ## Scalar multiplication /// /// Vectors can be multiplied or divided by their associated scalars via the /// following traits: /// /// - `Mul` /// - `Div` /// - `Rem` /// /// ```rust /// use cgmath::Vector2; /// /// let translation = Vector2::new(3.0, 4.0); /// let scale_factor = 2.0; /// /// let upscaled_translation = translation * scale_factor; /// let downscaled_translation = translation / scale_factor; /// ``` pub trait VectorSpace: Copy + Clone where Self: Zero, Self: Add, Self: Sub, Self: iter::Sum, // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: Mul<::Scalar, Output = Self>, Self: Div<::Scalar, Output = Self>, Self: Rem<::Scalar, Output = Self>, { /// The associated scalar. type Scalar: BaseNum; /// Returns the result of linearly interpolating the vector /// towards `other` by the specified amount. #[inline] fn lerp(self, other: Self, amount: Self::Scalar) -> Self { self + ((other - self) * amount) } } /// A type with a distance function between values. /// /// Examples are vectors, points, and quaternions. pub trait MetricSpace: Sized { /// The metric to be returned by the `distance` function. type Metric; /// Returns the squared distance. /// /// This does not perform an expensive square root operation like in /// `MetricSpace::distance` method, and so can be used to compare distances /// more efficiently. fn distance2(self, other: Self) -> Self::Metric; /// The distance between two values. fn distance(self, other: Self) -> Self::Metric where Self::Metric: Float, { Float::sqrt(Self::distance2(self, other)) } } /// Vectors that also have a [dot](https://en.wikipedia.org/wiki/Dot_product) /// (or [inner](https://en.wikipedia.org/wiki/Inner_product_space)) product. /// /// The dot product allows for the definition of other useful operations, like /// finding the magnitude of a vector or normalizing it. /// /// Examples include vectors and quaternions. pub trait InnerSpace: VectorSpace where // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: MetricSpace::Scalar>, { /// Vector dot (or inner) product. fn dot(self, other: Self) -> Self::Scalar; /// Returns `true` if the vector is perpendicular (at right angles) to the /// other vector. fn is_perpendicular(self, other: Self) -> bool where Self::Scalar: approx::UlpsEq, { ulps_eq!(Self::dot(self, other), &Self::Scalar::zero()) } /// Returns the squared magnitude. /// /// This does not perform an expensive square root operation like in /// `InnerSpace::magnitude` method, and so can be used to compare magnitudes /// more efficiently. #[inline] fn magnitude2(self) -> Self::Scalar { Self::dot(self, self) } /// Returns the angle between two vectors in radians. fn angle(self, other: Self) -> Rad where Self::Scalar: BaseFloat, { Rad::acos(Self::dot(self, other) / (self.magnitude() * other.magnitude())) } /// Returns the /// [vector projection](https://en.wikipedia.org/wiki/Vector_projection) /// of the current inner space projected onto the supplied argument. #[inline] fn project_on(self, other: Self) -> Self { other * (self.dot(other) / other.magnitude2()) } /// The distance from the tail to the tip of the vector. #[inline] fn magnitude(self) -> Self::Scalar where Self::Scalar: Float, { Float::sqrt(self.magnitude2()) } /// Returns a vector with the same direction, but with a magnitude of `1`. #[inline] fn normalize(self) -> Self where Self::Scalar: Float, { self.normalize_to(Self::Scalar::one()) } /// Returns a vector with the same direction and a given magnitude. #[inline] fn normalize_to(self, magnitude: Self::Scalar) -> Self where Self::Scalar: Float, { self * (magnitude / self.magnitude()) } } /// Points in a [Euclidean space](https://en.wikipedia.org/wiki/Euclidean_space) /// with an associated space of displacement vectors. /// /// # Point-Vector distinction /// /// `cgmath` distinguishes between points and vectors in the following way: /// /// - Points are _locations_ relative to an origin /// - Vectors are _displacements_ between those points /// /// For example, to find the midpoint between two points, you can write the /// following: /// /// ```rust /// use cgmath::Point3; /// /// let p0 = Point3::new(1.0, 2.0, 3.0); /// let p1 = Point3::new(-3.0, 1.0, 2.0); /// let midpoint: Point3 = p0 + (p1 - p0) * 0.5; /// ``` /// /// Breaking the expression up, and adding explicit types makes it clearer /// to see what is going on: /// /// ```rust /// # use cgmath::{Point3, Vector3}; /// # /// # let p0 = Point3::new(1.0, 2.0, 3.0); /// # let p1 = Point3::new(-3.0, 1.0, 2.0); /// # /// let dv: Vector3 = p1 - p0; /// let half_dv: Vector3 = dv * 0.5; /// let midpoint: Point3 = p0 + half_dv; /// ``` /// /// ## Converting between points and vectors /// /// Points can be converted to and from displacement vectors using the /// `EuclideanSpace::{from_vec, to_vec}` methods. Note that under the hood these /// are implemented as inlined a type conversion, so should not have any /// performance implications. /// /// ## References /// /// - [CGAL 4.7 - 2D and 3D Linear Geometry Kernel: 3.1 Points and Vectors](http://doc.cgal.org/latest/Kernel_23/index.html#Kernel_23PointsandVectors) /// - [What is the difference between a point and a vector](http://math.stackexchange.com/q/645827) /// pub trait EuclideanSpace: Copy + Clone where // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: Array::Scalar>, Self: Add<::Diff, Output = Self>, Self: Sub<::Diff, Output = Self>, Self: Sub::Diff>, Self: Mul<::Scalar, Output = Self>, Self: Div<::Scalar, Output = Self>, Self: Rem<::Scalar, Output = Self>, { /// The associated scalar over which the space is defined. /// /// Due to the equality constraints demanded by `Self::Diff`, this is effectively just an /// alias to `Self::Diff::Scalar`. type Scalar: BaseNum; /// The associated space of displacement vectors. type Diff: VectorSpace; /// The point at the origin of the Euclidean space. fn origin() -> Self; /// Convert a displacement vector to a point. /// /// This can be considered equivalent to the addition of the displacement /// vector `v` to to `Self::origin()`. fn from_vec(v: Self::Diff) -> Self; /// Convert a point to a displacement vector. /// /// This can be seen as equivalent to the displacement vector from /// `Self::origin()` to `self`. fn to_vec(self) -> Self::Diff; /// Returns the middle point between two other points. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Point3; /// /// let p = Point3::midpoint( /// Point3::new(1.0, 2.0, 3.0), /// Point3::new(3.0, 1.0, 2.0), /// ); /// ``` #[inline] fn midpoint(self, other: Self) -> Self { self + (other - self) / cast(2).unwrap() } /// Returns the average position of all points in the slice. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Point2; /// /// let triangle = [ /// Point2::new(1.0, 1.0), /// Point2::new(2.0, 3.0), /// Point2::new(3.0, 1.0), /// ]; /// /// let centroid = Point2::centroid(&triangle); /// ``` #[inline] fn centroid(points: &[Self]) -> Self { let total_displacement = points .iter() .fold(Self::Diff::zero(), |acc, p| acc + p.to_vec()); Self::from_vec(total_displacement / cast(points.len()).unwrap()) } /// This is a weird one, but its useful for plane calculations. fn dot(self, v: Self::Diff) -> Self::Scalar; } /// A column-major matrix of arbitrary dimensions. /// /// Because this is constrained to the `VectorSpace` trait, this means that /// following operators are required to be implemented: /// /// Matrix addition: /// /// - `Add` /// - `Sub` /// - `Neg` /// /// Scalar multiplication: /// /// - `Mul` /// - `Div` /// - `Rem` /// /// Note that matrix multiplication is not required for implementors of this /// trait. This is due to the complexities of implementing these operators with /// Rust's current type system. For the multiplication of square matrices, /// see `SquareMatrix`. pub trait Matrix: VectorSpace where Self::Scalar: Float, // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: Index::Column>, Self: IndexMut::Column>, { /// The row vector of the matrix. type Row: VectorSpace + Array; /// The column vector of the matrix. type Column: VectorSpace + Array; /// The result of transposing the matrix type Transpose: Matrix; /// Get the pointer to the first element of the array. #[inline] fn as_ptr(&self) -> *const Self::Scalar { &self[0][0] } /// Get a mutable pointer to the first element of the array. #[inline] fn as_mut_ptr(&mut self) -> *mut Self::Scalar { &mut self[0][0] } /// Replace a column in the array. #[inline] fn replace_col(&mut self, c: usize, src: Self::Column) -> Self::Column { use std::mem; mem::replace(&mut self[c], src) } /// Get a row from this matrix by-value. fn row(&self, r: usize) -> Self::Row; /// Swap two rows of this array. fn swap_rows(&mut self, a: usize, b: usize); /// Swap two columns of this array. fn swap_columns(&mut self, a: usize, b: usize); /// Swap the values at index `a` and `b` fn swap_elements(&mut self, a: (usize, usize), b: (usize, usize)); /// Transpose this matrix, returning a new matrix. fn transpose(&self) -> Self::Transpose; } /// A column-major major matrix where the rows and column vectors are of the same dimensions. pub trait SquareMatrix where Self::Scalar: Float, Self: One, Self: iter::Product, Self: Matrix< // FIXME: Can be cleaned up once equality constraints in where clauses are implemented Column = ::ColumnRow, Row = ::ColumnRow, Transpose = Self, >, Self: Mul<::ColumnRow, Output = ::ColumnRow>, Self: Mul, { // FIXME: Will not be needed once equality constraints in where clauses are implemented /// The row/column vector of the matrix. /// /// This is used to constrain the column and rows to be of the same type in lieu of equality /// constraints being implemented for `where` clauses. Once those are added, this type will /// likely go away. type ColumnRow: VectorSpace + Array; /// Create a new diagonal matrix using the supplied value. fn from_value(value: Self::Scalar) -> Self; /// Create a matrix from a non-uniform scale fn from_diagonal(diagonal: Self::ColumnRow) -> Self; /// The [identity matrix]. Multiplying this matrix with another should have /// no effect. /// /// Note that this is exactly the same as `One::one`. The term 'identity /// matrix' is more common though, so we provide this method as an /// alternative. /// /// [identity matrix]: https://en.wikipedia.org/wiki/Identity_matrix #[inline] fn identity() -> Self { Self::one() } /// Transpose this matrix in-place. fn transpose_self(&mut self); /// Take the determinant of this matrix. fn determinant(&self) -> Self::Scalar; /// Return a vector containing the diagonal of this matrix. fn diagonal(&self) -> Self::ColumnRow; /// Return the trace of this matrix. That is, the sum of the diagonal. #[inline] fn trace(&self) -> Self::Scalar { self.diagonal().sum() } /// Invert this matrix, returning a new matrix. `m.mul_m(m.invert())` is /// the identity matrix. Returns `None` if this matrix is not invertible /// (has a determinant of zero). fn invert(&self) -> Option; /// Test if this matrix is invertible. #[inline] fn is_invertible(&self) -> bool where Self::Scalar: approx::UlpsEq, { ulps_ne!(self.determinant(), &Self::Scalar::zero()) } /// Test if this matrix is the identity matrix. That is, it is diagonal /// and every element in the diagonal is one. #[inline] fn is_identity(&self) -> bool where Self: approx::UlpsEq, { ulps_eq!(self, &Self::identity()) } /// Test if this is a diagonal matrix. That is, every element outside of /// the diagonal is 0. fn is_diagonal(&self) -> bool; /// Test if this matrix is symmetric. That is, it is equal to its /// transpose. fn is_symmetric(&self) -> bool; } /// Angles and their associated trigonometric functions. /// /// Typed angles allow for the writing of self-documenting code that makes it /// clear when semantic violations have occured - for example, adding degrees to /// radians, or adding a number to an angle. /// pub trait Angle where Self: Copy + Clone, Self: PartialEq + cmp::PartialOrd, // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: approx::AbsDiffEq::Unitless>, Self: approx::RelativeEq::Unitless>, Self: approx::UlpsEq::Unitless>, Self: Zero, Self: Neg, Self: Add, Self: Sub, Self: Rem, Self: Mul<::Unitless, Output = Self>, Self: Div::Unitless>, Self: Div<::Unitless, Output = Self>, Self: iter::Sum, { type Unitless: BaseFloat; /// Return the angle, normalized to the range `[0, full_turn)`. #[inline] fn normalize(self) -> Self { let rem = self % Self::full_turn(); if rem < Self::zero() { rem + Self::full_turn() } else { rem } } /// Return the angle, normalized to the range `[-turn_div_2, turn_div_2)`. #[inline] fn normalize_signed(self) -> Self { let rem = self.normalize(); if Self::turn_div_2() < rem { rem - Self::full_turn() } else { rem } } /// Return the angle rotated by half a turn. #[inline] fn opposite(self) -> Self { Self::normalize(self + Self::turn_div_2()) } /// Returns the interior bisector of the two angles. #[inline] fn bisect(self, other: Self) -> Self { let half = cast(0.5f64).unwrap(); Self::normalize((self - other) * half + self) } /// A full rotation. fn full_turn() -> Self; /// Half of a full rotation. #[inline] fn turn_div_2() -> Self { let factor: Self::Unitless = cast(2).unwrap(); Self::full_turn() / factor } /// A third of a full rotation. #[inline] fn turn_div_3() -> Self { let factor: Self::Unitless = cast(3).unwrap(); Self::full_turn() / factor } /// A quarter of a full rotation. #[inline] fn turn_div_4() -> Self { let factor: Self::Unitless = cast(4).unwrap(); Self::full_turn() / factor } /// A sixth of a full rotation. #[inline] fn turn_div_6() -> Self { let factor: Self::Unitless = cast(6).unwrap(); Self::full_turn() / factor } /// Compute the sine of the angle, returning a unitless ratio. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle = Rad(35.0); /// let ratio: f32 = Rad::sin(angle); /// ``` fn sin(self) -> Self::Unitless; /// Compute the cosine of the angle, returning a unitless ratio. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle = Rad(35.0); /// let ratio: f32 = Rad::cos(angle); /// ``` fn cos(self) -> Self::Unitless; /// Compute the tangent of the angle, returning a unitless ratio. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle = Rad(35.0); /// let ratio: f32 = Rad::tan(angle); /// ``` fn tan(self) -> Self::Unitless; /// Compute the sine and cosine of the angle, returning the result as a /// pair. /// /// This does not have any performance benefits, but calculating both the /// sine and cosine of a single angle is a common operation. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle = Rad(35.0); /// let (s, c) = Rad::sin_cos(angle); /// ``` fn sin_cos(self) -> (Self::Unitless, Self::Unitless); /// Compute the cosecant of the angle. /// /// This is the same as computing the reciprocal of `Self::sin`. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle = Rad(35.0); /// let ratio: f32 = Rad::csc(angle); /// ``` #[inline] fn csc(self) -> Self::Unitless { Self::sin(self).recip() } /// Compute the cotangent of the angle. /// /// This is the same as computing the reciprocal of `Self::tan`. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle = Rad(35.0); /// let ratio: f32 = Rad::cot(angle); /// ``` #[inline] fn cot(self) -> Self::Unitless { Self::tan(self).recip() } /// Compute the secant of the angle. /// /// This is the same as computing the reciprocal of `Self::cos`. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle = Rad(35.0); /// let ratio: f32 = Rad::sec(angle); /// ``` #[inline] fn sec(self) -> Self::Unitless { Self::cos(self).recip() } /// Compute the arcsine of the ratio, returning the resulting angle. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle: Rad = Rad::asin(0.5); /// ``` fn asin(ratio: Self::Unitless) -> Self; /// Compute the arccosine of the ratio, returning the resulting angle. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle: Rad = Rad::acos(0.5); /// ``` fn acos(ratio: Self::Unitless) -> Self; /// Compute the arctangent of the ratio, returning the resulting angle. /// /// ```rust /// use cgmath::prelude::*; /// use cgmath::Rad; /// /// let angle: Rad = Rad::atan(0.5); /// ``` fn atan(ratio: Self::Unitless) -> Self; fn atan2(a: Self::Unitless, b: Self::Unitless) -> Self; } cgmath-0.18.0/src/transform.rs010064400007650000024000000323741376272520100144100ustar 00000000000000// Copyright 2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use structure::*; use approx; use matrix::{Matrix2, Matrix3, Matrix4}; use num::{BaseFloat, BaseNum}; use point::{Point2, Point3}; use rotation::*; use vector::{Vector2, Vector3}; use std::ops::Mul; /// A trait representing an [affine /// transformation](https://en.wikipedia.org/wiki/Affine_transformation) that /// can be applied to points or vectors. An affine transformation is one which pub trait Transform: Sized + One { /// Create a transformation that rotates a vector to look at `center` from /// `eye`, using `up` for orientation. #[deprecated = "Use look_at_rh or look_at_lh"] fn look_at(eye: P, center: P, up: P::Diff) -> Self; /// Create a transformation that rotates a vector to look at `center` from /// `eye`, using `up` for orientation. fn look_at_rh(eye: P, center: P, up: P::Diff) -> Self; /// Create a transformation that rotates a vector to look at `center` from /// `eye`, using `up` for orientation. fn look_at_lh(eye: P, center: P, up: P::Diff) -> Self; /// Transform a vector using this transform. fn transform_vector(&self, vec: P::Diff) -> P::Diff; /// Inverse transform a vector using this transform fn inverse_transform_vector(&self, vec: P::Diff) -> Option { self.inverse_transform() .and_then(|inverse| Some(inverse.transform_vector(vec))) } /// Transform a point using this transform. fn transform_point(&self, point: P) -> P; /// Combine this transform with another, yielding a new transformation /// which has the effects of both. fn concat(&self, other: &Self) -> Self; /// Create a transform that "un-does" this one. fn inverse_transform(&self) -> Option; /// Combine this transform with another, in-place. #[inline] fn concat_self(&mut self, other: &Self) { *self = Self::concat(self, other); } } /// A generic transformation consisting of a rotation, /// displacement vector and scale amount. #[derive(Copy, Clone, Debug, PartialEq)] pub struct Decomposed { pub scale: V::Scalar, pub rot: R, pub disp: V, } impl> One for Decomposed where P::Scalar: BaseFloat, { fn one() -> Self { Decomposed { scale: P::Scalar::one(), rot: R::one(), disp: P::Diff::zero(), } } } impl> Mul for Decomposed where P::Scalar: BaseFloat, P::Diff: VectorSpace, { type Output = Self; /// Multiplies the two transforms together. /// The result should be as if the two transforms were converted /// to matrices, then multiplied, then converted back with /// a (currently nonexistent) function that tries to convert /// a matrix into a `Decomposed`. fn mul(self, rhs: Decomposed) -> Self::Output { self.concat(&rhs) } } impl> Transform

for Decomposed where P::Scalar: BaseFloat, P::Diff: VectorSpace, { #[inline] fn look_at(eye: P, center: P, up: P::Diff) -> Decomposed { let rot = R::look_at(center - eye, up); let disp = rot.rotate_vector(P::origin() - eye); Decomposed { scale: P::Scalar::one(), rot: rot, disp: disp, } } #[inline] fn look_at_lh(eye: P, center: P, up: P::Diff) -> Decomposed { let rot = R::look_at(center - eye, up); let disp = rot.rotate_vector(P::origin() - eye); Decomposed { scale: P::Scalar::one(), rot: rot, disp: disp, } } #[inline] fn look_at_rh(eye: P, center: P, up: P::Diff) -> Decomposed { let rot = R::look_at(eye - center, up); let disp = rot.rotate_vector(P::origin() - eye); Decomposed { scale: P::Scalar::one(), rot: rot, disp: disp, } } #[inline] fn transform_vector(&self, vec: P::Diff) -> P::Diff { self.rot.rotate_vector(vec * self.scale) } #[inline] fn inverse_transform_vector(&self, vec: P::Diff) -> Option { if ulps_eq!(self.scale, &P::Scalar::zero()) { None } else { Some(self.rot.invert().rotate_vector(vec / self.scale)) } } #[inline] fn transform_point(&self, point: P) -> P { self.rot.rotate_point(point * self.scale) + self.disp } fn concat(&self, other: &Decomposed) -> Decomposed { Decomposed { scale: self.scale * other.scale, rot: self.rot * other.rot, disp: self.rot.rotate_vector(other.disp * self.scale) + self.disp, } } fn inverse_transform(&self) -> Option> { if ulps_eq!(self.scale, &P::Scalar::zero()) { None } else { let s = P::Scalar::one() / self.scale; let r = self.rot.invert(); let d = r.rotate_vector(self.disp.clone()) * -s; Some(Decomposed { scale: s, rot: r, disp: d, }) } } } pub trait Transform2: Transform::Scalar>> + Into::Scalar>> { type Scalar: BaseNum; } pub trait Transform3: Transform::Scalar>> + Into::Scalar>> { type Scalar: BaseNum; } impl> From, R>> for Matrix3 { fn from(dec: Decomposed, R>) -> Matrix3 { let m: Matrix2<_> = dec.rot.into(); let mut m: Matrix3<_> = (&m * dec.scale).into(); m.z = dec.disp.extend(S::one()); m } } impl> From, R>> for Matrix4 { fn from(dec: Decomposed, R>) -> Matrix4 { let m: Matrix3<_> = dec.rot.into(); let mut m: Matrix4<_> = (&m * dec.scale).into(); m.w = dec.disp.extend(S::one()); m } } impl> Transform2 for Decomposed, R> { type Scalar = S; } impl> Transform3 for Decomposed, R> { type Scalar = S; } impl approx::AbsDiffEq for Decomposed where S: approx::AbsDiffEq, S::Scalar: approx::AbsDiffEq, R: approx::AbsDiffEq, { type Epsilon = E; #[inline] fn default_epsilon() -> E { E::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: E) -> bool { S::Scalar::abs_diff_eq(&self.scale, &other.scale, epsilon) && R::abs_diff_eq(&self.rot, &other.rot, epsilon) && S::abs_diff_eq(&self.disp, &other.disp, epsilon) } } impl approx::RelativeEq for Decomposed where S: approx::RelativeEq, S::Scalar: approx::RelativeEq, R: approx::RelativeEq, { #[inline] fn default_max_relative() -> E { E::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: E, max_relative: E) -> bool { S::Scalar::relative_eq(&self.scale, &other.scale, epsilon, max_relative) && R::relative_eq(&self.rot, &other.rot, epsilon, max_relative) && S::relative_eq(&self.disp, &other.disp, epsilon, max_relative) } } impl approx::UlpsEq for Decomposed where S: approx::UlpsEq, S::Scalar: approx::UlpsEq, R: approx::UlpsEq, { #[inline] fn default_max_ulps() -> u32 { E::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: E, max_ulps: u32) -> bool { S::Scalar::ulps_eq(&self.scale, &other.scale, epsilon, max_ulps) && R::ulps_eq(&self.rot, &other.rot, epsilon, max_ulps) && S::ulps_eq(&self.disp, &other.disp, epsilon, max_ulps) } } #[cfg(feature = "serde")] #[doc(hidden)] mod serde_ser { use super::Decomposed; use serde::ser::SerializeStruct; use serde::{self, Serialize}; use structure::VectorSpace; impl Serialize for Decomposed where V: Serialize + VectorSpace, V::Scalar: Serialize, R: Serialize, { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { let mut struc = serializer.serialize_struct("Decomposed", 3)?; struc.serialize_field("scale", &self.scale)?; struc.serialize_field("rot", &self.rot)?; struc.serialize_field("disp", &self.disp)?; struc.end() } } } #[cfg(feature = "serde")] #[doc(hidden)] mod serde_de { use super::Decomposed; use serde::{self, Deserialize}; use std::fmt; use std::marker::PhantomData; use structure::VectorSpace; enum DecomposedField { Scale, Rot, Disp, } impl<'a> Deserialize<'a> for DecomposedField { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'a>, { struct DecomposedFieldVisitor; impl<'b> serde::de::Visitor<'b> for DecomposedFieldVisitor { type Value = DecomposedField; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("`scale`, `rot` or `disp`") } fn visit_str(self, value: &str) -> Result where E: serde::de::Error, { match value { "scale" => Ok(DecomposedField::Scale), "rot" => Ok(DecomposedField::Rot), "disp" => Ok(DecomposedField::Disp), _ => Err(serde::de::Error::custom("expected scale, rot or disp")), } } } deserializer.deserialize_str(DecomposedFieldVisitor) } } impl<'a, S: VectorSpace, R> Deserialize<'a> for Decomposed where S: Deserialize<'a>, S::Scalar: Deserialize<'a>, R: Deserialize<'a>, { fn deserialize(deserializer: D) -> Result, D::Error> where D: serde::de::Deserializer<'a>, { const FIELDS: &'static [&'static str] = &["scale", "rot", "disp"]; deserializer.deserialize_struct("Decomposed", FIELDS, DecomposedVisitor(PhantomData)) } } struct DecomposedVisitor(PhantomData<(S, R)>); impl<'a, S: VectorSpace, R> serde::de::Visitor<'a> for DecomposedVisitor where S: Deserialize<'a>, S::Scalar: Deserialize<'a>, R: Deserialize<'a>, { type Value = Decomposed; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("`scale`, `rot` and `disp` fields") } fn visit_map(self, mut visitor: V) -> Result, V::Error> where V: serde::de::MapAccess<'a>, { let mut scale = None; let mut rot = None; let mut disp = None; while let Some(key) = visitor.next_key()? { match key { DecomposedField::Scale => { scale = Some(visitor.next_value()?); } DecomposedField::Rot => { rot = Some(visitor.next_value()?); } DecomposedField::Disp => { disp = Some(visitor.next_value()?); } } } let scale = match scale { Some(scale) => scale, None => return Err(serde::de::Error::missing_field("scale")), }; let rot = match rot { Some(rot) => rot, None => return Err(serde::de::Error::missing_field("rot")), }; let disp = match disp { Some(disp) => disp, None => return Err(serde::de::Error::missing_field("disp")), }; Ok(Decomposed { scale: scale, rot: rot, disp: disp, }) } } } cgmath-0.18.0/src/vector.rs010064400007650000024000000753101376272520100136740ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use num_traits::{Bounded, Float, NumCast}; #[cfg(feature = "rand")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use std::fmt; use std::iter; use std::mem; use std::ops::*; use structure::*; use angle::Rad; use approx; use num::{BaseFloat, BaseNum}; #[cfg(feature = "mint")] use mint; /// A 1-dimensional vector. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(PartialEq, Eq, Copy, Clone, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Vector1 { /// The x component of the vector. pub x: S, } /// A 2-dimensional vector. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(PartialEq, Eq, Copy, Clone, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Vector2 { /// The x component of the vector. pub x: S, /// The y component of the vector. pub y: S, } /// A 3-dimensional vector. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(PartialEq, Eq, Copy, Clone, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Vector3 { /// The x component of the vector. pub x: S, /// The y component of the vector. pub y: S, /// The z component of the vector. pub z: S, } /// A 4-dimensional vector. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(PartialEq, Eq, Copy, Clone, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Vector4 { /// The x component of the vector. pub x: S, /// The y component of the vector. pub y: S, /// The z component of the vector. pub z: S, /// The w component of the vector. pub w: S, } // Utility macro for generating associated functions for the vectors macro_rules! impl_vector { ($VectorN:ident { $($field:ident),+ }, $n:expr, $constructor:ident) => { impl $VectorN { /// Construct a new vector, using the provided values. #[inline] pub const fn new($($field: S),+) -> $VectorN { $VectorN { $($field: $field),+ } } /// Perform the given operation on each field in the vector, returning a new point /// constructed from the operations. #[inline] pub fn map(self, mut f: F) -> $VectorN where F: FnMut(S) -> U { $VectorN { $($field: f(self.$field)),+ } } /// Construct a new vector where each component is the result of /// applying the given operation to each pair of components of the /// given vectors. #[inline] pub fn zip(self, v2: $VectorN, mut f: F) -> $VectorN where F: FnMut(S, S2) -> S3 { $VectorN { $($field: f(self.$field, v2.$field)),+ } } } /// The short constructor. #[inline] pub const fn $constructor($($field: S),+) -> $VectorN { $VectorN::new($($field),+) } impl $VectorN { /// Component-wise casting to another type. #[inline] pub fn cast(&self) -> Option<$VectorN> { $( let $field = match NumCast::from(self.$field) { Some(field) => field, None => return None }; )+ Some($VectorN { $($field),+ }) } } impl MetricSpace for $VectorN { type Metric = S; #[inline] fn distance2(self, other: Self) -> S { (other - self).magnitude2() } } impl Array for $VectorN { type Element = S; #[inline] fn len() -> usize { $n } #[inline] fn from_value(scalar: S) -> $VectorN { $VectorN { $($field: scalar),+ } } #[inline] fn sum(self) -> S where S: Add { fold_array!(add, { $(self.$field),+ }) } #[inline] fn product(self) -> S where S: Mul { fold_array!(mul, { $(self.$field),+ }) } fn is_finite(&self) -> bool where S: Float { $(self.$field.is_finite())&&+ } } impl Zero for $VectorN { #[inline] fn zero() -> $VectorN { $VectorN::from_value(S::zero()) } #[inline] fn is_zero(&self) -> bool { *self == $VectorN::zero() } } impl iter::Sum<$VectorN> for $VectorN { #[inline] fn sum>>(iter: I) -> $VectorN { iter.fold($VectorN::zero(), Add::add) } } impl<'a, S: 'a + BaseNum> iter::Sum<&'a $VectorN> for $VectorN { #[inline] fn sum>>(iter: I) -> $VectorN { iter.fold($VectorN::zero(), Add::add) } } impl VectorSpace for $VectorN { type Scalar = S; } impl> Neg for $VectorN { type Output = $VectorN; #[inline] default_fn!( neg(self) -> $VectorN { $VectorN::new($(-self.$field),+) } ); } impl approx::AbsDiffEq for $VectorN { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { S::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { $(S::abs_diff_eq(&self.$field, &other.$field, epsilon))&&+ } } impl approx::RelativeEq for $VectorN { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { $(S::relative_eq(&self.$field, &other.$field, epsilon, max_relative))&&+ } } impl approx::UlpsEq for $VectorN { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { $(S::ulps_eq(&self.$field, &other.$field, epsilon, max_ulps))&&+ } } #[cfg(feature = "rand")] impl Distribution<$VectorN> for Standard where Standard: Distribution, S: BaseFloat { #[inline] fn sample(&self, rng: &mut R) -> $VectorN { $VectorN { $($field: rng.gen()),+ } } } impl Bounded for $VectorN { #[inline] fn min_value() -> $VectorN { $VectorN { $($field: S::min_value()),+ } } #[inline] fn max_value() -> $VectorN { $VectorN { $($field: S::max_value()),+ } } } impl_operator!( Add<$VectorN > for $VectorN { fn add(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field + rhs.$field),+) } }); impl_assignment_operator!( AddAssign<$VectorN > for $VectorN { fn add_assign(&mut self, other) { $(self.$field += other.$field);+ } }); impl_operator!( Sub<$VectorN > for $VectorN { fn sub(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field - rhs.$field),+) } }); impl_assignment_operator!( SubAssign<$VectorN > for $VectorN { fn sub_assign(&mut self, other) { $(self.$field -= other.$field);+ } }); impl_operator!( Mul for $VectorN { fn mul(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field * scalar),+) } }); impl_assignment_operator!( MulAssign for $VectorN { fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ } }); impl_operator!( Div for $VectorN { fn div(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field / scalar),+) } }); impl_assignment_operator!( DivAssign for $VectorN { fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ } }); impl_operator!( Rem for $VectorN { fn rem(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field % scalar),+) } }); impl_assignment_operator!( RemAssign for $VectorN { fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ } }); impl ElementWise for $VectorN { #[inline] default_fn!( add_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field + rhs.$field),+) } ); #[inline] default_fn!( sub_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field - rhs.$field),+) } ); #[inline] default_fn!( mul_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field * rhs.$field),+) } ); #[inline] default_fn!( div_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field / rhs.$field),+) } ); #[inline] fn rem_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field % rhs.$field),+) } #[inline] default_fn!( add_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field += rhs.$field);+ } ); #[inline] default_fn!( sub_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field -= rhs.$field);+ } ); #[inline] default_fn!( mul_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field *= rhs.$field);+ } ); #[inline] default_fn!( div_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field /= rhs.$field);+ } ); #[inline] fn rem_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field %= rhs.$field);+ } } impl ElementWise for $VectorN { #[inline] default_fn!( add_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field + rhs),+) } ); #[inline] default_fn!( sub_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field - rhs),+) } ); #[inline] default_fn!( mul_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field * rhs),+) } ); #[inline] default_fn!( div_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field / rhs),+) } ); #[inline] fn rem_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field % rhs),+) } #[inline] default_fn!( add_assign_element_wise(&mut self, rhs: S) { $(self.$field += rhs);+ } ); #[inline] default_fn!( sub_assign_element_wise(&mut self, rhs: S) { $(self.$field -= rhs);+ } ); #[inline] default_fn!( mul_assign_element_wise(&mut self, rhs: S) { $(self.$field *= rhs);+ } ); #[inline] default_fn!( div_assign_element_wise(&mut self, rhs: S) { $(self.$field /= rhs);+ } ); #[inline] fn rem_assign_element_wise(&mut self, rhs: S) { $(self.$field %= rhs);+ } } impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); impl_index_operators!($VectorN, $n, S, usize); impl_index_operators!($VectorN, $n, [S], Range); impl_index_operators!($VectorN, $n, [S], RangeTo); impl_index_operators!($VectorN, $n, [S], RangeFrom); impl_index_operators!($VectorN, $n, [S], RangeFull); } } macro_rules! impl_scalar_ops { ($VectorN:ident<$S:ident> { $($field:ident),+ }) => { impl_operator!(Mul<$VectorN<$S>> for $S { fn mul(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar * vector.$field),+) } }); impl_operator!(Div<$VectorN<$S>> for $S { fn div(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar / vector.$field),+) } }); impl_operator!(Rem<$VectorN<$S>> for $S { fn rem(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar % vector.$field),+) } }); }; } impl_vector!(Vector1 { x }, 1, vec1); impl_vector!(Vector2 { x, y }, 2, vec2); impl_vector!(Vector3 { x, y, z }, 3, vec3); impl_vector!(Vector4 { x, y, z, w }, 4, vec4); impl_fixed_array_conversions!(Vector1 { x: 0 }, 1); impl_fixed_array_conversions!(Vector2 { x: 0, y: 1 }, 2); impl_fixed_array_conversions!(Vector3 { x: 0, y: 1, z: 2 }, 3); impl_fixed_array_conversions!(Vector4 { x: 0, y: 1, z: 2, w: 3 }, 4); impl_tuple_conversions!(Vector1 { x }, (S,)); impl_tuple_conversions!(Vector2 { x, y }, (S, S)); impl_tuple_conversions!(Vector3 { x, y, z }, (S, S, S)); impl_tuple_conversions!(Vector4 { x, y, z, w }, (S, S, S, S)); impl Vector1 { /// A unit vector in the `x` direction. #[inline] pub fn unit_x() -> Vector1 { Vector1::new(S::one()) } impl_swizzle_functions!(Vector1, Vector2, Vector3, Vector4, S, x); } impl Vector2 { /// A unit vector in the `x` direction. #[inline] pub fn unit_x() -> Vector2 { Vector2::new(S::one(), S::zero()) } /// A unit vector in the `y` direction. #[inline] pub fn unit_y() -> Vector2 { Vector2::new(S::zero(), S::one()) } /// The perpendicular dot product of the vector and `other`. #[inline] pub fn perp_dot(self, other: Vector2) -> S { (self.x * other.y) - (self.y * other.x) } /// Create a `Vector3`, using the `x` and `y` values from this vector, and the /// provided `z`. #[inline] pub fn extend(self, z: S) -> Vector3 { Vector3::new(self.x, self.y, z) } impl_swizzle_functions!(Vector1, Vector2, Vector3, Vector4, S, xy); } impl Vector3 { /// A unit vector in the `x` direction. #[inline] pub fn unit_x() -> Vector3 { Vector3::new(S::one(), S::zero(), S::zero()) } /// A unit vector in the `y` direction. #[inline] pub fn unit_y() -> Vector3 { Vector3::new(S::zero(), S::one(), S::zero()) } /// A unit vector in the `z` direction. #[inline] pub fn unit_z() -> Vector3 { Vector3::new(S::zero(), S::zero(), S::one()) } /// Returns the cross product of the vector and `other`. #[inline] pub fn cross(self, other: Vector3) -> Vector3 { Vector3::new( (self.y * other.z) - (self.z * other.y), (self.z * other.x) - (self.x * other.z), (self.x * other.y) - (self.y * other.x), ) } /// Create a `Vector4`, using the `x`, `y` and `z` values from this vector, and the /// provided `w`. #[inline] pub fn extend(self, w: S) -> Vector4 { Vector4::new(self.x, self.y, self.z, w) } /// Create a `Vector2`, dropping the `z` value. #[inline] pub fn truncate(self) -> Vector2 { Vector2::new(self.x, self.y) } impl_swizzle_functions!(Vector1, Vector2, Vector3, Vector4, S, xyz); } impl Vector4 { /// A unit vector in the `x` direction. #[inline] pub fn unit_x() -> Vector4 { Vector4::new(S::one(), S::zero(), S::zero(), S::zero()) } /// A unit vector in the `y` direction. #[inline] pub fn unit_y() -> Vector4 { Vector4::new(S::zero(), S::one(), S::zero(), S::zero()) } /// A unit vector in the `z` direction. #[inline] pub fn unit_z() -> Vector4 { Vector4::new(S::zero(), S::zero(), S::one(), S::zero()) } /// A unit vector in the `w` direction. #[inline] pub fn unit_w() -> Vector4 { Vector4::new(S::zero(), S::zero(), S::zero(), S::one()) } /// Create a `Vector3`, dropping the `w` value. #[inline] pub fn truncate(self) -> Vector3 { Vector3::new(self.x, self.y, self.z) } /// Create a `Vector3`, dropping the nth element. #[inline] pub fn truncate_n(&self, n: isize) -> Vector3 { match n { 0 => Vector3::new(self.y, self.z, self.w), 1 => Vector3::new(self.x, self.z, self.w), 2 => Vector3::new(self.x, self.y, self.w), 3 => Vector3::new(self.x, self.y, self.z), _ => panic!("{:?} is out of range", n), } } impl_swizzle_functions!(Vector1, Vector2, Vector3, Vector4, S, xyzw); } /// Dot product of two vectors. #[inline] pub fn dot(a: V, b: V) -> V::Scalar where V::Scalar: BaseFloat, { V::dot(a, b) } impl InnerSpace for Vector1 { #[inline] fn dot(self, other: Vector1) -> S { Vector1::mul_element_wise(self, other).sum() } } impl InnerSpace for Vector2 { #[inline] fn dot(self, other: Vector2) -> S { Vector2::mul_element_wise(self, other).sum() } #[inline] fn angle(self, other: Vector2) -> Rad where S: BaseFloat, { Rad::atan2(Self::perp_dot(self, other), Self::dot(self, other)) } } impl InnerSpace for Vector3 { #[inline] fn dot(self, other: Vector3) -> S { Vector3::mul_element_wise(self, other).sum() } #[inline] fn angle(self, other: Vector3) -> Rad where S: BaseFloat, { Rad::atan2(self.cross(other).magnitude(), Self::dot(self, other)) } } impl InnerSpace for Vector4 { #[inline] fn dot(self, other: Vector4) -> S { Vector4::mul_element_wise(self, other).sum() } } impl fmt::Debug for Vector1 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Vector1 ")?; <[S; 1] as fmt::Debug>::fmt(self.as_ref(), f) } } impl fmt::Debug for Vector2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Vector2 ")?; <[S; 2] as fmt::Debug>::fmt(self.as_ref(), f) } } impl fmt::Debug for Vector3 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Vector3 ")?; <[S; 3] as fmt::Debug>::fmt(self.as_ref(), f) } } impl fmt::Debug for Vector4 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Vector4 ")?; <[S; 4] as fmt::Debug>::fmt(self.as_ref(), f) } } #[cfg(feature = "mint")] impl_mint_conversions!(Vector2 { x, y }, Vector2); #[cfg(feature = "mint")] impl_mint_conversions!(Vector3 { x, y, z }, Vector3); #[cfg(feature = "mint")] impl_mint_conversions!(Vector4 { x, y, z, w }, Vector4); #[cfg(test)] mod tests { mod vector2 { use vector::*; const VECTOR2: Vector2 = Vector2 { x: 1, y: 2 }; #[test] fn test_index() { assert_eq!(VECTOR2[0], VECTOR2.x); assert_eq!(VECTOR2[1], VECTOR2.y); } #[test] fn test_index_mut() { let mut v = VECTOR2; *&mut v[0] = 0; assert_eq!(v, [0, 2].into()); } #[test] #[should_panic] fn test_index_out_of_bounds() { VECTOR2[2]; } #[test] fn test_index_range() { assert_eq!(&VECTOR2[..0], &[]); assert_eq!(&VECTOR2[..1], &[1]); assert_eq!(VECTOR2[..0].len(), 0); assert_eq!(VECTOR2[..1].len(), 1); assert_eq!(&VECTOR2[2..], &[]); assert_eq!(&VECTOR2[1..], &[2]); assert_eq!(VECTOR2[2..].len(), 0); assert_eq!(VECTOR2[1..].len(), 1); assert_eq!(&VECTOR2[..], &[1, 2]); assert_eq!(VECTOR2[..].len(), 2); } #[test] fn test_into() { let v = VECTOR2; { let v: [i32; 2] = v.into(); assert_eq!(v, [1, 2]); } { let v: (i32, i32) = v.into(); assert_eq!(v, (1, 2)); } } #[test] fn test_as_ref() { let v = VECTOR2; { let v: &[i32; 2] = v.as_ref(); assert_eq!(v, &[1, 2]); } { let v: &(i32, i32) = v.as_ref(); assert_eq!(v, &(1, 2)); } } #[test] fn test_as_mut() { let mut v = VECTOR2; { let v: &mut [i32; 2] = v.as_mut(); assert_eq!(v, &mut [1, 2]); } { let v: &mut (i32, i32) = v.as_mut(); assert_eq!(v, &mut (1, 2)); } } #[test] fn test_from() { assert_eq!(Vector2::from([1, 2]), VECTOR2); { let v = &[1, 2]; let v: &Vector2<_> = From::from(v); assert_eq!(v, &VECTOR2); } { let v = &mut [1, 2]; let v: &mut Vector2<_> = From::from(v); assert_eq!(v, &VECTOR2); } assert_eq!(Vector2::from((1, 2)), VECTOR2); { let v = &(1, 2); let v: &Vector2<_> = From::from(v); assert_eq!(v, &VECTOR2); } { let v = &mut (1, 2); let v: &mut Vector2<_> = From::from(v); assert_eq!(v, &VECTOR2); } } #[test] fn test_is_finite() { use num_traits::Float; assert!(!Vector2::from([Float::nan(), 1.0]).is_finite()); assert!(!Vector2::from([1.0, Float::infinity()]).is_finite()); assert!(Vector2::from([-1.0, 1.0]).is_finite()); } #[test] fn test_zip() { assert_eq!( Vector2::new(true, false), Vector2::new(-2, 1).zip(Vector2::new(-1, -1), |a, b| a < b) ); } } mod vector3 { use vector::*; const VECTOR3: Vector3 = Vector3 { x: 1, y: 2, z: 3 }; #[test] fn test_index() { assert_eq!(VECTOR3[0], VECTOR3.x); assert_eq!(VECTOR3[1], VECTOR3.y); assert_eq!(VECTOR3[2], VECTOR3.z); } #[test] fn test_index_mut() { let mut v = VECTOR3; *&mut v[1] = 0; assert_eq!(v, [1, 0, 3].into()); } #[test] #[should_panic] fn test_index_out_of_bounds() { VECTOR3[3]; } #[test] fn test_index_range() { assert_eq!(&VECTOR3[..1], &[1]); assert_eq!(&VECTOR3[..2], &[1, 2]); assert_eq!(VECTOR3[..1].len(), 1); assert_eq!(VECTOR3[..2].len(), 2); assert_eq!(&VECTOR3[2..], &[3]); assert_eq!(&VECTOR3[1..], &[2, 3]); assert_eq!(VECTOR3[2..].len(), 1); assert_eq!(VECTOR3[1..].len(), 2); assert_eq!(&VECTOR3[..], &[1, 2, 3]); assert_eq!(VECTOR3[..].len(), 3); } #[test] fn test_into() { let v = VECTOR3; { let v: [i32; 3] = v.into(); assert_eq!(v, [1, 2, 3]); } { let v: (i32, i32, i32) = v.into(); assert_eq!(v, (1, 2, 3)); } } #[test] fn test_as_ref() { let v = VECTOR3; { let v: &[i32; 3] = v.as_ref(); assert_eq!(v, &[1, 2, 3]); } { let v: &(i32, i32, i32) = v.as_ref(); assert_eq!(v, &(1, 2, 3)); } } #[test] fn test_as_mut() { let mut v = VECTOR3; { let v: &mut [i32; 3] = v.as_mut(); assert_eq!(v, &mut [1, 2, 3]); } { let v: &mut (i32, i32, i32) = v.as_mut(); assert_eq!(v, &mut (1, 2, 3)); } } #[test] fn test_from() { assert_eq!(Vector3::from([1, 2, 3]), VECTOR3); { let v = &[1, 2, 3]; let v: &Vector3<_> = From::from(v); assert_eq!(v, &VECTOR3); } { let v = &mut [1, 2, 3]; let v: &mut Vector3<_> = From::from(v); assert_eq!(v, &VECTOR3); } assert_eq!(Vector3::from((1, 2, 3)), VECTOR3); { let v = &(1, 2, 3); let v: &Vector3<_> = From::from(v); assert_eq!(v, &VECTOR3); } { let v = &mut (1, 2, 3); let v: &mut Vector3<_> = From::from(v); assert_eq!(v, &VECTOR3); } } #[test] fn test_is_finite() { use num_traits::Float; assert!(!Vector3::from([Float::nan(), 1.0, 1.0]).is_finite()); assert!(!Vector3::from([1.0, 1.0, Float::infinity()]).is_finite()); assert!(Vector3::from([-1.0, 1.0, 1.0]).is_finite()); } #[test] fn test_zip() { assert_eq!( Vector3::new(true, false, false), Vector3::new(-2, 1, 0).zip(Vector3::new(-1, -1, -1), |a, b| a < b) ); } } mod vector4 { use vector::*; const VECTOR4: Vector4 = Vector4 { x: 1, y: 2, z: 3, w: 4, }; #[test] fn test_index() { assert_eq!(VECTOR4[0], VECTOR4.x); assert_eq!(VECTOR4[1], VECTOR4.y); assert_eq!(VECTOR4[2], VECTOR4.z); assert_eq!(VECTOR4[3], VECTOR4.w); } #[test] fn test_index_mut() { let mut v = VECTOR4; *&mut v[2] = 0; assert_eq!(v, [1, 2, 0, 4].into()); } #[test] #[should_panic] fn test_index_out_of_bounds() { VECTOR4[4]; } #[test] fn test_index_range() { assert_eq!(&VECTOR4[..2], &[1, 2]); assert_eq!(&VECTOR4[..3], &[1, 2, 3]); assert_eq!(VECTOR4[..2].len(), 2); assert_eq!(VECTOR4[..3].len(), 3); assert_eq!(&VECTOR4[2..], &[3, 4]); assert_eq!(&VECTOR4[1..], &[2, 3, 4]); assert_eq!(VECTOR4[2..].len(), 2); assert_eq!(VECTOR4[1..].len(), 3); assert_eq!(&VECTOR4[..], &[1, 2, 3, 4]); assert_eq!(VECTOR4[..].len(), 4); } #[test] fn test_into() { let v = VECTOR4; { let v: [i32; 4] = v.into(); assert_eq!(v, [1, 2, 3, 4]); } { let v: (i32, i32, i32, i32) = v.into(); assert_eq!(v, (1, 2, 3, 4)); } } #[test] fn test_as_ref() { let v = VECTOR4; { let v: &[i32; 4] = v.as_ref(); assert_eq!(v, &[1, 2, 3, 4]); } { let v: &(i32, i32, i32, i32) = v.as_ref(); assert_eq!(v, &(1, 2, 3, 4)); } } #[test] fn test_as_mut() { let mut v = VECTOR4; { let v: &mut [i32; 4] = v.as_mut(); assert_eq!(v, &mut [1, 2, 3, 4]); } { let v: &mut (i32, i32, i32, i32) = v.as_mut(); assert_eq!(v, &mut (1, 2, 3, 4)); } } #[test] fn test_from() { assert_eq!(Vector4::from([1, 2, 3, 4]), VECTOR4); { let v = &[1, 2, 3, 4]; let v: &Vector4<_> = From::from(v); assert_eq!(v, &VECTOR4); } { let v = &mut [1, 2, 3, 4]; let v: &mut Vector4<_> = From::from(v); assert_eq!(v, &VECTOR4); } assert_eq!(Vector4::from((1, 2, 3, 4)), VECTOR4); { let v = &(1, 2, 3, 4); let v: &Vector4<_> = From::from(v); assert_eq!(v, &VECTOR4); } { let v = &mut (1, 2, 3, 4); let v: &mut Vector4<_> = From::from(v); assert_eq!(v, &VECTOR4); } } #[test] fn test_is_finite() { use num_traits::Float; assert!(!Vector4::from([0.0, Float::nan(), 1.0, 1.0]).is_finite()); assert!(!Vector4::from([1.0, 1.0, Float::neg_infinity(), 0.0]).is_finite()); assert!(Vector4::from([-1.0, 0.0, 1.0, 1.0]).is_finite()); } #[test] fn test_zip() { assert_eq!( Vector4::new(true, false, false, false), Vector4::new(-2, 1, 0, 1).zip(Vector4::new(-1, -1, -1, -1), |a, b| a < b) ); } #[test] fn test_dot() { assert_eq!(vec3(1.0, 2.0, 3.0).dot(vec3(4.0, 5.0, 6.0)), 32.0); assert_eq!(vec3(1, 2, 3).dot(vec3(4, 5, 6)), 32); } } } cgmath-0.18.0/src/vector_simd.rs010064400007650000024000000227341376272520100147120ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. use vector::*; use structure::*; use std::mem; use std::ops::*; use simd::f32x4 as Simdf32x4; use simd::i32x4 as Simdi32x4; use simd::u32x4 as Simdu32x4; impl From for Vector4 { #[inline] fn from(f: Simdf32x4) -> Self { unsafe { let mut ret: Self = mem::uninitialized(); { let ret_mut: &mut [f32; 4] = ret.as_mut(); f.store(ret_mut.as_mut(), 0 as usize); } ret } } } impl Vector4 { /// Compute and return the square root of each element. #[inline] pub fn sqrt_element_wide(self) -> Self { let s: Simdf32x4 = self.into(); s.sqrt().into() } /// Compute and return the reciprocal of the square root of each element. #[inline] pub fn rsqrt_element_wide(self) -> Self { let s: Simdf32x4 = self.into(); s.approx_rsqrt().into() } /// Compute and return the reciprocal of each element. #[inline] pub fn recip_element_wide(self) -> Self { let s: Simdf32x4 = self.into(); s.approx_reciprocal().into() } } impl Into for Vector4 { #[inline] fn into(self) -> Simdf32x4 { let self_ref: &[f32; 4] = self.as_ref(); Simdf32x4::load(self_ref.as_ref(), 0 as usize) } } impl_operator_simd! { [Simdf32x4]; Add> for Vector4 { fn add(lhs, rhs) -> Vector4 { (lhs + rhs).into() } } } impl_operator_simd! { [Simdf32x4]; Sub> for Vector4 { fn sub(lhs, rhs) -> Vector4 { (lhs - rhs).into() } } } impl_operator_simd! {@rs [Simdf32x4]; Mul for Vector4 { fn mul(lhs, rhs) -> Vector4 { (lhs * rhs).into() } } } impl_operator_simd! {@rs [Simdf32x4]; Div for Vector4 { fn div(lhs, rhs) -> Vector4 { (lhs / rhs).into() } } } impl_operator_simd! { [Simdf32x4]; Neg for Vector4 { fn neg(lhs) -> Vector4 { (-lhs).into() } } } impl AddAssign for Vector4 { #[inline] fn add_assign(&mut self, rhs: Self) { let s: Simdf32x4 = (*self).into(); let rhs: Simdf32x4 = rhs.into(); *self = (s + rhs).into(); } } impl SubAssign for Vector4 { #[inline] fn sub_assign(&mut self, rhs: Self) { let s: Simdf32x4 = (*self).into(); let rhs: Simdf32x4 = rhs.into(); *self = (s - rhs).into(); } } impl MulAssign for Vector4 { fn mul_assign(&mut self, other: f32) { let s: Simdf32x4 = (*self).into(); let other = Simdf32x4::splat(other); *self = (s * other).into(); } } impl DivAssign for Vector4 { fn div_assign(&mut self, other: f32) { let s: Simdf32x4 = (*self).into(); let other = Simdf32x4::splat(other); *self = (s / other).into(); } } impl ElementWise for Vector4 { #[inline] fn add_element_wise(self, rhs: Vector4) -> Vector4 { self + rhs } #[inline] fn sub_element_wise(self, rhs: Vector4) -> Vector4 { self - rhs } #[inline] fn mul_element_wise(self, rhs: Vector4) -> Vector4 { let s: Simdf32x4 = self.into(); let rhs: Simdf32x4 = rhs.into(); (s * rhs).into() } #[inline] fn div_element_wise(self, rhs: Vector4) -> Vector4 { let s: Simdf32x4 = self.into(); let rhs: Simdf32x4 = rhs.into(); (s / rhs).into() } #[inline] fn add_assign_element_wise(&mut self, rhs: Vector4) { (*self) += rhs; } #[inline] fn sub_assign_element_wise(&mut self, rhs: Vector4) { (*self) -= rhs; } #[inline] fn mul_assign_element_wise(&mut self, rhs: Vector4) { let s: Simdf32x4 = (*self).into(); let rhs: Simdf32x4 = rhs.into(); *self = (s * rhs).into(); } #[inline] fn div_assign_element_wise(&mut self, rhs: Vector4) { let s: Simdf32x4 = (*self).into(); let rhs: Simdf32x4 = rhs.into(); *self = (s * rhs).into(); } } impl ElementWise for Vector4 { #[inline] fn add_element_wise(self, rhs: f32) -> Vector4 { let s: Simdf32x4 = self.into(); let rhs = Simdf32x4::splat(rhs); (s + rhs).into() } #[inline] fn sub_element_wise(self, rhs: f32) -> Vector4 { let s: Simdf32x4 = self.into(); let rhs = Simdf32x4::splat(rhs); (s - rhs).into() } #[inline] fn mul_element_wise(self, rhs: f32) -> Vector4 { self * rhs } #[inline] fn div_element_wise(self, rhs: f32) -> Vector4 { self / rhs } #[inline] fn add_assign_element_wise(&mut self, rhs: f32) { let s: Simdf32x4 = (*self).into(); let rhs = Simdf32x4::splat(rhs); *self = (s + rhs).into(); } #[inline] fn sub_assign_element_wise(&mut self, rhs: f32) { let s: Simdf32x4 = (*self).into(); let rhs = Simdf32x4::splat(rhs); *self = (s - rhs).into(); } #[inline] fn mul_assign_element_wise(&mut self, rhs: f32) { (*self) *= rhs; } #[inline] fn div_assign_element_wise(&mut self, rhs: f32) { (*self) /= rhs; } } impl From for Vector4 { #[inline] fn from(f: Simdi32x4) -> Self { unsafe { let mut ret: Self = mem::uninitialized(); { let ret_mut: &mut [i32; 4] = ret.as_mut(); f.store(ret_mut.as_mut(), 0 as usize); } ret } } } impl Into for Vector4 { #[inline] fn into(self) -> Simdi32x4 { let self_ref: &[i32; 4] = self.as_ref(); Simdi32x4::load(self_ref.as_ref(), 0 as usize) } } impl_operator_simd! { [Simdi32x4]; Add> for Vector4 { fn add(lhs, rhs) -> Vector4 { (lhs + rhs).into() } } } impl_operator_simd! { [Simdi32x4]; Sub> for Vector4 { fn sub(lhs, rhs) -> Vector4 { (lhs - rhs).into() } } } impl_operator_simd! {@rs [Simdi32x4]; Mul for Vector4 { fn mul(lhs, rhs) -> Vector4 { (lhs * rhs).into() } } } impl_operator_simd! { [Simdi32x4]; Neg for Vector4 { fn neg(lhs) -> Vector4 { (-lhs).into() } } } impl AddAssign for Vector4 { #[inline] fn add_assign(&mut self, rhs: Self) { let s: Simdi32x4 = (*self).into(); let rhs: Simdi32x4 = rhs.into(); *self = (s + rhs).into(); } } impl SubAssign for Vector4 { #[inline] fn sub_assign(&mut self, rhs: Self) { let s: Simdi32x4 = (*self).into(); let rhs: Simdi32x4 = rhs.into(); *self = (s - rhs).into(); } } impl MulAssign for Vector4 { fn mul_assign(&mut self, other: i32) { let s: Simdi32x4 = (*self).into(); let other = Simdi32x4::splat(other); *self = (s * other).into(); } } impl From for Vector4 { #[inline] fn from(f: Simdu32x4) -> Self { unsafe { let mut ret: Self = mem::uninitialized(); { let ret_mut: &mut [u32; 4] = ret.as_mut(); f.store(ret_mut.as_mut(), 0 as usize); } ret } } } impl Into for Vector4 { #[inline] fn into(self) -> Simdu32x4 { let self_ref: &[u32; 4] = self.as_ref(); Simdu32x4::load(self_ref.as_ref(), 0 as usize) } } impl_operator_simd! { [Simdu32x4]; Add> for Vector4 { fn add(lhs, rhs) -> Vector4 { (lhs + rhs).into() } } } impl_operator_simd! { [Simdu32x4]; Sub> for Vector4 { fn sub(lhs, rhs) -> Vector4 { (lhs - rhs).into() } } } impl_operator_simd! {@rs [Simdu32x4]; Mul for Vector4 { fn mul(lhs, rhs) -> Vector4 { (lhs * rhs).into() } } } impl AddAssign for Vector4 { #[inline] fn add_assign(&mut self, rhs: Self) { let s: Simdu32x4 = (*self).into(); let rhs: Simdu32x4 = rhs.into(); *self = (s + rhs).into(); } } impl SubAssign for Vector4 { #[inline] fn sub_assign(&mut self, rhs: Self) { let s: Simdu32x4 = (*self).into(); let rhs: Simdu32x4 = rhs.into(); *self = (s - rhs).into(); } } impl MulAssign for Vector4 { fn mul_assign(&mut self, other: u32) { let s: Simdu32x4 = (*self).into(); let other = Simdu32x4::splat(other); *self = (s * other).into(); } } cgmath-0.18.0/tests/angle.rs010064400007650000024000000060101376272520100140220ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. #[macro_use] extern crate approx; extern crate cgmath; use cgmath::{Angle, Deg, Rad}; #[test] fn test_normalize() { let angle: Rad = Rad::full_turn().normalize(); assert_ulps_eq!(&angle, &Rad(0f64)); let angle: Rad = (Rad::full_turn() + Rad::turn_div_4()).normalize(); assert_ulps_eq!(&angle, &Rad::turn_div_4()); let angle: Rad = (-Rad::turn_div_4()).normalize(); assert_ulps_eq!(&angle, &(Rad::full_turn() - Rad::turn_div_4())); } #[test] fn test_normalize_signed() { let angle: Rad = Rad::full_turn().normalize_signed(); assert_ulps_eq!(&angle, &Rad(0f64)); let angle: Rad = (Rad::full_turn() + Rad::turn_div_4()).normalize_signed(); assert_ulps_eq!(&angle, &Rad::turn_div_4()); let angle: Rad = (Rad::full_turn() - Rad::turn_div_4()).normalize_signed(); assert_ulps_eq!(&angle, &-Rad::turn_div_4()); let angle: Rad = Rad::turn_div_2().normalize_signed(); assert_ulps_eq!(&angle, &Rad::turn_div_2()); let angle: Rad = (-Rad::turn_div_2()).normalize_signed(); assert_ulps_eq!(&angle, &Rad::turn_div_2()); } #[test] fn test_conv() { let angle: Rad<_> = Deg(-5.0f64).into(); let angle: Deg<_> = angle.into(); assert_ulps_eq!(&angle, &Deg(-5.0f64)); let angle: Rad<_> = Deg(30.0f64).into(); let angle: Deg<_> = angle.into(); assert_ulps_eq!(&angle, &Deg(30.0f64)); let angle: Deg<_> = Rad(-5.0f64).into(); let angle: Rad<_> = angle.into(); assert_ulps_eq!(&angle, &Rad(-5.0f64)); let angle: Deg<_> = Rad(30.0f64).into(); let angle: Rad<_> = angle.into(); assert_ulps_eq!(&angle, &Rad(30.0f64)); } mod rad { use cgmath::Rad; #[test] fn test_iter_sum() { assert_eq!( Rad(2.0) + Rad(3.0) + Rad(4.0), [Rad(2.0), Rad(3.0), Rad(4.0)].iter().sum() ); assert_eq!( Rad(2.0) + Rad(3.0) + Rad(4.0), [Rad(2.0), Rad(3.0), Rad(4.0)].iter().cloned().sum() ); } } mod deg { use cgmath::Deg; #[test] fn test_iter_sum() { assert_eq!( Deg(2.0) + Deg(3.0) + Deg(4.0), [Deg(2.0), Deg(3.0), Deg(4.0)].iter().sum() ); assert_eq!( Deg(2.0) + Deg(3.0) + Deg(4.0), [Deg(2.0), Deg(3.0), Deg(4.0)].iter().cloned().sum() ); } } cgmath-0.18.0/tests/matrix.rs010064400007650000024000000775411377421362200142630ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. extern crate approx; extern crate cgmath; pub mod matrix2 { use std::f64; use cgmath::*; const A: Matrix2 = Matrix2 { x: Vector2 { x: 1.0f64, y: 3.0f64, }, y: Vector2 { x: 2.0f64, y: 4.0f64, }, }; const B: Matrix2 = Matrix2 { x: Vector2 { x: 2.0f64, y: 4.0f64, }, y: Vector2 { x: 3.0f64, y: 5.0f64, }, }; const C: Matrix2 = Matrix2 { x: Vector2 { x: 2.0f64, y: 1.0f64, }, y: Vector2 { x: 1.0f64, y: 2.0f64, }, }; const V: Vector2 = Vector2 { x: 1.0f64, y: 2.0f64, }; const F: f64 = 0.5; #[test] fn test_neg() { assert_eq!(-A, Matrix2::new(-1.0f64, -3.0f64, -2.0f64, -4.0f64)); } #[test] fn test_mul_scalar() { let result = Matrix2::new(0.5f64, 1.5f64, 1.0f64, 2.0f64); assert_eq!(A * F, result); assert_eq!(F * A, result); } #[test] fn test_div_scalar() { assert_eq!(A / F, Matrix2::new(2.0f64, 6.0f64, 4.0f64, 8.0f64)); assert_eq!(4.0f64 / C, Matrix2::new(2.0f64, 4.0f64, 4.0f64, 2.0f64)); } #[test] fn test_rem_scalar() { assert_eq!(A % 3.0f64, Matrix2::new(1.0f64, 0.0f64, 2.0f64, 1.0f64)); assert_eq!(3.0f64 % A, Matrix2::new(0.0f64, 0.0f64, 1.0f64, 3.0f64)); } #[test] fn test_add_matrix() { assert_eq!(A + B, Matrix2::new(3.0f64, 7.0f64, 5.0f64, 9.0f64)); } #[test] fn test_sub_matrix() { assert_eq!(A - B, Matrix2::new(-1.0f64, -1.0f64, -1.0f64, -1.0f64)); } #[test] fn test_mul_vector() { assert_eq!(A * V, Vector2::new(5.0f64, 11.0f64)); } #[test] fn test_mul_matrix() { assert_eq!(A * B, Matrix2::new(10.0f64, 22.0f64, 13.0f64, 29.0f64)); assert_eq!(A * B, &A * &B); } #[test] fn test_sum_matrix() { assert_eq!(A + B + C, [A, B, C].iter().sum()); assert_eq!(A + B + C, [A, B, C].iter().cloned().sum()); } #[test] fn test_product_matrix() { assert_eq!(A * B * C, [A, B, C].iter().product()); assert_eq!(A * B * C, [A, B, C].iter().cloned().product()); } #[test] fn test_determinant() { assert_eq!(A.determinant(), -2.0f64) } #[test] fn test_trace() { assert_eq!(A.trace(), 5.0f64); } #[test] fn test_transpose() { assert_eq!( A.transpose(), Matrix2::::new(1.0f64, 2.0f64, 3.0f64, 4.0f64) ); } #[test] fn test_transpose_self() { let mut mut_a = A; mut_a.transpose_self(); assert_eq!(mut_a, A.transpose()); } #[test] fn test_invert() { assert!(Matrix2::::identity().invert().unwrap().is_identity()); assert_eq!( A.invert().unwrap(), Matrix2::new(-2.0f64, 1.5f64, 1.0f64, -0.5f64) ); assert!(Matrix2::new(0.0f64, 2.0f64, 0.0f64, 5.0f64) .invert() .is_none()); } #[test] fn test_predicates() { assert!(Matrix2::::identity().is_identity()); assert!(Matrix2::::identity().is_symmetric()); assert!(Matrix2::::identity().is_diagonal()); assert!(Matrix2::::identity().is_invertible()); assert!(!A.is_identity()); assert!(!A.is_symmetric()); assert!(!A.is_diagonal()); assert!(A.is_invertible()); assert!(!C.is_identity()); assert!(C.is_symmetric()); assert!(!C.is_diagonal()); assert!(C.is_invertible()); assert!(Matrix2::from_value(6.0f64).is_diagonal()); } #[test] fn test_from_angle() { // Rotate the vector (1, 0) by π/2 radians to the vector (0, 1) let rot1 = Matrix2::from_angle(Rad(0.5f64 * f64::consts::PI)); assert_ulps_eq!(rot1 * Vector2::unit_x(), &Vector2::unit_y()); // Rotate the vector (-1, 0) by -π/2 radians to the vector (0, 1) let rot2 = -rot1; assert_ulps_eq!(rot2 * -Vector2::unit_x(), &Vector2::unit_y()); // Rotate the vector (1, 1) by π radians to the vector (-1, -1) let rot3: Matrix2 = Matrix2::from_angle(Rad(f64::consts::PI)); assert_ulps_eq!(rot3 * Vector2::new(1.0, 1.0), &Vector2::new(-1.0, -1.0)); } #[test] fn test_look_at() { // rot should rotate unit_x() to look at the input vector let rot = Matrix2::look_at(V, Vector2::unit_y()); assert_eq!(rot * Vector2::unit_x(), V.normalize()); let new_up = Vector2::new(-V.y, V.x).normalize(); assert_eq!(rot * Vector2::unit_y(), new_up); let rot_down = Matrix2::look_at(V, -1.0 * Vector2::unit_y()); assert_eq!(rot_down * Vector2::unit_x(), V.normalize()); assert_eq!(rot_down * Vector2::unit_y(), -1.0 * new_up); let rot2 = Matrix2::look_at(-V, Vector2::unit_y()); assert_eq!(rot2 * Vector2::unit_x(), (-V).normalize()); } } pub mod matrix3 { use cgmath::*; const A: Matrix3 = Matrix3 { x: Vector3 { x: 1.0f64, y: 4.0f64, z: 7.0f64, }, y: Vector3 { x: 2.0f64, y: 5.0f64, z: 8.0f64, }, z: Vector3 { x: 3.0f64, y: 6.0f64, z: 9.0f64, }, }; const B: Matrix3 = Matrix3 { x: Vector3 { x: 2.0f64, y: 5.0f64, z: 8.0f64, }, y: Vector3 { x: 3.0f64, y: 6.0f64, z: 9.0f64, }, z: Vector3 { x: 4.0f64, y: 7.0f64, z: 10.0f64, }, }; const C: Matrix3 = Matrix3 { x: Vector3 { x: 2.0f64, y: 4.0f64, z: 6.0f64, }, y: Vector3 { x: 0.0f64, y: 2.0f64, z: 4.0f64, }, z: Vector3 { x: 0.0f64, y: 0.0f64, z: 1.0f64, }, }; const D: Matrix3 = Matrix3 { x: Vector3 { x: 3.0f64, y: 2.0f64, z: 1.0f64, }, y: Vector3 { x: 2.0f64, y: 3.0f64, z: 2.0f64, }, z: Vector3 { x: 1.0f64, y: 2.0f64, z: 3.0f64, }, }; const V: Vector3 = Vector3 { x: 1.0f64, y: 2.0f64, z: 3.0f64, }; const F: f64 = 0.5; #[test] fn test_neg() { assert_eq!( -A, Matrix3::new( -1.0f64, -4.0f64, -7.0f64, -2.0f64, -5.0f64, -8.0f64, -3.0f64, -6.0f64, -9.0f64 ) ); } #[test] fn test_mul_scalar() { let result = Matrix3::new( 0.5f64, 2.0f64, 3.5f64, 1.0f64, 2.5f64, 4.0f64, 1.5f64, 3.0f64, 4.5f64, ); assert_eq!(A * F, result); assert_eq!(F * A, result); } #[test] fn test_div_scalar() { assert_eq!( A / F, Matrix3::new( 2.0f64, 8.0f64, 14.0f64, 4.0f64, 10.0f64, 16.0f64, 6.0f64, 12.0f64, 18.0f64 ) ); assert_eq!( 6.0f64 / D, Matrix3::new(2.0f64, 3.0f64, 6.0f64, 3.0f64, 2.0f64, 3.0f64, 6.0f64, 3.0f64, 2.0f64) ); } #[test] fn test_rem_scalar() { assert_eq!( A % 3.0f64, Matrix3::new(1.0f64, 1.0f64, 1.0f64, 2.0f64, 2.0f64, 2.0f64, 0.0f64, 0.0f64, 0.0f64) ); assert_eq!( 9.0f64 % A, Matrix3::new(0.0f64, 1.0f64, 2.0f64, 1.0f64, 4.0f64, 1.0f64, 0.0f64, 3.0f64, 0.0f64) ); } #[test] fn test_add_matrix() { assert_eq!( A + B, Matrix3::new( 3.0f64, 9.0f64, 15.0f64, 5.0f64, 11.0f64, 17.0f64, 7.0f64, 13.0f64, 19.0f64 ) ); } #[test] fn test_sub_matrix() { assert_eq!( A - B, Matrix3::new( -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64 ) ); } #[test] fn test_mul_vector() { assert_eq!(A * V, Vector3::new(14.0f64, 32.0f64, 50.0f64)); } #[test] fn test_mul_matrix() { assert_eq!( A * B, Matrix3::new( 36.0f64, 81.0f64, 126.0f64, 42.0f64, 96.0f64, 150.0f64, 48.0f64, 111.0f64, 174.0f64 ) ); assert_eq!(A * B, &A * &B); } #[test] fn test_sum_matrix() { assert_eq!(A + B + C + D, [A, B, C, D].iter().sum()); assert_eq!(A + B + C + D, [A, B, C, D].iter().cloned().sum()); } #[test] fn test_product_matrix() { assert_eq!(A * B * C * D, [A, B, C, D].iter().product()); assert_eq!(A * B * C * D, [A, B, C, D].iter().cloned().product()); } #[test] fn test_determinant() { assert_eq!(A.determinant(), 0.0f64); } #[test] fn test_trace() { assert_eq!(A.trace(), 15.0f64); } #[test] fn test_transpose() { assert_eq!( A.transpose(), Matrix3::::new( 1.0f64, 2.0f64, 3.0f64, 4.0f64, 5.0f64, 6.0f64, 7.0f64, 8.0f64, 9.0f64 ) ); } #[test] fn test_transpose_self() { let mut mut_a = A; mut_a.transpose_self(); assert_eq!(mut_a, A.transpose()); } #[test] fn test_invert() { assert!(Matrix3::::identity().invert().unwrap().is_identity()); assert_eq!(A.invert(), None); assert_eq!( C.invert().unwrap(), Matrix3::new(0.5f64, -1.0f64, 1.0f64, 0.0f64, 0.5f64, -2.0f64, 0.0f64, 0.0f64, 1.0f64) ); } #[test] fn test_predicates() { assert!(Matrix3::::identity().is_identity()); assert!(Matrix3::::identity().is_symmetric()); assert!(Matrix3::::identity().is_diagonal()); assert!(Matrix3::::identity().is_invertible()); assert!(!A.is_identity()); assert!(!A.is_symmetric()); assert!(!A.is_diagonal()); assert!(!A.is_invertible()); assert!(!D.is_identity()); assert!(D.is_symmetric()); assert!(!D.is_diagonal()); assert!(D.is_invertible()); assert!(Matrix3::from_value(6.0f64).is_diagonal()); } #[test] fn test_from_translation() { let mat = Matrix3::from_translation(Vector2::new(1.0f64, 2.0f64)); let vertex = Vector3::new(0.0f64, 0.0f64, 1.0f64); let res = mat * vertex; assert_eq!(res, Vector3::new(1., 2., 1.)); } mod from_axis_x { use cgmath::*; fn check_from_axis_angle_x(pitch: Rad) { let found = Matrix3::from_angle_x(pitch); let expected = Matrix3::from(Euler { x: pitch, y: Rad(0.0), z: Rad(0.0), }); assert_relative_eq!(found, expected, epsilon = 0.001); } #[test] fn test_zero() { check_from_axis_angle_x(Rad(0.0)); } #[test] fn test_pos_1() { check_from_axis_angle_x(Rad(1.0)); } #[test] fn test_neg_1() { check_from_axis_angle_x(Rad(-1.0)); } } mod from_axis_y { use cgmath::*; fn check_from_axis_angle_y(yaw: Rad) { let found = Matrix3::from_angle_y(yaw); let expected = Matrix3::from(Euler { x: Rad(0.0), y: yaw, z: Rad(0.0), }); assert_relative_eq!(found, expected, epsilon = 0.001); } #[test] fn test_zero() { check_from_axis_angle_y(Rad(0.0)); } #[test] fn test_pos_1() { check_from_axis_angle_y(Rad(1.0)); } #[test] fn test_neg_1() { check_from_axis_angle_y(Rad(-1.0)); } } mod from_axis_z { use cgmath::*; fn check_from_axis_angle_z(roll: Rad) { let found = Matrix3::from_angle_z(roll); let expected = Matrix3::from(Euler { x: Rad(0.0), y: Rad(0.0), z: roll, }); assert_relative_eq!(found, expected, epsilon = 0.001); } #[test] fn test_zero() { check_from_axis_angle_z(Rad(0.0)); } #[test] fn test_pos_1() { check_from_axis_angle_z(Rad(1.0)); } #[test] fn test_neg_1() { check_from_axis_angle_z(Rad(-1.0)); } } mod from_axis_angle { mod axis_x { use cgmath::*; fn check_from_axis_angle_x(pitch: Rad) { let found = Matrix3::from_axis_angle(Vector3::unit_x(), pitch); let expected = Matrix3::from(Euler { x: pitch, y: Rad(0.0), z: Rad(0.0), }); assert_relative_eq!(found, expected, epsilon = 0.001); } #[test] fn test_zero() { check_from_axis_angle_x(Rad(0.0)); } #[test] fn test_pos_1() { check_from_axis_angle_x(Rad(1.0)); } #[test] fn test_neg_1() { check_from_axis_angle_x(Rad(-1.0)); } } mod axis_y { use cgmath::*; fn check_from_axis_angle_y(yaw: Rad) { let found = Matrix3::from_axis_angle(Vector3::unit_y(), yaw); let expected = Matrix3::from(Euler { x: Rad(0.0), y: yaw, z: Rad(0.0), }); assert_relative_eq!(found, expected, epsilon = 0.001); } #[test] fn test_zero() { check_from_axis_angle_y(Rad(0.0)); } #[test] fn test_pos_1() { check_from_axis_angle_y(Rad(1.0)); } #[test] fn test_neg_1() { check_from_axis_angle_y(Rad(-1.0)); } } mod axis_z { use cgmath::*; fn check_from_axis_angle_z(roll: Rad) { let found = Matrix3::from_axis_angle(Vector3::unit_z(), roll); let expected = Matrix3::from(Euler { x: Rad(0.0), y: Rad(0.0), z: roll, }); assert_relative_eq!(found, expected, epsilon = 0.001); } #[test] fn test_zero() { check_from_axis_angle_z(Rad(0.0)); } #[test] fn test_pos_1() { check_from_axis_angle_z(Rad(1.0)); } #[test] fn test_neg_1() { check_from_axis_angle_z(Rad(-1.0)); } } } mod rotate_from_euler { use cgmath::*; #[test] fn test_x() { let vec = vec3(0.0, 0.0, 1.0); let rot = Matrix3::from(Euler::new(Deg(90.0), Deg(0.0), Deg(0.0))); assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec); let rot = Matrix3::from(Euler::new(Deg(-90.0), Deg(0.0), Deg(0.0))); assert_ulps_eq!(vec3(0.0, 1.0, 0.0), rot * vec); } #[test] fn test_y() { let vec = vec3(0.0, 0.0, 1.0); let rot = Matrix3::from(Euler::new(Deg(0.0), Deg(90.0), Deg(0.0))); assert_ulps_eq!(vec3(1.0, 0.0, 0.0), rot * vec); let rot = Matrix3::from(Euler::new(Deg(0.0), Deg(-90.0), Deg(0.0))); assert_ulps_eq!(vec3(-1.0, 0.0, 0.0), rot * vec); } #[test] fn test_z() { let vec = vec3(1.0, 0.0, 0.0); let rot = Matrix3::from(Euler::new(Deg(0.0), Deg(0.0), Deg(90.0))); assert_ulps_eq!(vec3(0.0, 1.0, 0.0), rot * vec); let rot = Matrix3::from(Euler::new(Deg(0.0), Deg(0.0), Deg(-90.0))); assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec); } // tests that the Y rotation is done after the X #[test] fn test_x_then_y() { let vec = vec3(0.0, 1.0, 0.0); let rot = Matrix3::from(Euler::new(Deg(90.0), Deg(90.0), Deg(0.0))); assert_ulps_eq!(vec3(0.0, 0.0, 1.0), rot * vec); } // tests that the Z rotation is done after the Y #[test] fn test_y_then_z() { let vec = vec3(0.0, 0.0, 1.0); let rot = Matrix3::from(Euler::new(Deg(0.0), Deg(90.0), Deg(90.0))); assert_ulps_eq!(vec3(1.0, 0.0, 0.0), rot * vec); } } mod rotate_from_axis_angle { use cgmath::*; #[test] fn test_x() { let vec = vec3(0.0, 0.0, 1.0); let rot = Matrix3::from_angle_x(Deg(90.0)); println!("x mat: {:?}", rot); assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec); } #[test] fn test_y() { let vec = vec3(0.0, 0.0, 1.0); let rot = Matrix3::from_angle_y(Deg(90.0)); assert_ulps_eq!(vec3(1.0, 0.0, 0.0), rot * vec); } #[test] fn test_z() { let vec = vec3(1.0, 0.0, 0.0); let rot = Matrix3::from_angle_z(Deg(90.0)); assert_ulps_eq!(vec3(0.0, 1.0, 0.0), rot * vec); } #[test] fn test_xy() { let vec = vec3(0.0, 0.0, 1.0); let rot = Matrix3::from_axis_angle(vec3(1.0, 1.0, 0.0).normalize(), Deg(90.0)); assert_ulps_eq!( vec3(2.0f32.sqrt() / 2.0, -2.0f32.sqrt() / 2.0, 0.0), rot * vec ); } #[test] fn test_yz() { let vec = vec3(1.0, 0.0, 0.0); let rot = Matrix3::from_axis_angle(vec3(0.0, 1.0, 1.0).normalize(), Deg(-90.0)); assert_ulps_eq!( vec3(0.0, -2.0f32.sqrt() / 2.0, 2.0f32.sqrt() / 2.0), rot * vec ); } #[test] fn test_xz() { let vec = vec3(0.0, 1.0, 0.0); let rot = Matrix3::from_axis_angle(vec3(1.0, 0.0, 1.0).normalize(), Deg(90.0)); assert_ulps_eq!( vec3(-2.0f32.sqrt() / 2.0, 0.0, 2.0f32.sqrt() / 2.0), rot * vec ); } } #[test] fn test_look_to_lh() { let dir = Vector3::new(1.0, 2.0, 3.0).normalize(); let up = Vector3::unit_y(); let m = Matrix3::look_to_lh(dir, up); assert_ulps_eq!(m, Matrix3::from([ [0.9486833, -0.16903085, 0.26726127], [0.0, 0.8451542, 0.53452253], [-0.31622776, -0.50709254, 0.8017838_f32] ])); #[allow(deprecated)] { assert_ulps_eq!(m, Matrix3::look_at(dir, up)); } } #[test] fn test_look_to_rh() { let dir = Vector3::new(1.0, 2.0, 3.0).normalize(); let up = Vector3::unit_y(); let m = Matrix3::look_to_rh(dir, up); assert_ulps_eq!(m, Matrix3::from([ [-0.9486833, -0.16903085, -0.26726127], [0.0, 0.8451542, -0.53452253], [0.31622776, -0.50709254, -0.8017838_f32] ])); } } pub mod matrix4 { use cgmath::*; const A: Matrix4 = Matrix4 { x: Vector4 { x: 1.0f64, y: 5.0f64, z: 9.0f64, w: 13.0f64, }, y: Vector4 { x: 2.0f64, y: 6.0f64, z: 10.0f64, w: 14.0f64, }, z: Vector4 { x: 3.0f64, y: 7.0f64, z: 11.0f64, w: 15.0f64, }, w: Vector4 { x: 4.0f64, y: 8.0f64, z: 12.0f64, w: 16.0f64, }, }; const B: Matrix4 = Matrix4 { x: Vector4 { x: 2.0f64, y: 6.0f64, z: 10.0f64, w: 14.0f64, }, y: Vector4 { x: 3.0f64, y: 7.0f64, z: 11.0f64, w: 15.0f64, }, z: Vector4 { x: 4.0f64, y: 8.0f64, z: 12.0f64, w: 16.0f64, }, w: Vector4 { x: 5.0f64, y: 9.0f64, z: 13.0f64, w: 17.0f64, }, }; const C: Matrix4 = Matrix4 { x: Vector4 { x: 3.0f64, y: 2.0f64, z: 1.0f64, w: 1.0f64, }, y: Vector4 { x: 2.0f64, y: 3.0f64, z: 2.0f64, w: 2.0f64, }, z: Vector4 { x: 1.0f64, y: 2.0f64, z: 3.0f64, w: 3.0f64, }, w: Vector4 { x: 0.0f64, y: 1.0f64, z: 1.0f64, w: 0.0f64, }, }; const D: Matrix4 = Matrix4 { x: Vector4 { x: 4.0f64, y: 3.0f64, z: 2.0f64, w: 1.0f64, }, y: Vector4 { x: 3.0f64, y: 4.0f64, z: 3.0f64, w: 2.0f64, }, z: Vector4 { x: 2.0f64, y: 3.0f64, z: 4.0f64, w: 3.0f64, }, w: Vector4 { x: 1.0f64, y: 2.0f64, z: 3.0f64, w: 4.0f64, }, }; const V: Vector4 = Vector4 { x: 1.0f64, y: 2.0f64, z: 3.0f64, w: 4.0f64, }; const F: f64 = 0.5; #[test] fn test_neg() { assert_eq!( -A, Matrix4::new( -1.0f64, -5.0f64, -9.0f64, -13.0f64, -2.0f64, -6.0f64, -10.0f64, -14.0f64, -3.0f64, -7.0f64, -11.0f64, -15.0f64, -4.0f64, -8.0f64, -12.0f64, -16.0f64 ) ); } #[test] fn test_mul_scalar() { let result = Matrix4::new( 0.5f64, 2.5f64, 4.5f64, 6.5f64, 1.0f64, 3.0f64, 5.0f64, 7.0f64, 1.5f64, 3.5f64, 5.5f64, 7.5f64, 2.0f64, 4.0f64, 6.0f64, 8.0f64, ); assert_eq!(A * F, result); assert_eq!(F * A, result); } #[test] fn test_div_scalar() { assert_eq!( A / F, Matrix4::new( 2.0f64, 10.0f64, 18.0f64, 26.0f64, 4.0f64, 12.0f64, 20.0f64, 28.0f64, 6.0f64, 14.0f64, 22.0f64, 30.0f64, 8.0f64, 16.0f64, 24.0f64, 32.0f64 ) ); assert_eq!( 12.0f64 / D, Matrix4::new( 3.0f64, 4.0f64, 6.0f64, 12.0f64, 4.0f64, 3.0f64, 4.0f64, 6.0f64, 6.0f64, 4.0f64, 3.0f64, 4.0f64, 12.0f64, 6.0f64, 4.0f64, 3.0f64 ) ); } #[test] fn test_rem_scalar() { assert_eq!( A % 4.0f64, Matrix4::new( 1.0f64, 1.0f64, 1.0f64, 1.0f64, 2.0f64, 2.0f64, 2.0f64, 2.0f64, 3.0f64, 3.0f64, 3.0f64, 3.0f64, 0.0f64, 0.0f64, 0.0f64, 0.0f64 ) ); assert_eq!( 16.0f64 % A, Matrix4::new( 0.0f64, 1.0f64, 7.0f64, 3.0f64, 0.0f64, 4.0f64, 6.0f64, 2.0f64, 1.0f64, 2.0f64, 5.0f64, 1.0f64, 0.0f64, 0.0f64, 4.0f64, 0.0f64 ) ); } #[test] fn test_add_matrix() { assert_eq!( A + B, Matrix4::new( 3.0f64, 11.0f64, 19.0f64, 27.0f64, 5.0f64, 13.0f64, 21.0f64, 29.0f64, 7.0f64, 15.0f64, 23.0f64, 31.0f64, 9.0f64, 17.0f64, 25.0f64, 33.0f64 ) ); } #[test] fn test_sub_matrix() { assert_eq!( A - B, Matrix4::new( -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64, -1.0f64 ) ); } #[test] fn test_mul_vector() { assert_eq!(A * V, Vector4::new(30.0f64, 70.0f64, 110.0f64, 150.0f64)); } #[test] fn test_mul_matrix() { assert_eq!( A * B, Matrix4::new( 100.0f64, 228.0f64, 356.0f64, 484.0f64, 110.0f64, 254.0f64, 398.0f64, 542.0f64, 120.0f64, 280.0f64, 440.0f64, 600.0f64, 130.0f64, 306.0f64, 482.0f64, 658.0f64 ) ); assert_eq!(A * B, &A * &B); } #[test] fn test_sum_matrix() { assert_eq!(A + B + C + D, [A, B, C, D].iter().sum()); assert_eq!(A + B + C + D, [A, B, C, D].iter().cloned().sum()); } #[test] fn test_product_matrix() { assert_eq!(A * B * C * D, [A, B, C, D].iter().product()); assert_eq!(A * B * C * D, [A, B, C, D].iter().cloned().product()); } #[test] fn test_determinant() { assert_eq!(A.determinant(), 0.0f64); } #[test] fn test_trace() { assert_eq!(A.trace(), 34.0f64); } #[test] fn test_transpose() { assert_eq!( A.transpose(), Matrix4::::new( 1.0f64, 2.0f64, 3.0f64, 4.0f64, 5.0f64, 6.0f64, 7.0f64, 8.0f64, 9.0f64, 10.0f64, 11.0f64, 12.0f64, 13.0f64, 14.0f64, 15.0f64, 16.0f64 ) ); } #[test] fn test_transpose_self() { let mut mut_a = A; mut_a.transpose_self(); assert_eq!(mut_a, A.transpose()); } #[test] fn test_invert() { assert!(Matrix4::::identity().invert().unwrap().is_identity()); assert_ulps_eq!( &C.invert().unwrap(), &(Matrix4::new( 5.0f64, -4.0f64, 1.0f64, 0.0f64, -4.0f64, 8.0f64, -4.0f64, 0.0f64, 4.0f64, -8.0f64, 4.0f64, 8.0f64, -3.0f64, 4.0f64, 1.0f64, -8.0f64 ) * 0.125f64) ); let mat_c = Matrix4::new( -0.131917f64, -0.76871f64, 0.625846f64, 0.0f64, -0., 0.631364f64, 0.775487f64, 0.0f64, -0.991261f64, 0.1023f64, -0.083287f64, 0.0f64, 0., -1.262728f64, -1.550973f64, 1.0f64, ); assert!((mat_c.invert().unwrap() * mat_c).is_identity()); let mat_d = Matrix4::new( 0.065455f64, -0.720002f64, 0.690879f64, 0.0f64, -0., 0.692364f64, 0.721549f64, 0.0f64, -0.997856f64, -0.047229f64, 0.045318f64, 0.0f64, 0., -1.384727f64, -1.443098f64, 1.0f64, ); assert!((mat_d.invert().unwrap() * mat_d).is_identity()); let mat_e = Matrix4::new( 0.409936f64, 0.683812f64, -0.603617f64, 0.0f64, 0., 0.661778f64, 0.7497f64, 0.0f64, 0.912114f64, -0.307329f64, 0.271286f64, 0.0f64, -0., -1.323555f64, -1.499401f64, 1.0f64, ); assert!((mat_e.invert().unwrap() * mat_e).is_identity()); let mat_f = Matrix4::new( -0.160691f64, -0.772608f64, 0.614211f64, 0.0f64, -0., 0.622298f64, 0.78278f64, 0.0f64, -0.987005f64, 0.125786f64, -0.099998f64, 0.0f64, 0., -1.244597f64, -1.565561f64, 1.0f64, ); assert!((mat_f.invert().unwrap() * mat_f).is_identity()); } #[test] fn test_predicates() { assert!(Matrix4::::identity().is_identity()); assert!(Matrix4::::identity().is_symmetric()); assert!(Matrix4::::identity().is_diagonal()); assert!(Matrix4::::identity().is_invertible()); assert!(!A.is_identity()); assert!(!A.is_symmetric()); assert!(!A.is_diagonal()); assert!(!A.is_invertible()); assert!(!D.is_identity()); assert!(D.is_symmetric()); assert!(!D.is_diagonal()); assert!(D.is_invertible()); assert!(Matrix4::from_value(6.0f64).is_diagonal()); } #[test] fn test_from_translation() { let mat = Matrix4::from_translation(Vector3::new(1.0f64, 2.0f64, 3.0f64)); let vertex = Vector4::new(0.0f64, 0.0f64, 0.0f64, 1.0f64); let res = mat * vertex; assert_eq!(res, Vector4::new(1., 2., 3., 1.)); } #[test] fn test_cast() { assert_ulps_eq!( Matrix2::new(0.2f64, 1.5, 4.7, 2.3).cast().unwrap(), Matrix2::new(0.2f32, 1.5, 4.7, 2.3) ); assert_ulps_eq!( Matrix3::new(0.2f64, 1.5, 4.7, 2.3, 5.7, 2.1, 4.6, 5.2, 6.6,) .cast() .unwrap(), Matrix3::new(0.2f32, 1.5, 4.7, 2.3, 5.7, 2.1, 4.6, 5.2, 6.6,) ); assert_ulps_eq!( Matrix4::new( 0.2f64, 1.5, 4.7, 2.5, 2.3, 5.7, 2.1, 1.1, 4.6, 5.2, 6.6, 0.2, 3.2, 1.8, 0.4, 2.9, ) .cast() .unwrap(), Matrix4::new( 0.2f32, 1.5, 4.7, 2.5, 2.3, 5.7, 2.1, 1.1, 4.6, 5.2, 6.6, 0.2, 3.2, 1.8, 0.4, 2.9, ) ); } #[test] fn test_look_to_rh() { let eye = Point3::new(10.0, 15.0, 20.0); let dir = Vector3::new(1.0, 2.0, 3.0).normalize(); let up = Vector3::unit_y(); let m = Matrix4::look_to_rh(eye, dir, up); #[allow(deprecated)] { assert_ulps_eq!(m, Matrix4::look_at_dir(eye, dir, up)); } let expected = Matrix4::from([ [-0.9486833, -0.16903086, -0.26726127, 0.0], [0.0, 0.84515435, -0.53452253, 0.0], [0.31622776, -0.5070926, -0.8017838, 0.0], [3.1622782, -0.84515476, 26.726126, 1.0_f32] ]); assert_ulps_eq!(expected, m); let m = Matrix4::look_at_rh(eye, eye + dir, up); assert_abs_diff_eq!(expected, m, epsilon = 1.0e-4); } #[test] fn test_look_to_lh() { let eye = Point3::new(10.0, 15.0, 20.0); let dir = Vector3::new(1.0, 2.0, 3.0).normalize(); let up = Vector3::unit_y(); let m = Matrix4::look_to_lh(eye, dir, up); let expected = Matrix4::from([ [0.9486833, -0.16903086, 0.26726127, 0.0], [0.0, 0.84515435, 0.53452253, 0.0], [-0.31622776, -0.5070926, 0.8017838, 0.0], [-3.1622782, -0.84515476, -26.726126, 1.0_f32] ]); assert_ulps_eq!(expected, m); let m = Matrix4::look_at_lh(eye, eye + dir, up); assert_abs_diff_eq!(expected, m, epsilon = 1.0e-4); } mod from { use cgmath::*; #[test] fn test_quaternion() { let quaternion = Quaternion::new(2f32, 3f32, 4f32, 5f32); let matrix_short = Matrix4::from(quaternion); let matrix_long = Matrix3::from(quaternion); let matrix_long = Matrix4::from(matrix_long); assert_ulps_eq!(matrix_short, matrix_long); } } } cgmath-0.18.0/tests/point.rs010064400007650000024000000060431376272520100140730ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. extern crate approx; extern crate cgmath; use cgmath::*; #[test] fn test_constructor() { assert_eq!(point1(1f32), Point1::new(1f32)); assert_eq!(point2(1f32, 2f32), Point2::new(1f32, 2f32)); assert_eq!(point3(1f64, 2f64, 3f64), Point3::new(1f64, 2f64, 3f64)); } macro_rules! impl_test_mul { ($PointN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // point * scalar ops assert_eq!($v * $s, $PointN::new($($v.$field * $s),+)); assert_eq!($s * $v, $PointN::new($($s * $v.$field),+)); assert_eq!(&$v * $s, $v * $s); assert_eq!($s * &$v, $s * $v); // commutativity assert_eq!($v * $s, $s * $v); ) } macro_rules! impl_test_div { ($PointN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // point / scalar ops assert_eq!($v / $s, $PointN::new($($v.$field / $s),+)); assert_eq!($s / $v, $PointN::new($($s / $v.$field),+)); assert_eq!(&$v / $s, $v / $s); assert_eq!($s / &$v, $s / $v); ) } macro_rules! impl_test_rem { ($PointN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // point % scalar ops assert_eq!($v % $s, $PointN::new($($v.$field % $s),+)); assert_eq!($s % $v, $PointN::new($($s % $v.$field),+)); assert_eq!(&$v % $s, $v % $s); assert_eq!($s % &$v, $s % $v); ) } #[test] fn test_homogeneous() { let p = Point3::new(1.0f64, 2.0f64, 3.0f64); assert_ulps_eq!(&p, &Point3::from_homogeneous(p.to_homogeneous())); } #[test] fn test_mul() { impl_test_mul!(Point3 { x, y, z }, 2.0f32, Point3::new(2.0f32, 4.0, 6.0)); impl_test_mul!(Point2 { x, y }, 2.0f32, Point2::new(2.0f32, 4.0)); } #[test] fn test_div() { impl_test_div!(Point3 { x, y, z }, 2.0f32, Point3::new(2.0f32, 4.0, 6.0)); impl_test_div!(Point2 { x, y }, 2.0f32, Point2::new(2.0f32, 4.0)); } #[test] fn test_rem() { impl_test_rem!(Point3 { x, y, z }, 2.0f32, Point3::new(2.0f32, 4.0, 6.0)); impl_test_rem!(Point2 { x, y }, 2.0f32, Point2::new(2.0f32, 4.0)); } #[test] fn test_cast() { assert_ulps_eq!(Point1::new(0.9f64).cast().unwrap(), Point1::new(0.9f32)); assert_ulps_eq!( Point2::new(0.9f64, 1.5).cast().unwrap(), Point2::new(0.9f32, 1.5) ); assert_ulps_eq!( Point3::new(1.0f64, 2.4, -3.13).cast().unwrap(), Point3::new(1.0f32, 2.4, -3.13) ); } cgmath-0.18.0/tests/projection.rs010064400007650000024000000050451376272520100151170ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. extern crate cgmath; use cgmath::{ortho, Matrix4, Vector4}; #[test] fn test_ortho_scale() { // An orthographic projection can be used to scale points // but this will result in the clip space (Z) being swapped (-1 -> 1) // this test asserts that this property is true of our ortho projection let vec_near: Vector4 = Vector4::new(-1., -1., -1., 1.); let vec_orig: Vector4 = Vector4::new(0., 0., 0., 1.); let vec_far: Vector4 = Vector4::new(1., 1., 1., 1.); let o: Matrix4 = ortho(-1., 1., -1., 1., -1., 1.); let near = o * vec_near; let orig = o * vec_orig; let far = o * vec_far; assert_eq!(near, Vector4::new(-1f32, -1., 1., 1.)); assert_eq!(orig, Vector4::new(0f32, 0., 0., 1.)); assert_eq!(far, Vector4::new(1f32, 1., -1., 1.)); let o: Matrix4 = ortho(-2., 2., -2., 2., -2., 2.); let near = o * vec_near; let orig = o * vec_orig; let far = o * vec_far; assert_eq!(near, Vector4::new(-0.5f32, -0.5, 0.5, 1.)); assert_eq!(orig, Vector4::new(0f32, 0., 0., 1.)); assert_eq!(far, Vector4::new(0.5f32, 0.5, -0.5, 1.)); } #[test] fn test_ortho_translate() { // An orthographic projection can be used to translate a point // but this will result in the clip space (Z) being swapped (-1 -> 1) // this test asserts that this property is true of our ortho projection let vec_orig: Vector4 = Vector4::new(0., 0., 0., 1.); let o: Matrix4 = ortho(-1., 1., -1., 1., -1., 1.); let orig = o * vec_orig; assert_eq!(orig, Vector4::new(0., 0., 0., 1.)); let o: Matrix4 = ortho(0., 2., 0., 2., 0., 2.); let orig = o * vec_orig; assert_eq!(orig, Vector4::new(-1., -1., -1., 1.)); let o: Matrix4 = ortho(-2., 0., -2., 0., -2., 0.); let orig = o * vec_orig; assert_eq!(orig, Vector4::new(1., 1., 1., 1.)); } cgmath-0.18.0/tests/quaternion.rs010064400007650000024000000273521376272520100151350ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. extern crate approx; extern crate cgmath; macro_rules! impl_test_mul { ($s:expr, $v:expr) => { // point * scalar ops assert_eq!($v * $s, Quaternion::from_sv($v.s * $s, $v.v * $s)); assert_eq!($s * $v, Quaternion::from_sv($s * $v.s, $s * $v.v)); assert_eq!(&$v * $s, $v * $s); assert_eq!($s * &$v, $s * $v); // commutativity assert_eq!($v * $s, $s * $v); }; } macro_rules! impl_test_div { ($s:expr, $v:expr) => { // point / scalar ops assert_eq!($v / $s, Quaternion::from_sv($v.s / $s, $v.v / $s)); assert_eq!($s / $v, Quaternion::from_sv($s / $v.s, $s / $v.v)); assert_eq!(&$v / $s, $v / $s); assert_eq!($s / &$v, $s / $v); }; } mod operators { use cgmath::*; #[test] fn test_mul() { impl_test_mul!( 2.0f32, Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(1f32), }) ); } #[test] fn test_div() { impl_test_div!( 2.0f32, Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(1f32), }) ); } #[test] fn test_iter_sum() { let q1 = Quaternion::from(Euler { x: Rad(2f32), y: Rad(1f32), z: Rad(1f32), }); let q2 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(2f32), z: Rad(1f32), }); let q3 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(2f32), }); assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().sum()); assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().cloned().sum()); } #[test] fn test_iter_product() { let q1 = Quaternion::from(Euler { x: Rad(2f32), y: Rad(1f32), z: Rad(1f32), }); let q2 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(2f32), z: Rad(1f32), }); let q3 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(2f32), }); assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().product()); assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().cloned().product()); } } mod to_from_euler { use std::f32; use cgmath::*; fn check_euler(rotation: Euler>) { assert_relative_eq!( Euler::from(Quaternion::from(rotation)), rotation, epsilon = 0.001 ); } const HPI: f32 = f32::consts::FRAC_PI_2; #[test] fn test_zero() { check_euler(Euler { x: Rad(0f32), y: Rad(0f32), z: Rad(0f32), }); } #[test] fn test_yaw_pos_1() { check_euler(Euler { x: Rad(0f32), y: Rad(1f32), z: Rad(0f32), }); } #[test] fn test_yaw_neg_1() { check_euler(Euler { x: Rad(0f32), y: Rad(-1f32), z: Rad(0f32), }); } #[test] fn test_pitch_pos_1() { check_euler(Euler { x: Rad(1f32), y: Rad(0f32), z: Rad(0f32), }); } #[test] fn test_pitch_neg_1() { check_euler(Euler { x: Rad(-1f32), y: Rad(0f32), z: Rad(0f32), }); } #[test] fn test_roll_pos_1() { check_euler(Euler { x: Rad(0f32), y: Rad(0f32), z: Rad(1f32), }); } #[test] fn test_roll_neg_1() { check_euler(Euler { x: Rad(0f32), y: Rad(0f32), z: Rad(-1f32), }); } #[test] fn test_pitch_yaw_roll_pos_1() { check_euler(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(1f32), }); } #[test] fn test_pitch_yaw_roll_neg_1() { check_euler(Euler { x: Rad(-1f32), y: Rad(-1f32), z: Rad(-1f32), }); } #[test] fn test_pitch_yaw_roll_pos_hp() { check_euler(Euler { x: Rad(0f32), y: Rad(HPI), z: Rad(1f32), }); } #[test] fn test_pitch_yaw_roll_neg_hp() { check_euler(Euler { x: Rad(0f32), y: Rad(-HPI), z: Rad(1f32), }); } } mod from { mod matrix3 { use cgmath::*; fn check_with_euler(x: Rad, y: Rad, z: Rad) { let matrix3 = Matrix3::from(Euler { x: x, y: y, z: z }); let quaternion = Quaternion::from(matrix3); let quaternion_matrix3 = Matrix3::from(quaternion); assert_ulps_eq!(matrix3, quaternion_matrix3); } // triggers: trace >= S::zero() #[test] fn test_positive_trace() { check_with_euler(Rad(0.0f32), Rad(0.0), Rad(0.0f32)); } // triggers: (mat[0][0] > mat[1][1]) && (mat[0][0] > mat[2][2]) #[test] fn test_xx_maximum() { check_with_euler(Rad(2.0f32), Rad(1.0), Rad(-1.2f32)); } // triggers: mat[1][1] > mat[2][2] #[test] fn test_yy_maximum() { check_with_euler(Rad(2.0f32), Rad(1.0), Rad(3.0f32)); } // base case #[test] fn test_zz_maximum() { check_with_euler(Rad(1.0f32), Rad(1.0), Rad(3.0f32)); } } } mod arc { use cgmath::*; #[inline] fn test(src: Vector3, dst: Vector3) { let q = Quaternion::from_arc(src, dst, None); let v = q.rotate_vector(src); assert_ulps_eq!(v.normalize(), dst.normalize()); } #[test] fn test_same() { let v = Vector3::unit_x(); let q = Quaternion::from_arc(v, v, None); assert_eq!(q, Quaternion::new(1.0, 0.0, 0.0, 0.0)); } #[test] fn test_opposite() { let v = Vector3::unit_x(); test(v, -v); } #[test] fn test_random() { test(vec3(1.0, 2.0, 3.0), vec3(-4.0, 5.0, -6.0)); } #[test] fn test_ortho() { let q: Quaternion = Quaternion::from_arc(Vector3::unit_x(), Vector3::unit_y(), None); let q2 = Quaternion::from_axis_angle(Vector3::unit_z(), Rad::turn_div_4()); assert_ulps_eq!(q, q2); } } mod rotate_from_euler { use cgmath::*; #[test] fn test_x() { let vec = vec3(0.0, 0.0, 1.0); let rot = Quaternion::from(Euler::new(Deg(90.0), Deg(0.0), Deg(0.0))); assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec); let rot = Quaternion::from(Euler::new(Deg(-90.0), Deg(0.0), Deg(0.0))); assert_ulps_eq!(vec3(0.0, 1.0, 0.0), rot * vec); } #[test] fn test_y() { let vec = vec3(0.0, 0.0, 1.0); let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(90.0), Deg(0.0))); assert_ulps_eq!(vec3(1.0, 0.0, 0.0), rot * vec); let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(-90.0), Deg(0.0))); assert_ulps_eq!(vec3(-1.0, 0.0, 0.0), rot * vec); } #[test] fn test_z() { let vec = vec3(1.0, 0.0, 0.0); let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(0.0), Deg(90.0))); assert_ulps_eq!(vec3(0.0, 1.0, 0.0), rot * vec); let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(0.0), Deg(-90.0))); assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec); } // tests that the Y rotation is done after the X #[test] fn test_x_then_y() { let vec = vec3(0.0, 1.0, 0.0); let rot = Quaternion::from(Euler::new(Deg(90.0), Deg(90.0), Deg(0.0))); assert_ulps_eq!(vec3(0.0f32, 0.0f32, 1.0f32), rot * vec); } // tests that the Z rotation is done after the Y #[test] fn test_y_then_z() { let vec = vec3(0.0f32, 0.0f32, 1.0f32); let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(90.0), Deg(90.0))); assert_ulps_eq!(vec3(1.0, 0.0, 0.0), rot * vec); } } mod rotate_from_axis_angle { use cgmath::*; #[test] fn test_x() { let vec = vec3(0.0, 0.0, 1.0); let rot = Quaternion::from_angle_x(Deg(90.0)); assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec); } #[test] fn test_y() { let vec = vec3(0.0, 0.0, 1.0); let rot = Quaternion::from_angle_y(Deg(90.0)); assert_ulps_eq!(vec3(1.0, 0.0, 0.0), rot * vec); } #[test] fn test_z() { let vec = vec3(1.0, 0.0, 0.0); let rot = Quaternion::from_angle_z(Deg(90.0)); assert_ulps_eq!(vec3(0.0, 1.0, 0.0), rot * vec); } #[test] fn test_xy() { let vec = vec3(0.0, 0.0, 1.0); let rot = Quaternion::from_axis_angle(vec3(1.0, 1.0, 0.0).normalize(), Deg(90.0)); assert_ulps_eq!( vec3(2.0f32.sqrt() / 2.0, -2.0f32.sqrt() / 2.0, 0.0), rot * vec ); } #[test] fn test_yz() { let vec = vec3(1.0, 0.0, 0.0); let rot = Quaternion::from_axis_angle(vec3(0.0, 1.0, 1.0).normalize(), Deg(-90.0)); assert_ulps_eq!( vec3(0.0, -2.0f32.sqrt() / 2.0, 2.0f32.sqrt() / 2.0), rot * vec ); } #[test] fn test_xz() { let vec = vec3(0.0, 1.0, 0.0); let rot = Quaternion::from_axis_angle(vec3(1.0, 0.0, 1.0).normalize(), Deg(90.0)); assert_ulps_eq!( vec3(-2.0f32.sqrt() / 2.0, 0.0, 2.0f32.sqrt() / 2.0), rot * vec ); } } mod rotate_between_vectors { use cgmath::*; #[test] fn test_around_z_0() { let expected = Quaternion::new(1.0, 0.0, 0.0, 0.0); let a = vec3(12.0, 0.0, 0.0); let b = vec3(1.0, 0.0, 0.0); assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); } #[test] fn test_around_z_90_cw() { let expected = Quaternion::new(0.5_f32.sqrt(), 0.0, 0.0, 0.5_f32.sqrt()); let a = vec3(8.0, 0.0, 0.0); let b = vec3(0.0, 9.0, 0.0); assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); } #[test] fn test_around_z_90_ccw() { let expected = Quaternion::new(0.5_f32.sqrt(), 0.0, 0.0, -0.5_f32.sqrt()); let a = vec3(-26.0, 0.0, 0.0); let b = vec3(0.0, 10.0, 0.0); assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); } #[test] fn test_around_z_180_cw() { let expected = Quaternion::new(0.0, 0.0, 0.0, 1.0); let a = vec3(10.0, 0.0, 0.0); let b = vec3(-5.0, 0.0, 0.0); assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); } #[test] fn test_around_z_180_ccw() { let expected = Quaternion::new(0.0, 0.0, 0.0, -1.0); let a = vec3(-3.0, 0.0, 0.0); let b = vec3(40.0, 0.0, 0.0); assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); } } mod cast { use cgmath::*; #[test] fn test_cast() { assert_ulps_eq!( Quaternion::new(0.9f64, 1.5, 2.4, 7.6).cast().unwrap(), Quaternion::new(0.9f32, 1.5, 2.4, 7.6) ); } } cgmath-0.18.0/tests/rotation.rs010064400007650000024000000026001376272520100145740ustar 00000000000000// Copyright 2015 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. extern crate cgmath; use cgmath::*; mod rotation { use super::cgmath::*; pub fn a2>() -> R { Rotation2::from_angle(Deg(30.0)) } pub fn a3>() -> R { let axis = Vector3::new(1.0, 1.0, 0.0).normalize(); Rotation3::from_axis_angle(axis, Deg(30.0)) } } #[test] fn test_invert_basis2() { let a: Basis2<_> = rotation::a2(); let a = a * a.invert(); let a: &Matrix2<_> = a.as_ref(); assert!(a.is_identity()); } #[test] fn test_invert_basis3() { let a: Basis3<_> = rotation::a3(); let a = a * a.invert(); let a: &Matrix3<_> = a.as_ref(); assert!(a.is_identity()); } cgmath-0.18.0/tests/swizzle.rs010064400007650000024000000042511376272520100144500ustar 00000000000000// Copyright 2013-2017 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. #![cfg(feature = "swizzle")] extern crate cgmath; use cgmath::{Point1, Point2, Point3, Vector1, Vector2, Vector3, Vector4}; // Sanity checks #[test] fn test_point_swizzle() { let p1 = Point1::new(1.0); let p2 = Point2::new(1.0, 2.0); let p3 = Point3::new(1.0, 2.0, 3.0); assert_eq!(p1.x(), p1); assert_eq!(p2.x(), p1); assert_eq!(p2.y(), Point1::new(2.0)); assert_eq!(p2.xx(), Point2::new(1.0, 1.0)); assert_eq!(p2.xy(), p2); assert_eq!(p2.yx(), Point2::new(2.0, 1.0)); assert_eq!(p2.yy(), Point2::new(2.0, 2.0)); assert_eq!(p3.x(), p1); assert_eq!(p3.y(), Point1::new(2.0)); assert_eq!(p3.xy(), p2); assert_eq!(p3.zy(), Point2::new(3.0, 2.0)); assert_eq!(p3.yyx(), Point3::new(2.0, 2.0, 1.0)); } #[test] fn test_vector_swizzle() { let p1 = Vector1::new(1.0); let p2 = Vector2::new(1.0, 2.0); let p3 = Vector3::new(1.0, 2.0, 3.0); let p4 = Vector4::new(1.0, 2.0, 3.0, 4.0); assert_eq!(p1.x(), p1); assert_eq!(p2.x(), p1); assert_eq!(p2.y(), Vector1::new(2.0)); assert_eq!(p2.xx(), Vector2::new(1.0, 1.0)); assert_eq!(p2.xy(), p2); assert_eq!(p2.yx(), Vector2::new(2.0, 1.0)); assert_eq!(p2.yy(), Vector2::new(2.0, 2.0)); assert_eq!(p3.x(), p1); assert_eq!(p3.y(), Vector1::new(2.0)); assert_eq!(p3.xy(), p2); assert_eq!(p3.zy(), Vector2::new(3.0, 2.0)); assert_eq!(p3.yyx(), Vector3::new(2.0, 2.0, 1.0)); assert_eq!(p4.xyxy(), Vector4::new(1.0, 2.0, 1.0, 2.0)); } cgmath-0.18.0/tests/transform.rs010064400007650000024000000124161376272520100147560ustar 00000000000000// Copyright 2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. extern crate approx; extern crate cgmath; #[cfg(feature = "serde")] extern crate serde_json; use cgmath::*; #[test] fn test_mul() { let t1 = Decomposed { scale: 2.0f64, rot: Quaternion::new(0.5f64.sqrt(), 0.5f64.sqrt(), 0.0, 0.0), disp: Vector3::new(1.0f64, 2.0, 3.0), }; let t2 = Decomposed { scale: 3.0f64, rot: Quaternion::new(0.5f64.sqrt(), 0.0, 0.5f64.sqrt(), 0.0), disp: Vector3::new(-2.0, 1.0, 0.0), }; let actual = t1 * t2; let expected = Decomposed { scale: 6.0f64, rot: Quaternion::new(0.5, 0.5, 0.5, 0.5), disp: Vector3::new(-3.0, 2.0, 5.0), }; assert_ulps_eq!(actual, expected); } #[test] fn test_mul_one() { let t = Decomposed { scale: 2.0f64, rot: Quaternion::new(0.5f64.sqrt(), 0.5f64.sqrt(), 0.0, 0.0), disp: Vector3::new(1.0f64, 2.0, 3.0), }; let one = Decomposed::one(); assert_ulps_eq!(t * one, t); assert_ulps_eq!(one * t, t); } #[test] fn test_invert() { let v = Vector3::new(1.0f64, 2.0, 3.0); let t = Decomposed { scale: 1.5f64, rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5), disp: Vector3::new(6.0f64, -7.0, 8.0), }; let ti = t .inverse_transform() .expect("Expected successful inversion"); let vt = t.transform_vector(v); assert_ulps_eq!(&v, &ti.transform_vector(vt)); } #[test] fn test_inverse_vector() { let v = Vector3::new(1.0f64, 2.0, 3.0); let t = Decomposed { scale: 1.5f64, rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5), disp: Vector3::new(6.0f64, -7.0, 8.0), }; let vt = t .inverse_transform_vector(v) .expect("Expected successful inversion"); assert_ulps_eq!(v, t.transform_vector(vt)); } #[test] #[allow(deprecated)] fn test_look_at() { let eye = Point3::new(0.0f64, 0.0, -5.0); let center = Point3::new(0.0f64, 0.0, 0.0); let up = Vector3::new(1.0f64, 0.0, 0.0); let t: Decomposed, Quaternion> = Transform::look_at(eye, center, up); assert_ulps_eq!(t, Decomposed::, Quaternion>::look_at(eye, center, up)); let point = Point3::new(1.0f64, 0.0, 0.0); let view_point = Point3::new(0.0f64, 1.0, 5.0); assert_ulps_eq!(&t.transform_point(point), &view_point); } #[test] fn test_look_at_lh() { let eye = Point3::new(0.0f64, 0.0, -5.0); let center = Point3::new(0.0f64, 0.0, 0.0); let up = Vector3::new(1.0f64, 0.0, 0.0); let t: Decomposed, Quaternion> = Transform::look_at_lh(eye, center, up); assert_ulps_eq!(t, Decomposed::, Quaternion>::look_at_lh(eye, center, up)); let point = Point3::new(1.0f64, 0.0, 0.0); let view_point = Point3::new(0.0f64, 1.0, 5.0); assert_ulps_eq!(&t.transform_point(point), &view_point); // Decomposed::look_at_lh and Matrix4::look_at_lh should be consistent let t: Matrix4 = Transform::look_at_lh(eye, center, up); assert_ulps_eq!(t, Matrix4::::look_at_lh(eye, center, up)); assert_ulps_eq!(&t.transform_point(point), &view_point); // Decomposed::look_at is inconsistent and deprecated, but verify that the behvaior // remains the same until removed. #[allow(deprecated)] let t: Decomposed, Quaternion> = Transform::look_at(eye, center, up); assert_ulps_eq!(&t.transform_point(point), &view_point); } #[test] fn test_look_at_rh() { let eye = Point3::new(0.0f64, 0.0, -5.0); let center = Point3::new(0.0f64, 0.0, 0.0); let up = Vector3::new(1.0f64, 0.0, 0.0); let t: Decomposed, Quaternion> = Transform::look_at_rh(eye, center, up); assert_ulps_eq!(t, Decomposed::, Quaternion>::look_at_rh(eye, center, up)); let point = Point3::new(1.0f64, 0.0, 0.0); let view_point = Point3::new(0.0f64, 1.0, -5.0); assert_ulps_eq!(&t.transform_point(point), &view_point); // Decomposed::look_at_rh and Matrix4::look_at_rh should be consistent let t: Matrix4 = Transform::look_at_rh(eye, center, up); assert_ulps_eq!(t, Matrix4::::look_at_rh(eye, center, up)); assert_ulps_eq!(&t.transform_point(point), &view_point); } #[cfg(feature = "serde")] #[test] fn test_serialize() { let t = Decomposed { scale: 1.5f64, rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5), disp: Vector3::new(6.0f64, -7.0, 8.0), }; let serialized = serde_json::to_string(&t).unwrap(); let deserialized: Decomposed, Quaternion> = serde_json::from_str(&serialized).unwrap(); assert_ulps_eq!(&t, &deserialized); } cgmath-0.18.0/tests/vector.rs010064400007650000024000000263331376272520100142500ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // 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. extern crate approx; extern crate cgmath; use cgmath::*; use std::f64; use std::iter; #[test] fn test_constructor() { assert_eq!(vec1(1f32), Vector1::new(1f32)); assert_eq!(vec2(1f32, 2f32), Vector2::new(1f32, 2f32)); assert_eq!(vec3(1f64, 2f64, 3f64), Vector3::new(1f64, 2f64, 3f64)); assert_eq!( vec4(1isize, 2isize, 3isize, 4isize), Vector4::new(1isize, 2isize, 3isize, 4isize) ); } #[test] fn test_from_value() { assert_eq!( Vector2::from_value(102isize), Vector2::new(102isize, 102isize) ); assert_eq!( Vector3::from_value(22isize), Vector3::new(22isize, 22isize, 22isize) ); assert_eq!( Vector4::from_value(76.5f64), Vector4::new(76.5f64, 76.5f64, 76.5f64, 76.5f64) ); } macro_rules! impl_test_add { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector + vector ops assert_eq!($v + $v, $VectorN::new($($v.$field + $v.$field),+)); assert_eq!(&$v + &$v, $v + $v); assert_eq!(&$v + $v, $v + $v); assert_eq!($v + &$v, $v + $v); ) } macro_rules! impl_test_sub { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector - vector ops assert_eq!($v - $v, $VectorN::new($($v.$field - $v.$field),+)); assert_eq!(&$v - &$v, $v - $v); assert_eq!(&$v - $v, $v - $v); assert_eq!($v - &$v, $v - $v); ) } macro_rules! impl_test_mul { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector * scalar ops assert_eq!($v * $s, $VectorN::new($($v.$field * $s),+)); assert_eq!($s * $v, $VectorN::new($($s * $v.$field),+)); assert_eq!(&$v * $s, $v * $s); assert_eq!($s * &$v, $s * $v); // commutativity assert_eq!($v * $s, $s * $v); ) } macro_rules! impl_test_div { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector / scalar ops assert_eq!($v / $s, $VectorN::new($($v.$field / $s),+)); assert_eq!($s / $v, $VectorN::new($($s / $v.$field),+)); assert_eq!(&$v / $s, $v / $s); assert_eq!($s / &$v, $s / $v); ) } macro_rules! impl_test_rem { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector % scalar ops assert_eq!($v % $s, $VectorN::new($($v.$field % $s),+)); assert_eq!($s % $v, $VectorN::new($($s % $v.$field),+)); assert_eq!(&$v % $s, $v % $s); assert_eq!($s % &$v, $s % $v); ) } macro_rules! impl_test_iter_sum { ($VectorN:ident { $($field:ident),+ }, $ty:ty, $s:expr, $v:expr) => ( assert_eq!($VectorN::new($($v.$field * $s),+), iter::repeat($v).take($s as usize).sum()); ) } #[test] fn test_add() { impl_test_add!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); impl_test_add!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); impl_test_add!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); } #[test] fn test_sub() { impl_test_sub!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); impl_test_sub!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); impl_test_sub!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); } #[test] fn test_mul() { impl_test_mul!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); impl_test_mul!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); impl_test_mul!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); } #[test] fn test_div() { impl_test_div!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); impl_test_div!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); impl_test_div!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); } #[test] fn test_rem() { impl_test_rem!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); impl_test_rem!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); impl_test_rem!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); } #[test] fn test_dot() { assert_eq!(Vector2::new(1.0, 2.0).dot(Vector2::new(3.0, 4.0)), 11.0); assert_eq!( Vector3::new(1.0, 2.0, 3.0).dot(Vector3::new(4.0, 5.0, 6.0)), 32.0 ); assert_eq!( Vector4::new(1.0, 2.0, 3.0, 4.0).dot(Vector4::new(5.0, 6.0, 7.0, 8.0)), 70.0 ); } #[test] fn test_sum() { assert_eq!(Vector2::new(1isize, 2isize).sum(), 3isize); assert_eq!(Vector3::new(1isize, 2isize, 3isize).sum(), 6isize); assert_eq!(Vector4::new(1isize, 2isize, 3isize, 4isize).sum(), 10isize); assert_eq!(Vector2::new(3.0f64, 4.0f64).sum(), 7.0f64); assert_eq!(Vector3::new(4.0f64, 5.0f64, 6.0f64).sum(), 15.0f64); assert_eq!(Vector4::new(5.0f64, 6.0f64, 7.0f64, 8.0f64).sum(), 26.0f64); } #[test] fn test_iter_sum() { impl_test_iter_sum!( Vector4 { x, y, z, w }, f32, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0) ); impl_test_iter_sum!(Vector3 { x, y, z }, f32, 2.0f32, vec3(2.0f32, 4.0, 6.0)); impl_test_iter_sum!(Vector2 { x, y }, f32, 2.0f32, vec2(2.0f32, 4.0)); impl_test_iter_sum!(Vector4 { x, y, z, w }, usize, 2usize, vec4(2usize, 4, 6, 8)); impl_test_iter_sum!(Vector3 { x, y, z }, usize, 2usize, vec3(2usize, 4, 6)); impl_test_iter_sum!(Vector2 { x, y }, usize, 2usize, vec2(2usize, 4)); } #[test] fn test_product() { assert_eq!(Vector2::new(1isize, 2isize).product(), 2isize); assert_eq!(Vector3::new(1isize, 2isize, 3isize).product(), 6isize); assert_eq!( Vector4::new(1isize, 2isize, 3isize, 4isize).product(), 24isize ); assert_eq!(Vector2::new(3.0f64, 4.0f64).product(), 12.0f64); assert_eq!(Vector3::new(4.0f64, 5.0f64, 6.0f64).product(), 120.0f64); assert_eq!( Vector4::new(5.0f64, 6.0f64, 7.0f64, 8.0f64).product(), 1680.0f64 ); } #[test] fn test_cross() { let a = Vector3::new(1isize, 2isize, 3isize); let b = Vector3::new(4isize, 5isize, 6isize); let r = Vector3::new(-3isize, 6isize, -3isize); assert_eq!(a.cross(b), r); } #[test] fn test_is_perpendicular() { assert!(Vector2::new(1.0f64, 0.0f64).is_perpendicular(Vector2::new(0.0f64, 1.0f64))); assert!( Vector3::new(0.0f64, 1.0f64, 0.0f64).is_perpendicular(Vector3::new(0.0f64, 0.0f64, 1.0f64)) ); assert!(Vector4::new(1.0f64, 0.0f64, 0.0f64, 0.0f64) .is_perpendicular(Vector4::new(0.0f64, 0.0f64, 0.0f64, 1.0f64))); } #[cfg(test)] mod test_magnitude { use cgmath::*; #[test] fn test_vector2() { let (a, a_res) = (Vector2::new(3.0f64, 4.0f64), 5.0f64); // (3, 4, 5) Pythagorean triple let (b, b_res) = (Vector2::new(5.0f64, 12.0f64), 13.0f64); // (5, 12, 13) Pythagorean triple assert_eq!(a.magnitude2(), a_res * a_res); assert_eq!(b.magnitude2(), b_res * b_res); assert_eq!(a.magnitude(), a_res); assert_eq!(b.magnitude(), b_res); } #[test] fn test_vector3() { let (a, a_res) = (Vector3::new(2.0f64, 3.0f64, 6.0f64), 7.0f64); // (2, 3, 6, 7) Pythagorean quadruple let (b, b_res) = (Vector3::new(1.0f64, 4.0f64, 8.0f64), 9.0f64); // (1, 4, 8, 9) Pythagorean quadruple assert_eq!(a.magnitude2(), a_res * a_res); assert_eq!(b.magnitude2(), b_res * b_res); assert_eq!(a.magnitude(), a_res); assert_eq!(b.magnitude(), b_res); } #[test] fn test_vector4() { let (a, a_res) = (Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64), 11.0f64); // (1, 2, 4, 10, 11) Pythagorean quintuple let (b, b_res) = (Vector4::new(1.0f64, 2.0f64, 8.0f64, 10.0f64), 13.0f64); // (1, 2, 8, 10, 13) Pythagorean quintuple assert_eq!(a.magnitude2(), a_res * a_res); assert_eq!(b.magnitude2(), b_res * b_res); assert_eq!(a.magnitude(), a_res); assert_eq!(b.magnitude(), b_res); } } #[test] fn test_angle() { assert_ulps_eq!( Vector2::new(1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)), &Rad(f64::consts::FRAC_PI_2) ); assert_ulps_eq!( Vector2::new(10.0f64, 0.0f64).angle(Vector2::new(0.0f64, 5.0f64)), &Rad(f64::consts::FRAC_PI_2) ); assert_ulps_eq!( Vector2::new(-1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)), &-Rad(f64::consts::FRAC_PI_2) ); assert_ulps_eq!( Vector3::new(1.0f64, 0.0f64, 1.0f64).angle(Vector3::new(1.0f64, 1.0f64, 0.0f64)), &Rad(f64::consts::FRAC_PI_3) ); assert_ulps_eq!( Vector3::new(10.0f64, 0.0f64, 10.0f64).angle(Vector3::new(5.0f64, 5.0f64, 0.0f64)), &Rad(f64::consts::FRAC_PI_3) ); assert_ulps_eq!( Vector3::new(-1.0f64, 0.0f64, -1.0f64).angle(Vector3::new(1.0f64, -1.0f64, 0.0f64)), &Rad(2.0f64 * f64::consts::FRAC_PI_3) ); assert_ulps_eq!( Vector4::new(1.0f64, 0.0f64, 1.0f64, 0.0f64) .angle(Vector4::new(0.0f64, 1.0f64, 0.0f64, 1.0f64)), &Rad(f64::consts::FRAC_PI_2) ); assert_ulps_eq!( Vector4::new(10.0f64, 0.0f64, 10.0f64, 0.0f64) .angle(Vector4::new(0.0f64, 5.0f64, 0.0f64, 5.0f64)), &Rad(f64::consts::FRAC_PI_2) ); assert_ulps_eq!( Vector4::new(-1.0f64, 0.0f64, -1.0f64, 0.0f64) .angle(Vector4::new(0.0f64, 1.0f64, 0.0f64, 1.0f64)), &Rad(f64::consts::FRAC_PI_2) ); } #[test] fn test_normalize() { // TODO: test normalize_to, normalize_sel.0, and normalize_self_to assert_ulps_eq!( Vector2::new(3.0f64, 4.0f64).normalize(), &Vector2::new(3.0 / 5.0, 4.0 / 5.0) ); assert_ulps_eq!( Vector3::new(2.0f64, 3.0f64, 6.0f64).normalize(), &Vector3::new(2.0 / 7.0, 3.0 / 7.0, 6.0 / 7.0) ); assert_ulps_eq!( Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64).normalize(), &Vector4::new(1.0 / 11.0, 2.0 / 11.0, 4.0 / 11.0, 10.0 / 11.0) ); } #[test] fn test_project_on() { assert_ulps_eq!( Vector2::new(-1.0f64, 5.0).project_on(Vector2::new(2.0, 4.0)), &Vector2::new(9.0 / 5.0, 18.0 / 5.0) ); assert_ulps_eq!( Vector3::new(5.0f64, 6.0, 7.0).project_on(Vector3::new(1.0, 1.0, 1.0)), &Vector3::new(6.0, 6.0, 6.0) ); assert_ulps_eq!( Vector4::new(0.0f64, -5.0, 5.0, 5.0).project_on(Vector4::new(0.0, 1.0, 0.0, 0.5)), &Vector4::new(0.0, -2.0, 0.0, -1.0) ); } #[test] fn test_cast() { assert_ulps_eq!( Vector2::new(0.9f64, 1.5).cast().unwrap(), Vector2::new(0.9f32, 1.5) ); assert_ulps_eq!( Vector3::new(1.0f64, 2.4, -3.13).cast().unwrap(), Vector3::new(1.0f32, 2.4, -3.13) ); assert_ulps_eq!( Vector4::new(13.5f64, -4.6, -8.3, 2.41).cast().unwrap(), Vector4::new(13.5f32, -4.6, -8.3, 2.41) ); } cgmath-0.18.0/tests/vector4f32.rs010064400007650000024000000146641376272520100146530ustar 00000000000000// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0f32 (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.0f32 // // 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. extern crate approx; extern crate cgmath; use cgmath::*; use std::f32; #[test] fn test_constructor() { assert_eq!( vec4(1f32, 2f32, 3f32, 4f32), Vector4::new(1f32, 2f32, 3f32, 4f32) ); } #[test] fn test_from_value() { assert_eq!( Vector4::from_value(76.5f32), Vector4::new(76.5f32, 76.5f32, 76.5f32, 76.5f32) ); } macro_rules! impl_test_add { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector + vector ops assert_eq!($v + $v, $VectorN::new($($v.$field + $v.$field),+)); assert_eq!(&$v + &$v, $v + $v); assert_eq!(&$v + $v, $v + $v); assert_eq!($v + &$v, $v + $v); ) } macro_rules! impl_test_sub { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector - vector ops assert_eq!($v - $v, $VectorN::new($($v.$field - $v.$field),+)); assert_eq!(&$v - &$v, $v - $v); assert_eq!(&$v - $v, $v - $v); assert_eq!($v - &$v, $v - $v); ) } macro_rules! impl_test_mul { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector * scalar ops assert_eq!($v * $s, $VectorN::new($($v.$field * $s),+)); assert_eq!($s * $v, $VectorN::new($($s * $v.$field),+)); assert_eq!(&$v * $s, $v * $s); assert_eq!($s * &$v, $s * $v); // commutativity assert_eq!($v * $s, $s * $v); ) } macro_rules! impl_test_div { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector / scalar ops assert_eq!($v / $s, $VectorN::new($($v.$field / $s),+)); assert_eq!($s / $v, $VectorN::new($($s / $v.$field),+)); assert_eq!(&$v / $s, $v / $s); assert_eq!($s / &$v, $s / $v); ) } macro_rules! impl_test_rem { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( // vector % scalar ops assert_eq!($v % $s, $VectorN::new($($v.$field % $s),+)); assert_eq!($s % $v, $VectorN::new($($s % $v.$field),+)); assert_eq!(&$v % $s, $v % $s); assert_eq!($s % &$v, $s % $v); ) } #[test] fn test_add() { impl_test_add!( Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) ); } #[test] fn test_sub() { impl_test_sub!( Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) ); } #[test] fn test_mul() { impl_test_mul!( Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) ); } #[test] fn test_div() { impl_test_div!( Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) ); } #[test] fn test_rem() { impl_test_rem!( Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) ); } #[test] fn test_dot() { assert_eq!( Vector4::new(1.0f32, 2.0f32, 3.0f32, 4.0f32) .dot(Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32)), 70.0f32 ); } #[test] fn test_sum() { assert_eq!(Vector4::new(1f32, 2f32, 3f32, 4f32).sum(), 10f32); assert_eq!(Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32).sum(), 26.0f32); } #[test] fn test_product() { assert_eq!(Vector4::new(1f32, 2f32, 3f32, 4f32).product(), 24f32); assert_eq!( Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32).product(), 1680.0f32 ); } #[test] fn test_is_perpendicular() { assert!(Vector4::new(1.0f32, 0.0f32, 0.0f32, 0.0f32) .is_perpendicular(Vector4::new(0.0f32, 0.0f32, 0.0f32, 1.0f32))); } #[cfg(test)] mod test_magnitude { use cgmath::*; #[test] fn test_vector4() { let (a, a_res) = (Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32), 11.0f32); // (1, 2, 4, 10, 11) Pythagorean quintuple let (b, b_res) = (Vector4::new(1.0f32, 2.0f32, 8.0f32, 10.0f32), 13.0f32); // (1, 2, 8, 10, 13) Pythagorean quintuple assert_eq!(a.magnitude2(), a_res * a_res); assert_eq!(b.magnitude2(), b_res * b_res); assert_eq!(a.magnitude(), a_res); assert_eq!(b.magnitude(), b_res); #[cfg(feature = "simd")] { let a = Vector4::new(1f32, 4f32, 9f32, 16f32); assert_ulps_eq!(a.sqrt_element_wide(), Vector4::new(1f32, 2f32, 3f32, 4f32)); assert_relative_eq!( a.sqrt_element_wide().recip_element_wide(), Vector4::new(1f32, 1f32 / 2f32, 1f32 / 3f32, 1f32 / 4f32), max_relative = 0.005f32 ); assert_relative_eq!( a.rsqrt_element_wide(), Vector4::new(1f32, 1f32 / 2f32, 1f32 / 3f32, 1f32 / 4f32), max_relative = 0.005f32 ); } } } #[test] fn test_angle() { assert_ulps_eq!( Vector4::new(1.0f32, 0.0f32, 1.0f32, 0.0f32) .angle(Vector4::new(0.0f32, 1.0f32, 0.0f32, 1.0f32)), &Rad(f32::consts::FRAC_PI_2) ); assert_ulps_eq!( Vector4::new(10.0f32, 0.0f32, 10.0f32, 0.0f32) .angle(Vector4::new(0.0f32, 5.0f32, 0.0f32, 5.0f32)), &Rad(f32::consts::FRAC_PI_2) ); assert_ulps_eq!( Vector4::new(-1.0f32, 0.0f32, -1.0f32, 0.0f32) .angle(Vector4::new(0.0f32, 1.0f32, 0.0f32, 1.0f32)), &Rad(f32::consts::FRAC_PI_2) ); } #[test] fn test_normalize() { // TODO: test normalize_to, normalize_sel.0f32, and normalize_self_to assert_ulps_eq!( Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32).normalize(), &Vector4::new( 1.0f32 / 11.0f32, 2.0f32 / 11.0f32, 4.0f32 / 11.0f32, 10.0f32 / 11.0f32 ) ); } #[test] fn test_cast() { assert_ulps_eq!( Vector4::new(13.5f32, -4.6, -8.3, 2.41).cast().unwrap(), Vector4::new(13.5f32, -4.6, -8.3, 2.41) ); }