third_party/cargo: add cgmath
parent
b6071b0a51
commit
1fc9d9ff1a
|
@ -8,6 +8,10 @@ package(default_visibility = ["//visibility:public"])
|
|||
licenses([
|
||||
"notice" # See individual crates for specific licenses
|
||||
])
|
||||
alias(
|
||||
name = "cgmath",
|
||||
actual = "//third_party/cargo/vendor/cgmath-0.17.0:cgmath",
|
||||
)
|
||||
alias(
|
||||
name = "env_logger",
|
||||
actual = "//third_party/cargo/vendor/env_logger-0.6.2:env_logger",
|
||||
|
|
|
@ -112,6 +112,16 @@ name = "cfg-if"
|
|||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cgmath"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
|
@ -138,6 +148,7 @@ dependencies = [
|
|||
name = "compile_with_bazel"
|
||||
version = "1.33.7"
|
||||
dependencies = [
|
||||
"cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vulkano 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1014,6 +1025,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum cocoa 0.18.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54"
|
||||
"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
|
||||
|
|
|
@ -12,6 +12,7 @@ log = "0.4.6"
|
|||
vulkano = "0.11.1"
|
||||
vulkano-win = "0.11.1"
|
||||
winit = "0.18.0"
|
||||
cgmath = "0.17.0"
|
||||
|
||||
[raze]
|
||||
workspace_path = "//third_party/cargo"
|
||||
|
@ -37,3 +38,6 @@ additional_flags = [
|
|||
|
||||
[raze.crates.libloading.'0.5.2']
|
||||
additional_deps = ['//third_party/cargo/patches:libloading_global_static']
|
||||
|
||||
[raze.crates.cgmath.'0.17.0']
|
||||
gen_buildrs = true
|
||||
|
|
|
@ -6,5 +6,5 @@ To do Rust vendoring first run:
|
|||
After changing Rust deps in Cargo.toml do:
|
||||
|
||||
cargo generate-lockfile
|
||||
cargo-vendor vendor -x
|
||||
cargo raze
|
||||
cargo vendor --versioned-dirs
|
||||
cargo-raze
|
||||
|
|
|
@ -39,6 +39,8 @@ rust_library(
|
|||
],
|
||||
version = "0.3.2",
|
||||
crate_features = [
|
||||
"default",
|
||||
"std",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"2da9965b39d778331e30375417e8a9dc0412eda7467c65bc338f7b08f8104cd8","Cargo.toml":"0c473a17c57fb8609d353f075491e66ea28b835b26a9895916d92c0382b80041","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"0f8d1be0ee2342b30c3ba1bc5530b9823aade548278925d7c0f57c0aade4cfcd","benches/common/macros.rs":"87bf502fc9726c488a065e6c3a190de078559eb9a74bdc8f2937344e76b1e94f","benches/construction.rs":"7ed917339a838a8252176441fc5fb0b737cf0ac10121436fa4d1cb82ca8325d5","benches/mat.rs":"63e1d7f4e363abf92ca4898a6cf1418cd8b484d88c313f33e96116d0b08f240f","benches/quat.rs":"ace3874169b64ddccb49d9231acf701bdadb2bdb59294eb5931f213087795030","benches/vec.rs":"7f780bdc8f36dd4b3a230c3626f2dc4faaf4b2cd543fa210414a8ca7a9414e87","build.rs":"7aed18c06d7b03df8e737ddeb11c0d716a6bce1f0217f29c9011ebe621bfbd4b","src/angle.rs":"270930870ddfe90b789c308bb562446587c64be3ee1416f419d417060c9e7882","src/conv.rs":"42092eb2b9edd523fc7dd37312343e5d58f8234c32f3683094f3ae2908988811","src/euler.rs":"f1ba30d199a9aafd8ee3d282ac4c2ffed61d2af91fa74539b31a2c00bc59fe05","src/lib.rs":"c5c633392010bcefadd9060a3729fca55300a8695bd31f7f8c025790831e8d90","src/macros.rs":"0781fb116a874f5d396143a362d8cc9ee8cd4d5518403cf3f30d029896e9c58d","src/matrix.rs":"d077d394ce03017892aefa1cd4559db4eb3a52a72c5ef6d16042a0bbf3ab7e76","src/num.rs":"54e2273d0b516de48c14328c08325717d9c8942f23665dd4325b43093263606a","src/point.rs":"d874f486365ee98c689516c0f08e58299ba22a229b12125f4bbf8c54632742d9","src/prelude.rs":"e02daa6ea97d656afb9828cff87cb9caf9567819df72ebcafcb6ed064ccbe616","src/projection.rs":"9963ee9e889b6ead50269b5d93358d65fa0f70417a74eaec195fae320eb049e7","src/quaternion.rs":"e96f430135ffb04306b7d18afa67f59d7f1ed44e44b4a9f187515ffb8713ab48","src/rotation.rs":"2fa93d4ea758550ab93cb1c4a56b605d821a7923e5d8c90c0da42326c3d69fce","src/structure.rs":"1c7d8eadc75da0fc8a45ba4b795bf1d2dcdc3e88e02ba6da7e6bdd4fc6dc6c8a","src/transform.rs":"b65bb8944c8b5759762c7196a921ad86dbc331c34cff25ade15739f01376d554","src/vector.rs":"17edb53c2d0f011ea19097505237697f97bff6fa623749b8d3143cc21f1279ed","tests/angle.rs":"d1e4b06bcd5e37fdb4ff201607c719eb3b116ac840766b24804e78bac39a3e5a","tests/matrix.rs":"58ee6ff98217ce2f81c61ee9d4a6f656e32ae4f04fad92d765f7e0db50fec468","tests/point.rs":"c8f33f2f25903e6de984250a3b6c5818f7ed506662140d5d316151c8107e14ae","tests/projection.rs":"f5156fd39109932726e1633ffcb12e4871a2bbb88469a8e45f74edb9f1b11f0c","tests/quaternion.rs":"f40c98d8ae77b02a8341a42d67268e3b88a9c788f5b5dbfc2cb06d12db49876c","tests/rotation.rs":"af5eb7db5519f10d7ab3e4dab65580c951de167e6f49ef4612e55845767c4698","tests/swizzle.rs":"4e6bd2b7beb70f57512f22f5bd3690a027ca196aef4b9f116c4e5d1a079d00c8","tests/transform.rs":"eaf27d9c4c2121bbd4e45a03654b8ba8d218319a98dd82d79617c91fece3e08a","tests/vector.rs":"56b1883a14accb35f82aa67f16cfc3260bd432bfb1786a0fd2e726537113137e","tests/vector4f32.rs":"61b197eba505fb58f4aec59f0690c2b501c55fdec167e32ae0e6793b53fcf41e"},"package":"283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7"}
|
|
@ -0,0 +1,96 @@
|
|||
"""
|
||||
cargo-raze crate build file.
|
||||
|
||||
DO NOT EDIT! Replaced on runs of cargo-raze
|
||||
"""
|
||||
package(default_visibility = [
|
||||
# Public for visibility by "@raze__crate__version//" targets.
|
||||
#
|
||||
# Prefer access through "//third_party/cargo", which limits external
|
||||
# visibility to explicit Cargo.toml dependencies.
|
||||
"//visibility:public",
|
||||
])
|
||||
|
||||
licenses([
|
||||
"notice", # "Apache-2.0"
|
||||
])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_rust//rust:rust.bzl",
|
||||
"rust_library",
|
||||
"rust_binary",
|
||||
"rust_test",
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "cgmath_build_script",
|
||||
srcs = glob(["**/*.rs"]),
|
||||
crate_root = "build.rs",
|
||||
edition = "2015",
|
||||
deps = [
|
||||
],
|
||||
rustc_flags = [
|
||||
"--cap-lints=allow",
|
||||
],
|
||||
crate_features = [
|
||||
],
|
||||
data = glob(["*"]),
|
||||
version = "0.17.0",
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "cgmath_build_script_executor",
|
||||
srcs = glob(["*", "**/*.rs"]),
|
||||
outs = ["cgmath_out_dir_outputs.tar.gz"],
|
||||
tools = [
|
||||
":cgmath_build_script",
|
||||
],
|
||||
tags = ["no-sandbox"],
|
||||
cmd = "mkdir -p $$(dirname $@)/cgmath_out_dir_outputs/;"
|
||||
+ " (export CARGO_MANIFEST_DIR=\"$$PWD/$$(dirname $(location :Cargo.toml))\";"
|
||||
# TODO(acmcarther): This needs to be revisited as part of the cross compilation story.
|
||||
# See also: https://github.com/google/cargo-raze/pull/54
|
||||
+ " export TARGET='x86_64-unknown-linux-gnu';"
|
||||
+ " export RUST_BACKTRACE=1;"
|
||||
+ " export OUT_DIR=$$PWD/$$(dirname $@)/cgmath_out_dir_outputs;"
|
||||
+ " export BINARY_PATH=\"$$PWD/$(location :cgmath_build_script)\";"
|
||||
+ " export OUT_TAR=$$PWD/$@;"
|
||||
+ " cd $$(dirname $(location :Cargo.toml)) && $$BINARY_PATH && tar -czf $$OUT_TAR -C $$OUT_DIR .)"
|
||||
)
|
||||
|
||||
# Unsupported target "angle" with type "test" omitted
|
||||
|
||||
rust_library(
|
||||
name = "cgmath",
|
||||
crate_root = "src/lib.rs",
|
||||
crate_type = "lib",
|
||||
edition = "2015",
|
||||
srcs = glob(["**/*.rs"]),
|
||||
deps = [
|
||||
"//third_party/cargo/vendor/approx-0.3.2:approx",
|
||||
"//third_party/cargo/vendor/num-traits-0.2.11:num_traits",
|
||||
"//third_party/cargo/vendor/rand-0.6.5:rand",
|
||||
],
|
||||
rustc_flags = [
|
||||
"--cap-lints=allow",
|
||||
],
|
||||
out_dir_tar = ":cgmath_build_script_executor",
|
||||
version = "0.17.0",
|
||||
crate_features = [
|
||||
],
|
||||
)
|
||||
|
||||
# Unsupported target "construction" with type "bench" omitted
|
||||
# Unsupported target "mat" with type "bench" omitted
|
||||
# Unsupported target "matrix" with type "test" omitted
|
||||
# Unsupported target "point" with type "test" omitted
|
||||
# Unsupported target "projection" with type "test" omitted
|
||||
# Unsupported target "quat" with type "bench" omitted
|
||||
# Unsupported target "quaternion" with type "test" omitted
|
||||
# Unsupported target "rotation" with type "test" omitted
|
||||
# Unsupported target "swizzle" with type "test" omitted
|
||||
# Unsupported target "transform" with type "test" omitted
|
||||
# Unsupported target "vec" with type "bench" omitted
|
||||
# Unsupported target "vector" with type "test" omitted
|
||||
# Unsupported target "vector4f32" with type "test" omitted
|
|
@ -0,0 +1,339 @@
|
|||
# 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
|
||||
- Move `lerp()` from `InnerSpace` to `VectorSpace`
|
||||
|
||||
## [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<f32>`,
|
||||
`Matrix4<f32>` and `Quaternion<f32>`. 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<Rad<S>>` 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<Rad<S>>`.
|
||||
- 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<S, A>` to
|
||||
`PerspectiveFov<S>`.
|
||||
- The following trait constraints were removed from `Angle`: `Debug`,
|
||||
`ScalarConv`, `Into<Rad<S>>`, `Into<Deg<S>>`.
|
||||
- `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
|
|
@ -0,0 +1,56 @@
|
|||
# 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.17.0"
|
||||
authors = ["Brendan Zabarauskas <bjzaba@yahoo.com.au>"]
|
||||
description = "A linear algebra and mathematics library for computer graphics."
|
||||
homepage = "https://github.com/brendanzab/cgmath"
|
||||
documentation = "https://docs.rs/cgmath"
|
||||
readme = "README.md"
|
||||
keywords = ["gamedev", "math", "matrix", "vector", "quaternion"]
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/brendanzab/cgmath"
|
||||
|
||||
[lib]
|
||||
name = "cgmath"
|
||||
[dependencies.approx]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.mint]
|
||||
version = "0.5"
|
||||
optional = true
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
|
||||
[dependencies.rand]
|
||||
version = "0.6"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["serde_derive"]
|
||||
optional = true
|
||||
|
||||
[dependencies.simd]
|
||||
version = "0.2"
|
||||
optional = true
|
||||
[dev-dependencies.glium]
|
||||
version = "0.23"
|
||||
|
||||
[dev-dependencies.serde_json]
|
||||
version = "1.0"
|
||||
|
||||
[features]
|
||||
swizzle = []
|
||||
unstable = []
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
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.
|
|
@ -0,0 +1,81 @@
|
|||
# cgmath-rs
|
||||
|
||||
[![Build Status](https://travis-ci.org/brendanzab/cgmath.svg?branch=master)](https://travis-ci.org/brendanzab/cgmath)
|
||||
[![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/brendanzab/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 }
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## Support
|
||||
|
||||
Contact `bjz` on irc.mozilla.org [#rust](http://mibbit.com/?server=irc.mozilla.org&channel=%23rust)
|
||||
and [#rust-gamedev](http://mibbit.com/?server=irc.mozilla.org&channel=%23rust-gamedev),
|
||||
or [post an issue](https://github.com/bjz/cgmath/issues/new) on Github.
|
|
@ -0,0 +1,82 @@
|
|||
// 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 = IsaacRng::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 = IsaacRng::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 = IsaacRng::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)
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// 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::{IsaacRng, Rng, FromEntropy};
|
||||
use test::Bencher;
|
||||
use cgmath::*;
|
||||
|
||||
#[path = "common/macros.rs"]
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
fn bench_from_axis_angle<T: Rotation3<f32>>(bh: &mut Bencher) {
|
||||
const LEN: usize = 1 << 13;
|
||||
|
||||
let mut rng = IsaacRng::from_entropy();
|
||||
|
||||
let axis: Vec<_> = (0..LEN).map(|_| rng.gen::<Vector3<f32>>()).collect();
|
||||
let angle: Vec<_> = (0..LEN).map(|_| rng.gen::<Rad<f32>>()).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::<Quaternion<f32>>(bh)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn _bench_rot3_from_axisangle(bh: &mut Bencher) {
|
||||
bench_from_axis_angle::<Basis3<f32>>(bh)
|
||||
}
|
||||
|
||||
bench_construction!(
|
||||
_bench_rot2_from_axisangle,
|
||||
Basis2<f32>,
|
||||
Basis2::from_angle[angle: Rad<f32>]
|
||||
);
|
||||
|
||||
bench_construction!(
|
||||
_bench_quat_from_euler_angles,
|
||||
Quaternion<f32>,
|
||||
Quaternion::from[src: Euler<Rad<f32>>]
|
||||
);
|
||||
bench_construction!(
|
||||
_bench_rot3_from_euler_angles,
|
||||
Basis3<f32>,
|
||||
Basis3::from[src: Euler<Rad<f32>>]
|
||||
);
|
|
@ -0,0 +1,65 @@
|
|||
// 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::{IsaacRng, Rng, FromEntropy};
|
||||
use std::ops::*;
|
||||
use test::Bencher;
|
||||
|
||||
use cgmath::*;
|
||||
|
||||
#[path = "common/macros.rs"]
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
bench_binop!(_bench_matrix2_mul_m, Matrix2<f32>, Matrix2<f32>, mul);
|
||||
bench_binop!(_bench_matrix3_mul_m, Matrix3<f32>, Matrix3<f32>, mul);
|
||||
bench_binop!(_bench_matrix4_mul_m, Matrix4<f32>, Matrix4<f32>, mul);
|
||||
|
||||
bench_binop!(_bench_matrix2_add_m, Matrix2<f32>, Matrix2<f32>, add);
|
||||
bench_binop!(_bench_matrix3_add_m, Matrix3<f32>, Matrix3<f32>, add);
|
||||
bench_binop!(_bench_matrix4_add_m, Matrix4<f32>, Matrix4<f32>, add);
|
||||
|
||||
bench_binop!(_bench_matrix2_sub_m, Matrix2<f32>, Matrix2<f32>, sub);
|
||||
bench_binop!(_bench_matrix3_sub_m, Matrix3<f32>, Matrix3<f32>, sub);
|
||||
bench_binop!(_bench_matrix4_sub_m, Matrix4<f32>, Matrix4<f32>, sub);
|
||||
|
||||
bench_binop!(_bench_matrix2_mul_v, Matrix2<f32>, Vector2<f32>, mul);
|
||||
bench_binop!(_bench_matrix3_mul_v, Matrix3<f32>, Vector3<f32>, mul);
|
||||
bench_binop!(_bench_matrix4_mul_v, Matrix4<f32>, Vector4<f32>, mul);
|
||||
|
||||
bench_binop!(_bench_matrix2_mul_s, Matrix2<f32>, f32, mul);
|
||||
bench_binop!(_bench_matrix3_mul_s, Matrix3<f32>, f32, mul);
|
||||
bench_binop!(_bench_matrix4_mul_s, Matrix4<f32>, f32, mul);
|
||||
|
||||
bench_binop!(_bench_matrix2_div_s, Matrix2<f32>, f32, div);
|
||||
bench_binop!(_bench_matrix3_div_s, Matrix3<f32>, f32, div);
|
||||
bench_binop!(_bench_matrix4_div_s, Matrix4<f32>, f32, div);
|
||||
|
||||
bench_unop!(_bench_matrix2_invert, Matrix2<f32>, invert);
|
||||
bench_unop!(_bench_matrix3_invert, Matrix3<f32>, invert);
|
||||
bench_unop!(_bench_matrix4_invert, Matrix4<f32>, invert);
|
||||
|
||||
bench_unop!(_bench_matrix2_transpose, Matrix2<f32>, transpose);
|
||||
bench_unop!(_bench_matrix3_transpose, Matrix3<f32>, transpose);
|
||||
bench_unop!(_bench_matrix4_transpose, Matrix4<f32>, transpose);
|
||||
|
||||
bench_unop!(_bench_matrix4_determinant, Matrix4<f32>, determinant);
|
|
@ -0,0 +1,40 @@
|
|||
// 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)]
|
||||
|
||||
extern crate cgmath;
|
||||
extern crate rand;
|
||||
extern crate test;
|
||||
|
||||
use rand::{IsaacRng, Rng, FromEntropy};
|
||||
use std::ops::*;
|
||||
use test::Bencher;
|
||||
|
||||
use cgmath::*;
|
||||
|
||||
#[path = "common/macros.rs"]
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
bench_binop!(_bench_quat_add_q, Quaternion<f32>, Quaternion<f32>, add);
|
||||
bench_binop!(_bench_quat_sub_q, Quaternion<f32>, Quaternion<f32>, sub);
|
||||
bench_binop!(_bench_quat_mul_q, Quaternion<f32>, Quaternion<f32>, mul);
|
||||
bench_binop!(_bench_quat_mul_v, Quaternion<f32>, Vector3<f32>, mul);
|
||||
bench_binop!(_bench_quat_mul_s, Quaternion<f32>, f32, mul);
|
||||
bench_binop!(_bench_quat_div_s, Quaternion<f32>, f32, div);
|
||||
bench_unop!(_bench_quat_invert, Quaternion<f32>, invert);
|
||||
bench_unop!(_bench_quat_conjugate, Quaternion<f32>, conjugate);
|
||||
bench_unop!(_bench_quat_normalize, Quaternion<f32>, normalize);
|
|
@ -0,0 +1,65 @@
|
|||
// 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::{IsaacRng, Rng, FromEntropy};
|
||||
use std::ops::*;
|
||||
use test::Bencher;
|
||||
|
||||
use cgmath::*;
|
||||
|
||||
#[path = "common/macros.rs"]
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
bench_binop!(_bench_vector2_add_v, Vector2<f32>, Vector2<f32>, add);
|
||||
bench_binop!(_bench_vector3_add_v, Vector3<f32>, Vector3<f32>, add);
|
||||
bench_binop!(_bench_vector4_add_v, Vector4<f32>, Vector4<f32>, add);
|
||||
|
||||
bench_binop!(_bench_vector2_sub_v, Vector2<f32>, Vector2<f32>, sub);
|
||||
bench_binop!(_bench_vector3_sub_v, Vector3<f32>, Vector3<f32>, sub);
|
||||
bench_binop!(_bench_vector4_sub_v, Vector4<f32>, Vector4<f32>, sub);
|
||||
|
||||
bench_binop!(_bench_vector2_mul_s, Vector2<f32>, f32, mul);
|
||||
bench_binop!(_bench_vector3_mul_s, Vector3<f32>, f32, mul);
|
||||
bench_binop!(_bench_vector4_mul_s, Vector4<f32>, f32, mul);
|
||||
|
||||
bench_binop!(_bench_vector2_div_s, Vector2<f32>, f32, div);
|
||||
bench_binop!(_bench_vector3_div_s, Vector3<f32>, f32, div);
|
||||
bench_binop!(_bench_vector4_div_s, Vector4<f32>, f32, div);
|
||||
|
||||
bench_binop!(_bench_vector2_rem_s, Vector2<f32>, f32, rem);
|
||||
bench_binop!(_bench_vector3_rem_s, Vector3<f32>, f32, rem);
|
||||
bench_binop!(_bench_vector4_rem_s, Vector4<f32>, f32, rem);
|
||||
|
||||
bench_binop!(_bench_vector2_dot, Vector2<f32>, Vector2<f32>, dot);
|
||||
bench_binop!(_bench_vector3_dot, Vector3<f32>, Vector3<f32>, dot);
|
||||
bench_binop!(_bench_vector4_dot, Vector4<f32>, Vector4<f32>, dot);
|
||||
|
||||
bench_binop!(_bench_vector3_cross, Vector3<f32>, Vector3<f32>, cross);
|
||||
|
||||
bench_unop!(_bench_vector2_magnitude, Vector2<f32>, magnitude);
|
||||
bench_unop!(_bench_vector3_magnitude, Vector3<f32>, magnitude);
|
||||
bench_unop!(_bench_vector4_magnitude, Vector4<f32>, magnitude);
|
||||
|
||||
bench_unop!(_bench_vector2_normalize, Vector2<f32>, normalize);
|
||||
bench_unop!(_bench_vector3_normalize, Vector3<f32>, normalize);
|
||||
bench_unop!(_bench_vector4_normalize, Vector4<f32>, normalize);
|
|
@ -0,0 +1,96 @@
|
|||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::env;
|
||||
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.");
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
// 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::fmt;
|
||||
use std::f64;
|
||||
use std::iter;
|
||||
use std::ops::*;
|
||||
|
||||
use rand::Rng;
|
||||
use rand::distributions::{Distribution, Standard};
|
||||
use rand::distributions::uniform::SampleUniform;
|
||||
use num_traits::{cast, Bounded};
|
||||
|
||||
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<S>(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<S>(pub S);
|
||||
|
||||
impl<S> From<Rad<S>> for Deg<S>
|
||||
where
|
||||
S: BaseFloat,
|
||||
{
|
||||
#[inline]
|
||||
fn from(rad: Rad<S>) -> Deg<S> {
|
||||
Deg(rad.0 * cast(180.0 / f64::consts::PI).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<Deg<S>> for Rad<S>
|
||||
where
|
||||
S: BaseFloat,
|
||||
{
|
||||
#[inline]
|
||||
fn from(deg: Deg<S>) -> Rad<S> {
|
||||
Rad(deg.0 * cast(f64::consts::PI / 180.0).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_angle {
|
||||
($Angle:ident, $fmt:expr, $full_turn:expr, $hi:expr) => {
|
||||
impl<S: BaseFloat> Zero for $Angle<S> {
|
||||
#[inline]
|
||||
fn zero() -> $Angle<S> {
|
||||
$Angle(S::zero())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_zero(&self) -> bool {
|
||||
ulps_eq!(self, &Self::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> iter::Sum<$Angle<S>> for $Angle<S> {
|
||||
#[inline]
|
||||
fn sum<I: Iterator<Item=$Angle<S>>>(iter: I) -> $Angle<S> {
|
||||
iter.fold($Angle::zero(), Add::add)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + BaseFloat> iter::Sum<&'a $Angle<S>> for $Angle<S> {
|
||||
#[inline]
|
||||
fn sum<I: Iterator<Item=&'a $Angle<S>>>(iter: I) -> $Angle<S> {
|
||||
iter.fold($Angle::zero(), Add::add)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Angle for $Angle<S> {
|
||||
type Unitless = S;
|
||||
|
||||
#[inline] fn full_turn() -> $Angle<S> { $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<S> { Rad(a.asin()).into() }
|
||||
#[inline] fn acos(a: S) -> $Angle<S> { Rad(a.acos()).into() }
|
||||
#[inline] fn atan(a: S) -> $Angle<S> { Rad(a.atan()).into() }
|
||||
#[inline] fn atan2(a: S, b: S) -> $Angle<S> { Rad(a.atan2(b)).into() }
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Neg for $Angle<S> {
|
||||
type Output = $Angle<S>;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> $Angle<S> { $Angle(-self.0) }
|
||||
}
|
||||
|
||||
impl<'a, S: BaseFloat> Neg for &'a $Angle<S> {
|
||||
type Output = $Angle<S>;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> $Angle<S> { $Angle(-self.0) }
|
||||
}
|
||||
|
||||
impl<S: Bounded> Bounded for $Angle<S> {
|
||||
#[inline]
|
||||
fn min_value() -> $Angle<S> {
|
||||
$Angle(S::min_value())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn max_value() -> $Angle<S> {
|
||||
$Angle(S::max_value())
|
||||
}
|
||||
}
|
||||
|
||||
impl_operator!(<S: BaseFloat> Add<$Angle<S> > for $Angle<S> {
|
||||
fn add(lhs, rhs) -> $Angle<S> { $Angle(lhs.0 + rhs.0) }
|
||||
});
|
||||
impl_operator!(<S: BaseFloat> Sub<$Angle<S> > for $Angle<S> {
|
||||
fn sub(lhs, rhs) -> $Angle<S> { $Angle(lhs.0 - rhs.0) }
|
||||
});
|
||||
impl_operator!(<S: BaseFloat> Div<$Angle<S> > for $Angle<S> {
|
||||
fn div(lhs, rhs) -> S { lhs.0 / rhs.0 }
|
||||
});
|
||||
impl_operator!(<S: BaseFloat> Rem<$Angle<S> > for $Angle<S> {
|
||||
fn rem(lhs, rhs) -> $Angle<S> { $Angle(lhs.0 % rhs.0) }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseFloat> AddAssign<$Angle<S> > for $Angle<S> {
|
||||
fn add_assign(&mut self, other) { self.0 += other.0; }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseFloat> SubAssign<$Angle<S> > for $Angle<S> {
|
||||
fn sub_assign(&mut self, other) { self.0 -= other.0; }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseFloat> RemAssign<$Angle<S> > for $Angle<S> {
|
||||
fn rem_assign(&mut self, other) { self.0 %= other.0; }
|
||||
});
|
||||
|
||||
impl_operator!(<S: BaseFloat> Mul<S> for $Angle<S> {
|
||||
fn mul(lhs, scalar) -> $Angle<S> { $Angle(lhs.0 * scalar) }
|
||||
});
|
||||
impl_operator!(<S: BaseFloat> Div<S> for $Angle<S> {
|
||||
fn div(lhs, scalar) -> $Angle<S> { $Angle(lhs.0 / scalar) }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseFloat> MulAssign<S> for $Angle<S> {
|
||||
fn mul_assign(&mut self, scalar) { self.0 *= scalar; }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseFloat> DivAssign<S> for $Angle<S> {
|
||||
fn div_assign(&mut self, scalar) { self.0 /= scalar; }
|
||||
});
|
||||
|
||||
impl<S: BaseFloat> approx::AbsDiffEq for $Angle<S> {
|
||||
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<S: BaseFloat> approx::RelativeEq for $Angle<S> {
|
||||
#[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<S: BaseFloat> approx::UlpsEq for $Angle<S> {
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Distribution<$Angle<S>> for Standard
|
||||
where Standard: Distribution<S>,
|
||||
S: BaseFloat + SampleUniform {
|
||||
#[inline]
|
||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $Angle<S> {
|
||||
$Angle(rng.gen_range(cast::<_, S>(-$hi).unwrap(), cast::<_, S>($hi).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: fmt::Debug> fmt::Debug for $Angle<S> {
|
||||
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);
|
|
@ -0,0 +1,84 @@
|
|||
//! 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!
|
||||
//!
|
||||
//! ```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:
|
||||
//!
|
||||
//! ```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<T, A: Into<[T; 2]>>(value: A) -> [T; 2] {
|
||||
value.into()
|
||||
}
|
||||
|
||||
/// Force a conversion into a 3-element array.
|
||||
#[inline]
|
||||
pub fn array3<T, A: Into<[T; 3]>>(value: A) -> [T; 3] {
|
||||
value.into()
|
||||
}
|
||||
|
||||
/// Force a conversion into a 4-element array.
|
||||
#[inline]
|
||||
pub fn array4<T, A: Into<[T; 4]>>(value: A) -> [T; 4] {
|
||||
value.into()
|
||||
}
|
||||
|
||||
/// Force a conversion into a 2x2-element array.
|
||||
#[inline]
|
||||
pub fn array2x2<T, A: Into<[[T; 2]; 2]>>(value: A) -> [[T; 2]; 2] {
|
||||
value.into()
|
||||
}
|
||||
|
||||
/// Force a conversion into a 3x3-element array.
|
||||
#[inline]
|
||||
pub fn array3x3<T, A: Into<[[T; 3]; 3]>>(value: A) -> [[T; 3]; 3] {
|
||||
value.into()
|
||||
}
|
||||
|
||||
/// Force a conversion into a 4x4-element array.
|
||||
#[inline]
|
||||
pub fn array4x4<T, A: Into<[[T; 4]; 4]>>(value: A) -> [[T; 4]; 4] {
|
||||
value.into()
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
// 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 rand::distributions::{Distribution, Standard};
|
||||
use rand::Rng;
|
||||
use num_traits::cast;
|
||||
|
||||
use structure::*;
|
||||
|
||||
use angle::Rad;
|
||||
use approx;
|
||||
use quaternion::Quaternion;
|
||||
#[cfg(feature = "mint")]
|
||||
use mint;
|
||||
use num::BaseFloat;
|
||||
|
||||
/// 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<Euler<A>>` 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<A> {
|
||||
/// 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<A> Euler<A> {
|
||||
/// 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<A> {
|
||||
Euler { x: x, y: y, z: z }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<Quaternion<S>> for Euler<Rad<S>> {
|
||||
fn from(src: Quaternion<S>) -> Euler<Rad<S>> {
|
||||
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<A: Angle> approx::AbsDiffEq for Euler<A> {
|
||||
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<A: Angle> approx::RelativeEq for Euler<A> {
|
||||
#[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<A: Angle> approx::UlpsEq for Euler<A> {
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Distribution<Euler<A>> for Standard
|
||||
where Standard: Distribution<A>,
|
||||
A: Angle {
|
||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Euler<A> {
|
||||
Euler {
|
||||
x: rng.gen(),
|
||||
y: rng.gen(),
|
||||
z: rng.gen(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
type MintEuler<S> = mint::EulerAngles<S, mint::IntraXYZ>;
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<S, A: Angle + From<S>> From<MintEuler<S>> for Euler<A> {
|
||||
fn from(mint: MintEuler<S>) -> Self {
|
||||
Euler {
|
||||
x: mint.a.into(),
|
||||
y: mint.b.into(),
|
||||
z: mint.c.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<S: Clone, A: Angle + Into<S>> Into<MintEuler<S>> for Euler<A> {
|
||||
fn into(self) -> MintEuler<S> {
|
||||
MintEuler::from([self.x.into(), self.y.into(), self.z.into()])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
// 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 trait 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;
|
||||
|
||||
extern crate rand;
|
||||
pub extern crate num_traits;
|
||||
|
||||
#[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, Vector1, Vector2, Vector3, Vector4, vec1, vec2, vec3, vec4};
|
||||
|
||||
pub use angle::{Deg, Rad};
|
||||
pub use euler::Euler;
|
||||
pub use point::{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;
|
||||
mod vector;
|
||||
|
||||
mod angle;
|
||||
mod euler;
|
||||
mod point;
|
||||
mod rotation;
|
||||
mod transform;
|
||||
|
||||
mod projection;
|
|
@ -0,0 +1,480 @@
|
|||
// 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]
|
||||
|
||||
/// 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]
|
||||
fn $op(self) -> $Output {
|
||||
let $x = self; $body
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, $S: $Constraint> $Op for &'a $Lhs {
|
||||
type Output = $Output;
|
||||
#[inline]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
macro_rules! impl_operator_default {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
macro_rules! impl_assignment_operator_default {
|
||||
(<$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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// 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<S: Clone> Into<mint::$Mint<S>> for $ArrayN<S> {
|
||||
#[inline]
|
||||
fn into(self) -> mint::$Mint<S> {
|
||||
mint::$Mint::from([$(self.$field),+])
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<mint::$Mint<S>> for $ArrayN<S> {
|
||||
#[inline]
|
||||
fn from(v: mint::$Mint<S>) -> Self {
|
||||
$ArrayN { $( $field: v.$field, )+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/swizzle_operator_macro.rs"));
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,73 @@
|
|||
// 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<T> 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<Epsilon = Self>
|
||||
+ approx::RelativeEq<Epsilon = Self>
|
||||
+ approx::UlpsEq<Epsilon = Self>
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> BaseFloat for T
|
||||
where
|
||||
T: BaseNum
|
||||
+ Float
|
||||
+ approx::AbsDiffEq<Epsilon = Self>
|
||||
+ approx::RelativeEq<Epsilon = Self>
|
||||
+ approx::UlpsEq<Epsilon = Self>,
|
||||
{
|
||||
}
|
|
@ -0,0 +1,586 @@
|
|||
// 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, 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<S> {
|
||||
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<S> {
|
||||
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<S> {
|
||||
pub x: S,
|
||||
pub y: S,
|
||||
pub z: S,
|
||||
}
|
||||
|
||||
impl<S: BaseNum> Point3<S> {
|
||||
#[inline]
|
||||
pub fn from_homogeneous(v: Vector4<S>) -> Point3<S> {
|
||||
let e = v.truncate() * (S::one() / v.w);
|
||||
Point3::new(e.x, e.y, e.z) //FIXME
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_homogeneous(self) -> Vector4<S> {
|
||||
Vector4::new(self.x, self.y, self.z, S::one())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_point {
|
||||
($PointN:ident { $($field:ident),+ }, $VectorN:ident, $n:expr) => {
|
||||
impl<S> $PointN<S> {
|
||||
/// Construct a new point, using the provided values.
|
||||
#[inline]
|
||||
pub const fn new($($field: S),+) -> $PointN<S> {
|
||||
$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<U, F>(self, mut f: F) -> $PointN<U>
|
||||
where F: FnMut(S) -> U
|
||||
{
|
||||
$PointN { $($field: f(self.$field)),+ }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseNum> Array for $PointN<S> {
|
||||
type Element = S;
|
||||
|
||||
#[inline]
|
||||
fn len() -> usize {
|
||||
$n
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_value(scalar: S) -> $PointN<S> {
|
||||
$PointN { $($field: scalar),+ }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sum(self) -> S where S: Add<Output = S> {
|
||||
fold_array!(add, { $(self.$field),+ })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn product(self) -> S where S: Mul<Output = S> {
|
||||
fold_array!(mul, { $(self.$field),+ })
|
||||
}
|
||||
|
||||
fn is_finite(&self) -> bool where S: BaseFloat {
|
||||
$(self.$field.is_finite())&&+
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: NumCast + Copy> $PointN<S> {
|
||||
/// Component-wise casting to another type
|
||||
#[inline]
|
||||
pub fn cast<T: NumCast>(&self) -> Option<$PointN<T>> {
|
||||
$(
|
||||
let $field = match NumCast::from(self.$field) {
|
||||
Some(field) => field,
|
||||
None => return None
|
||||
};
|
||||
)+
|
||||
Some($PointN { $($field),+ })
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> MetricSpace for $PointN<S> {
|
||||
type Metric = S;
|
||||
|
||||
#[inline]
|
||||
fn distance2(self, other: Self) -> S {
|
||||
(other - self).magnitude2()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseNum> EuclideanSpace for $PointN<S> {
|
||||
type Scalar = S;
|
||||
type Diff = $VectorN<S>;
|
||||
|
||||
#[inline]
|
||||
fn origin() -> $PointN<S> {
|
||||
$PointN { $($field: S::zero()),+ }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_vec(v: $VectorN<S>) -> $PointN<S> {
|
||||
$PointN::new($(v.$field),+)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_vec(self) -> $VectorN<S> {
|
||||
$VectorN::new($(self.$field),+)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dot(self, v: $VectorN<S>) -> S {
|
||||
$VectorN::new($(self.$field * v.$field),+).sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> approx::AbsDiffEq for $PointN<S> {
|
||||
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<S: BaseFloat> approx::RelativeEq for $PointN<S> {
|
||||
#[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<S: BaseFloat> approx::UlpsEq for $PointN<S> {
|
||||
#[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<S: Bounded> Bounded for $PointN<S> {
|
||||
#[inline]
|
||||
fn min_value() -> $PointN<S> {
|
||||
$PointN { $($field: S::min_value()),+ }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn max_value() -> $PointN<S> {
|
||||
$PointN { $($field: S::max_value()),+ }
|
||||
}
|
||||
}
|
||||
|
||||
impl_operator!(<S: BaseNum> Add<$VectorN<S> > for $PointN<S> {
|
||||
fn add(lhs, rhs) -> $PointN<S> { $PointN::new($(lhs.$field + rhs.$field),+) }
|
||||
});
|
||||
impl_operator!(<S: BaseNum> Sub<$VectorN<S>> for $PointN<S> {
|
||||
fn sub(lhs, rhs) -> $PointN<S> { $PointN::new($(lhs.$field - rhs.$field),+) }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseNum> AddAssign<$VectorN<S> > for $PointN<S> {
|
||||
fn add_assign(&mut self, vector) { $(self.$field += vector.$field);+ }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseNum> SubAssign<$VectorN<S>> for $PointN<S> {
|
||||
fn sub_assign(&mut self, vector) { $(self.$field -= vector.$field);+ }
|
||||
});
|
||||
|
||||
impl_operator!(<S: BaseNum> Sub<$PointN<S> > for $PointN<S> {
|
||||
fn sub(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field - rhs.$field),+) }
|
||||
});
|
||||
|
||||
impl_operator!(<S: BaseNum> Mul<S> for $PointN<S> {
|
||||
fn mul(point, scalar) -> $PointN<S> { $PointN::new($(point.$field * scalar),+) }
|
||||
});
|
||||
impl_operator!(<S: BaseNum> Div<S> for $PointN<S> {
|
||||
fn div(point, scalar) -> $PointN<S> { $PointN::new($(point.$field / scalar),+) }
|
||||
});
|
||||
impl_operator!(<S: BaseNum> Rem<S> for $PointN<S> {
|
||||
fn rem(point, scalar) -> $PointN<S> { $PointN::new($(point.$field % scalar),+) }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseNum> MulAssign<S> for $PointN<S> {
|
||||
fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseNum> DivAssign<S> for $PointN<S> {
|
||||
fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ }
|
||||
});
|
||||
impl_assignment_operator!(<S: BaseNum> RemAssign<S> for $PointN<S> {
|
||||
fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ }
|
||||
});
|
||||
|
||||
impl<S: BaseNum> ElementWise for $PointN<S> {
|
||||
#[inline] fn add_element_wise(self, rhs: $PointN<S>) -> $PointN<S> { $PointN::new($(self.$field + rhs.$field),+) }
|
||||
#[inline] fn sub_element_wise(self, rhs: $PointN<S>) -> $PointN<S> { $PointN::new($(self.$field - rhs.$field),+) }
|
||||
#[inline] fn mul_element_wise(self, rhs: $PointN<S>) -> $PointN<S> { $PointN::new($(self.$field * rhs.$field),+) }
|
||||
#[inline] fn div_element_wise(self, rhs: $PointN<S>) -> $PointN<S> { $PointN::new($(self.$field / rhs.$field),+) }
|
||||
#[inline] fn rem_element_wise(self, rhs: $PointN<S>) -> $PointN<S> { $PointN::new($(self.$field % rhs.$field),+) }
|
||||
|
||||
#[inline] fn add_assign_element_wise(&mut self, rhs: $PointN<S>) { $(self.$field += rhs.$field);+ }
|
||||
#[inline] fn sub_assign_element_wise(&mut self, rhs: $PointN<S>) { $(self.$field -= rhs.$field);+ }
|
||||
#[inline] fn mul_assign_element_wise(&mut self, rhs: $PointN<S>) { $(self.$field *= rhs.$field);+ }
|
||||
#[inline] fn div_assign_element_wise(&mut self, rhs: $PointN<S>) { $(self.$field /= rhs.$field);+ }
|
||||
#[inline] fn rem_assign_element_wise(&mut self, rhs: $PointN<S>) { $(self.$field %= rhs.$field);+ }
|
||||
}
|
||||
|
||||
impl<S: BaseNum> ElementWise<S> for $PointN<S> {
|
||||
#[inline] fn add_element_wise(self, rhs: S) -> $PointN<S> { $PointN::new($(self.$field + rhs),+) }
|
||||
#[inline] fn sub_element_wise(self, rhs: S) -> $PointN<S> { $PointN::new($(self.$field - rhs),+) }
|
||||
#[inline] fn mul_element_wise(self, rhs: S) -> $PointN<S> { $PointN::new($(self.$field * rhs),+) }
|
||||
#[inline] fn div_element_wise(self, rhs: S) -> $PointN<S> { $PointN::new($(self.$field / rhs),+) }
|
||||
#[inline] fn rem_element_wise(self, rhs: S) -> $PointN<S> { $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<usize> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<u8> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<u16> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<u32> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<u64> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<isize> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<i8> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<i16> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<i32> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<i64> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<f32> { $($field),+ });
|
||||
impl_scalar_ops!($PointN<f64> { $($field),+ });
|
||||
|
||||
impl_index_operators!($PointN<S>, $n, S, usize);
|
||||
impl_index_operators!($PointN<S>, $n, [S], Range<usize>);
|
||||
impl_index_operators!($PointN<S>, $n, [S], RangeTo<usize>);
|
||||
impl_index_operators!($PointN<S>, $n, [S], RangeFrom<usize>);
|
||||
impl_index_operators!($PointN<S>, $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);
|
||||
impl_point!(Point2 { x, y }, Vector2, 2);
|
||||
impl_point!(Point3 { x, y, z }, Vector3, 3);
|
||||
|
||||
impl<S: Copy> Point1<S> {
|
||||
impl_swizzle_functions!(Point1, Point2, Point3, S, x);
|
||||
}
|
||||
|
||||
impl<S: Copy> Point2<S> {
|
||||
impl_swizzle_functions!(Point1, Point2, Point3, S, xy);
|
||||
}
|
||||
|
||||
impl<S: Copy> Point3<S> {
|
||||
impl_swizzle_functions!(Point1, Point2, Point3, S, xyz);
|
||||
}
|
||||
|
||||
impl_fixed_array_conversions!(Point1<S> { x: 0 }, 1);
|
||||
impl_fixed_array_conversions!(Point2<S> { x: 0, y: 1 }, 2);
|
||||
impl_fixed_array_conversions!(Point3<S> { x: 0, y: 1, z: 2 }, 3);
|
||||
|
||||
impl_tuple_conversions!(Point1<S> { x }, (S,));
|
||||
impl_tuple_conversions!(Point2<S> { x, y }, (S, S));
|
||||
impl_tuple_conversions!(Point3<S> { 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<S: fmt::Debug> fmt::Debug for Point1<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(write!(f, "Point1 "));
|
||||
<[S; 1] as fmt::Debug>::fmt(self.as_ref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: fmt::Debug> fmt::Debug for Point2<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(write!(f, "Point2 "));
|
||||
<[S; 2] as fmt::Debug>::fmt(self.as_ref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: fmt::Debug> fmt::Debug for Point3<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(write!(f, "Point3 "));
|
||||
<[S; 3] as fmt::Debug>::fmt(self.as_ref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod point2 {
|
||||
use point::*;
|
||||
|
||||
const POINT2: Point2<i32> = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod point3 {
|
||||
use point::*;
|
||||
|
||||
const POINT3: Point3<i32> = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//! 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;
|
|
@ -0,0 +1,281 @@
|
|||
// 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::Zero;
|
||||
use num_traits::cast;
|
||||
|
||||
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<S: BaseFloat, A: Into<Rad<S>>>(
|
||||
fovy: A,
|
||||
aspect: S,
|
||||
near: S,
|
||||
far: S,
|
||||
) -> Matrix4<S> {
|
||||
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<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
||||
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<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
||||
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<S> {
|
||||
pub fovy: Rad<S>,
|
||||
pub aspect: S,
|
||||
pub near: S,
|
||||
pub far: S,
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> PerspectiveFov<S> {
|
||||
pub fn to_perspective(&self) -> Perspective<S> {
|
||||
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<S: BaseFloat> From<PerspectiveFov<S>> for Matrix4<S> {
|
||||
fn from(persp: PerspectiveFov<S>) -> Matrix4<S> {
|
||||
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!(
|
||||
persp.aspect > S::zero(),
|
||||
"The aspect ratio cannot be below zero, found: {:?}",
|
||||
persp.aspect
|
||||
);
|
||||
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!(
|
||||
persp.far > persp.near,
|
||||
"The far plane cannot be closer than the near plane, 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<S> {
|
||||
pub left: S,
|
||||
pub right: S,
|
||||
pub bottom: S,
|
||||
pub top: S,
|
||||
pub near: S,
|
||||
pub far: S,
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<Perspective<S>> for Matrix4<S> {
|
||||
fn from(persp: Perspective<S>) -> Matrix4<S> {
|
||||
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<S> {
|
||||
pub left: S,
|
||||
pub right: S,
|
||||
pub bottom: S,
|
||||
pub top: S,
|
||||
pub near: S,
|
||||
pub far: S,
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<Ortho<S>> for Matrix4<S> {
|
||||
fn from(ortho: Ortho<S>) -> Matrix4<S> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,978 @@
|
|||
// 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 rand::distributions::{Distribution, Standard};
|
||||
use rand::Rng;
|
||||
use num_traits::{cast, NumCast};
|
||||
|
||||
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 = "simd")]
|
||||
use simd::f32x4 as Simdf32x4;
|
||||
|
||||
#[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<S> {
|
||||
/// The scalar part of the quaternion.
|
||||
pub s: S,
|
||||
/// The vector part of the quaternion.
|
||||
pub v: Vector3<S>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl From<Simdf32x4> for Quaternion<f32> {
|
||||
#[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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl Into<Simdf32x4> for Quaternion<f32> {
|
||||
#[inline]
|
||||
fn into(self) -> Simdf32x4 {
|
||||
let self_ref: &[f32; 4] = self.as_ref();
|
||||
Simdf32x4::load(self_ref.as_ref(), 0 as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Quaternion<S> {
|
||||
/// 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<S> {
|
||||
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<S>) -> Quaternion<S> {
|
||||
Quaternion { s: s, v: v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Quaternion<S> {
|
||||
/// 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<S>,
|
||||
dst: Vector3<S>,
|
||||
fallback: Option<Vector3<S>>,
|
||||
) -> Quaternion<S> {
|
||||
let mag_avg = (src.magnitude2() * dst.magnitude2()).sqrt();
|
||||
let dot = src.dot(dst);
|
||||
if ulps_eq!(dot, &mag_avg) {
|
||||
Quaternion::<S>::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<S> {
|
||||
Quaternion::from_sv(self.s, -self.v)
|
||||
}
|
||||
|
||||
/// Do a normalized linear interpolation with `other`, by `amount`.
|
||||
pub fn nlerp(self, other: Quaternion<S>, amount: S) -> Quaternion<S> {
|
||||
(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.
|
||||
///
|
||||
/// # 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, other: Quaternion<S>, amount: S) -> Quaternion<S> {
|
||||
let dot = self.dot(other);
|
||||
let dot_threshold = cast(0.9995f64).unwrap();
|
||||
|
||||
// if quaternions are close together use `nlerp`
|
||||
if dot > dot_threshold {
|
||||
self.nlerp(other, amount)
|
||||
} else {
|
||||
// stay within the domain of acos()
|
||||
// TODO REMOVE WHEN https://github.com/mozilla/rust/issues/12068 IS RESOLVED
|
||||
let robust_dot = if dot > S::one() {
|
||||
S::one()
|
||||
} else if dot < -S::one() {
|
||||
-S::one()
|
||||
} else {
|
||||
dot
|
||||
};
|
||||
|
||||
let theta = Rad::acos(robust_dot.clone());
|
||||
|
||||
let scale1 = Rad::sin(theta * (S::one() - amount));
|
||||
let scale2 = Rad::sin(theta * amount);
|
||||
|
||||
(self * scale1 + other * scale2) * Rad::sin(theta).recip()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_finite(&self) -> bool {
|
||||
self.s.is_finite() && self.v.is_finite()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Zero for Quaternion<S> {
|
||||
#[inline]
|
||||
fn zero() -> Quaternion<S> {
|
||||
Quaternion::from_sv(S::zero(), Vector3::zero())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_zero(&self) -> bool {
|
||||
ulps_eq!(self, &Quaternion::<S>::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> One for Quaternion<S> {
|
||||
#[inline]
|
||||
fn one() -> Quaternion<S> {
|
||||
Quaternion::from_sv(S::one(), Vector3::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> iter::Sum<Quaternion<S>> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn sum<I: Iterator<Item = Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
iter.fold(Quaternion::<S>::zero(), Add::add)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + BaseFloat> iter::Sum<&'a Quaternion<S>> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn sum<I: Iterator<Item = &'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
iter.fold(Quaternion::<S>::zero(), Add::add)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> iter::Product<Quaternion<S>> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item = Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
iter.fold(Quaternion::<S>::one(), Mul::mul)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Quaternion<S>> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item = &'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
iter.fold(Quaternion::<S>::one(), Mul::mul)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> VectorSpace for Quaternion<S> {
|
||||
type Scalar = S;
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> MetricSpace for Quaternion<S> {
|
||||
type Metric = S;
|
||||
|
||||
#[inline]
|
||||
fn distance2(self, other: Self) -> S {
|
||||
(other - self).magnitude2()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: NumCast + Copy> Quaternion<S> {
|
||||
/// Component-wise casting to another type.
|
||||
pub fn cast<T: BaseFloat>(&self) -> Option<Quaternion<T>> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl<S: BaseFloat> InnerSpace for Quaternion<S> {
|
||||
#[inline]
|
||||
fn dot(self, other: Quaternion<S>) -> S {
|
||||
self.s * other.s + self.v.dot(other.v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl<S: BaseFloat> InnerSpace for Quaternion<S> {
|
||||
#[inline]
|
||||
default fn dot(self, other: Quaternion<S>) -> S {
|
||||
self.s * other.s + self.v.dot(other.v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl InnerSpace for Quaternion<f32> {
|
||||
#[inline]
|
||||
fn dot(self, other: Quaternion<f32>) -> 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<A> From<Euler<A>> for Quaternion<A::Unitless>
|
||||
where
|
||||
A: Angle + Into<Rad<<A as Angle>::Unitless>>,
|
||||
{
|
||||
fn from(src: Euler<A>) -> Quaternion<A::Unitless> {
|
||||
// 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_operator!(<S: BaseFloat> Neg for Quaternion<S> {
|
||||
fn neg(quat) -> Quaternion<S> {
|
||||
Quaternion::from_sv(-quat.s, -quat.v)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_default!(<S: BaseFloat> Neg for Quaternion<S> {
|
||||
fn neg(quat) -> Quaternion<S> {
|
||||
Quaternion::from_sv(-quat.s, -quat.v)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_simd!{
|
||||
[Simdf32x4]; Neg for Quaternion<f32> {
|
||||
fn neg(lhs) -> Quaternion<f32> {
|
||||
(-lhs).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_operator!(<S: BaseFloat> Mul<S> for Quaternion<S> {
|
||||
fn mul(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::from_sv(lhs.s * rhs, lhs.v * rhs)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_default!(<S: BaseFloat> Mul<S> for Quaternion<S> {
|
||||
fn mul(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::from_sv(lhs.s * rhs, lhs.v * rhs)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_simd!{@rs
|
||||
[Simdf32x4]; Mul<f32> for Quaternion<f32> {
|
||||
fn mul(lhs, rhs) -> Quaternion<f32> {
|
||||
(lhs * rhs).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_assignment_operator!(<S: BaseFloat> MulAssign<S> for Quaternion<S> {
|
||||
fn mul_assign(&mut self, scalar) { self.s *= scalar; self.v *= scalar; }
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_assignment_operator_default!(<S: BaseFloat> MulAssign<S> for Quaternion<S> {
|
||||
fn mul_assign(&mut self, scalar) { self.s *= scalar; self.v *= scalar; }
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl MulAssign<f32> for Quaternion<f32> {
|
||||
fn mul_assign(&mut self, other: f32) {
|
||||
let s: Simdf32x4 = (*self).into();
|
||||
let other = Simdf32x4::splat(other);
|
||||
*self = (s * other).into();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_operator!(<S: BaseFloat> Div<S> for Quaternion<S> {
|
||||
fn div(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::from_sv(lhs.s / rhs, lhs.v / rhs)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_default!(<S: BaseFloat> Div<S> for Quaternion<S> {
|
||||
fn div(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::from_sv(lhs.s / rhs, lhs.v / rhs)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_simd!{@rs
|
||||
[Simdf32x4]; Div<f32> for Quaternion<f32> {
|
||||
fn div(lhs, rhs) -> Quaternion<f32> {
|
||||
(lhs / rhs).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_assignment_operator!(<S: BaseFloat> DivAssign<S> for Quaternion<S> {
|
||||
fn div_assign(&mut self, scalar) { self.s /= scalar; self.v /= scalar; }
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_assignment_operator_default!(<S: BaseFloat> DivAssign<S> for Quaternion<S> {
|
||||
fn div_assign(&mut self, scalar) { self.s /= scalar; self.v /= scalar; }
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl DivAssign<f32> for Quaternion<f32> {
|
||||
fn div_assign(&mut self, other: f32) {
|
||||
let s: Simdf32x4 = (*self).into();
|
||||
let other = Simdf32x4::splat(other);
|
||||
*self = (s / other).into();
|
||||
}
|
||||
}
|
||||
|
||||
impl_operator!(<S: BaseFloat> Rem<S> for Quaternion<S> {
|
||||
fn rem(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::from_sv(lhs.s % rhs, lhs.v % rhs)
|
||||
}
|
||||
});
|
||||
|
||||
impl_assignment_operator!(<S: BaseFloat> RemAssign<S> for Quaternion<S> {
|
||||
fn rem_assign(&mut self, scalar) { self.s %= scalar; self.v %= scalar; }
|
||||
});
|
||||
|
||||
impl_operator!(<S: BaseFloat> Mul<Vector3<S> > for Quaternion<S> {
|
||||
fn mul(lhs, rhs) -> Vector3<S> {{
|
||||
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
|
||||
}}
|
||||
});
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_operator!(<S: BaseFloat> Add<Quaternion<S> > for Quaternion<S> {
|
||||
fn add(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::from_sv(lhs.s + rhs.s, lhs.v + rhs.v)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_default!(<S: BaseFloat> Add<Quaternion<S> > for Quaternion<S> {
|
||||
fn add(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::from_sv(lhs.s + rhs.s, lhs.v + rhs.v)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_simd!{
|
||||
[Simdf32x4]; Add<Quaternion<f32>> for Quaternion<f32> {
|
||||
fn add(lhs, rhs) -> Quaternion<f32> {
|
||||
(lhs + rhs).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_assignment_operator!(<S: BaseFloat> AddAssign<Quaternion<S> > for Quaternion<S> {
|
||||
fn add_assign(&mut self, other) { self.s += other.s; self.v += other.v; }
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_assignment_operator_default!(<S: BaseFloat> AddAssign<Quaternion<S> > for Quaternion<S> {
|
||||
fn add_assign(&mut self, other) { self.s += other.s; self.v += other.v; }
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl AddAssign for Quaternion<f32> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
let s: Simdf32x4 = (*self).into();
|
||||
let rhs: Simdf32x4 = rhs.into();
|
||||
*self = (s + rhs).into();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_operator!(<S: BaseFloat> Sub<Quaternion<S> > for Quaternion<S> {
|
||||
fn sub(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::from_sv(lhs.s - rhs.s, lhs.v - rhs.v)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_default!(<S: BaseFloat> Sub<Quaternion<S> > for Quaternion<S> {
|
||||
fn sub(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::from_sv(lhs.s - rhs.s, lhs.v - rhs.v)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_simd!{
|
||||
[Simdf32x4]; Sub<Quaternion<f32>> for Quaternion<f32> {
|
||||
fn sub(lhs, rhs) -> Quaternion<f32> {
|
||||
(lhs - rhs).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_assignment_operator!(<S: BaseFloat> SubAssign<Quaternion<S> > for Quaternion<S> {
|
||||
fn sub_assign(&mut self, other) { self.s -= other.s; self.v -= other.v; }
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_assignment_operator_default!(<S: BaseFloat> SubAssign<Quaternion<S> > for Quaternion<S> {
|
||||
fn sub_assign(&mut self, other) { self.s -= other.s; self.v -= other.v; }
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl SubAssign for Quaternion<f32> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
let s: Simdf32x4 = (*self).into();
|
||||
let rhs: Simdf32x4 = rhs.into();
|
||||
*self = (s - rhs).into();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simd"))]
|
||||
impl_operator!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
|
||||
fn mul(lhs, rhs) -> Quaternion<S> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_default!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
|
||||
fn mul(lhs, rhs) -> Quaternion<S> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_simd!{
|
||||
[Simdf32x4]; Mul<Quaternion<f32>> for Quaternion<f32> {
|
||||
fn mul(lhs, rhs) -> Quaternion<f32> {
|
||||
{
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_scalar_mul {
|
||||
($S:ident) => {
|
||||
impl_operator!(Mul<Quaternion<$S>> 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<Quaternion<$S>> 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<S: BaseFloat> approx::AbsDiffEq for Quaternion<S> {
|
||||
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<S: BaseFloat> approx::RelativeEq for Quaternion<S> {
|
||||
#[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<S: BaseFloat> approx::UlpsEq for Quaternion<S> {
|
||||
#[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<S: BaseFloat> From<Quaternion<S>> for Matrix3<S> {
|
||||
/// Convert the quaternion to a 3 x 3 rotation matrix.
|
||||
fn from(quat: Quaternion<S>) -> Matrix3<S> {
|
||||
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<S: BaseFloat> From<Quaternion<S>> for Matrix4<S> {
|
||||
/// Convert the quaternion to a 4 x 4 rotation matrix.
|
||||
fn from(quat: Quaternion<S>) -> Matrix4<S> {
|
||||
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<S: BaseFloat> From<Quaternion<S>> for Basis3<S> {
|
||||
#[inline]
|
||||
fn from(quat: Quaternion<S>) -> Basis3<S> {
|
||||
Basis3::from_quaternion(&quat)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Rotation<Point3<S>> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn look_at(dir: Vector3<S>, up: Vector3<S>) -> Quaternion<S> {
|
||||
Matrix3::look_at(dir, up).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn between_vectors(a: Vector3<S>, b: Vector3<S>) -> Quaternion<S> {
|
||||
// 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::<S>::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()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rotate_vector(&self, vec: Vector3<S>) -> Vector3<S> {
|
||||
self * vec
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn invert(&self) -> Quaternion<S> {
|
||||
self.conjugate() / self.magnitude2()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Rotation3<S> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn from_axis_angle<A: Into<Rad<S>>>(axis: Vector3<S>, angle: A) -> Quaternion<S> {
|
||||
let (s, c) = Rad::sin_cos(angle.into() * cast(0.5f64).unwrap());
|
||||
Quaternion::from_sv(c, axis * s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Into<[S; 4]> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn into(self) -> [S; 4] {
|
||||
match self.into() {
|
||||
(w, xi, yj, zk) => [w, xi, yj, zk],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> AsRef<[S; 4]> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[S; 4] {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> AsMut<[S; 4]> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [S; 4] {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<[S; 4]> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn from(v: [S; 4]) -> Quaternion<S> {
|
||||
Quaternion::new(v[0], v[1], v[2], v[3])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: BaseFloat> From<&'a [S; 4]> for &'a Quaternion<S> {
|
||||
#[inline]
|
||||
fn from(v: &'a [S; 4]) -> &'a Quaternion<S> {
|
||||
unsafe { mem::transmute(v) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: BaseFloat> From<&'a mut [S; 4]> for &'a mut Quaternion<S> {
|
||||
#[inline]
|
||||
fn from(v: &'a mut [S; 4]) -> &'a mut Quaternion<S> {
|
||||
unsafe { mem::transmute(v) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Into<(S, S, S, S)> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn into(self) -> (S, S, S, S) {
|
||||
match self {
|
||||
Quaternion {
|
||||
s,
|
||||
v: Vector3 { x, y, z },
|
||||
} => (s, x, y, z),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> AsRef<(S, S, S, S)> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &(S, S, S, S) {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> AsMut<(S, S, S, S)> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut (S, S, S, S) {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<(S, S, S, S)> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn from(v: (S, S, S, S)) -> Quaternion<S> {
|
||||
match v {
|
||||
(w, xi, yj, zk) => Quaternion::new(w, xi, yj, zk),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: BaseFloat> From<&'a (S, S, S, S)> for &'a Quaternion<S> {
|
||||
#[inline]
|
||||
fn from(v: &'a (S, S, S, S)) -> &'a Quaternion<S> {
|
||||
unsafe { mem::transmute(v) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: BaseFloat> From<&'a mut (S, S, S, S)> for &'a mut Quaternion<S> {
|
||||
#[inline]
|
||||
fn from(v: &'a mut (S, S, S, S)) -> &'a mut Quaternion<S> {
|
||||
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<usize>);
|
||||
index_operators!(S, [S], RangeTo<usize>);
|
||||
index_operators!(S, [S], RangeFrom<usize>);
|
||||
index_operators!(S, [S], RangeFull);
|
||||
|
||||
impl<S> Distribution<Quaternion<S>> for Standard
|
||||
where Standard: Distribution<S>,
|
||||
Standard: Distribution<Vector3<S>>,
|
||||
S: BaseFloat {
|
||||
#[inline]
|
||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Quaternion<S> {
|
||||
Quaternion::from_sv(rng.gen(), rng.gen())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<S> From<mint::Quaternion<S>> for Quaternion<S> {
|
||||
fn from(q: mint::Quaternion<S>) -> Self {
|
||||
Quaternion {
|
||||
s: q.s,
|
||||
v: q.v.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<S: Clone> Into<mint::Quaternion<S>> for Quaternion<S> {
|
||||
fn into(self) -> mint::Quaternion<S> {
|
||||
mint::Quaternion {
|
||||
s: self.s,
|
||||
v: self.v.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use quaternion::*;
|
||||
use vector::*;
|
||||
|
||||
const QUATERNION: Quaternion<f32> = Quaternion {
|
||||
s: 1.0,
|
||||
v: Vector3 {
|
||||
x: 2.0,
|
||||
y: 3.0,
|
||||
z: 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,451 @@
|
|||
// 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<P: EuclideanSpace>: Sized + Copy + One
|
||||
where
|
||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||
Self: approx::AbsDiffEq<Epsilon = P::Scalar>,
|
||||
Self: approx::RelativeEq<Epsilon = P::Scalar>,
|
||||
Self: approx::UlpsEq<Epsilon = P::Scalar>,
|
||||
P::Scalar: BaseFloat,
|
||||
Self: iter::Product<Self>,
|
||||
{
|
||||
/// Create a rotation to a given direction with an 'up' vector.
|
||||
fn look_at(dir: P::Diff, up: P::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: P::Diff, b: P::Diff) -> Self;
|
||||
|
||||
/// Rotate a vector using this rotation.
|
||||
fn rotate_vector(&self, vec: P::Diff) -> P::Diff;
|
||||
|
||||
/// Rotate a point using this rotation, by converting it to its
|
||||
/// representation as a vector.
|
||||
#[inline]
|
||||
fn rotate_point(&self, point: P) -> P {
|
||||
P::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<S: BaseFloat>
|
||||
: Rotation<Point2<S>> + Into<Matrix2<S>> + Into<Basis2<S>> {
|
||||
/// 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<A: Into<Rad<S>>>(theta: A) -> Self;
|
||||
}
|
||||
|
||||
/// A three-dimensional rotation.
|
||||
pub trait Rotation3<S: BaseFloat>
|
||||
: Rotation<Point3<S>> + Into<Matrix3<S>> + Into<Basis3<S>> + Into<Quaternion<S>> + From<Euler<Rad<S>>>
|
||||
{
|
||||
/// 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<A: Into<Rad<S>>>(axis: Vector3<S>, angle: A) -> Self;
|
||||
|
||||
/// Create a rotation from an angle around the `x` axis (pitch).
|
||||
#[inline]
|
||||
fn from_angle_x<A: Into<Rad<S>>>(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<A: Into<Rad<S>>>(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<A: Into<Rad<S>>>(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<f64> = Vector2::unit_x();
|
||||
/// let rot: Basis2<f64> = 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<f64> = 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<S> {
|
||||
mat: Matrix2<S>,
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> AsRef<Matrix2<S>> for Basis2<S> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Matrix2<S> {
|
||||
&self.mat
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<Basis2<S>> for Matrix2<S> {
|
||||
#[inline]
|
||||
fn from(b: Basis2<S>) -> Matrix2<S> {
|
||||
b.mat
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> iter::Product<Basis2<S>> for Basis2<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item = Basis2<S>>>(iter: I) -> Basis2<S> {
|
||||
iter.fold(Basis2::one(), Mul::mul)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis2<S>> for Basis2<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item = &'a Basis2<S>>>(iter: I) -> Basis2<S> {
|
||||
iter.fold(Basis2::one(), Mul::mul)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Rotation<Point2<S>> for Basis2<S> {
|
||||
#[inline]
|
||||
fn look_at(dir: Vector2<S>, up: Vector2<S>) -> Basis2<S> {
|
||||
Basis2 {
|
||||
mat: Matrix2::look_at(dir, up),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn between_vectors(a: Vector2<S>, b: Vector2<S>) -> Basis2<S> {
|
||||
Rotation2::from_angle(Rad::acos(a.dot(b)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rotate_vector(&self, vec: Vector2<S>) -> Vector2<S> {
|
||||
self.mat * vec
|
||||
}
|
||||
|
||||
// TODO: we know the matrix is orthogonal, so this could be re-written
|
||||
// to be faster
|
||||
#[inline]
|
||||
fn invert(&self) -> Basis2<S> {
|
||||
Basis2 {
|
||||
mat: self.mat.invert().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> One for Basis2<S> {
|
||||
#[inline]
|
||||
fn one() -> Basis2<S> {
|
||||
Basis2 {
|
||||
mat: Matrix2::one(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_operator!(<S: BaseFloat> Mul<Basis2<S> > for Basis2<S> {
|
||||
fn mul(lhs, rhs) -> Basis2<S> { Basis2 { mat: lhs.mat * rhs.mat } }
|
||||
});
|
||||
|
||||
impl<S: BaseFloat> approx::AbsDiffEq for Basis2<S> {
|
||||
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<S: BaseFloat> approx::RelativeEq for Basis2<S> {
|
||||
#[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<S: BaseFloat> approx::UlpsEq for Basis2<S> {
|
||||
#[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<S: BaseFloat> Rotation2<S> for Basis2<S> {
|
||||
fn from_angle<A: Into<Rad<S>>>(theta: A) -> Basis2<S> {
|
||||
Basis2 {
|
||||
mat: Matrix2::from_angle(theta),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: fmt::Debug> fmt::Debug for Basis2<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(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<S> {
|
||||
mat: Matrix3<S>,
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Basis3<S> {
|
||||
/// Create a new rotation matrix from a quaternion.
|
||||
#[inline]
|
||||
pub fn from_quaternion(quaternion: &Quaternion<S>) -> Basis3<S> {
|
||||
Basis3 {
|
||||
mat: quaternion.clone().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> AsRef<Matrix3<S>> for Basis3<S> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Matrix3<S> {
|
||||
&self.mat
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<Basis3<S>> for Matrix3<S> {
|
||||
#[inline]
|
||||
fn from(b: Basis3<S>) -> Matrix3<S> {
|
||||
b.mat
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<Basis3<S>> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn from(b: Basis3<S>) -> Quaternion<S> {
|
||||
b.mat.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> iter::Product<Basis3<S>> for Basis3<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item = Basis3<S>>>(iter: I) -> Basis3<S> {
|
||||
iter.fold(Basis3::one(), Mul::mul)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3<S>> for Basis3<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item = &'a Basis3<S>>>(iter: I) -> Basis3<S> {
|
||||
iter.fold(Basis3::one(), Mul::mul)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Rotation<Point3<S>> for Basis3<S> {
|
||||
#[inline]
|
||||
fn look_at(dir: Vector3<S>, up: Vector3<S>) -> Basis3<S> {
|
||||
Basis3 {
|
||||
mat: Matrix3::look_at(dir, up),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn between_vectors(a: Vector3<S>, b: Vector3<S>) -> Basis3<S> {
|
||||
let q: Quaternion<S> = Rotation::between_vectors(a, b);
|
||||
q.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rotate_vector(&self, vec: Vector3<S>) -> Vector3<S> {
|
||||
self.mat * vec
|
||||
}
|
||||
|
||||
// TODO: we know the matrix is orthogonal, so this could be re-written
|
||||
// to be faster
|
||||
#[inline]
|
||||
fn invert(&self) -> Basis3<S> {
|
||||
Basis3 {
|
||||
mat: self.mat.invert().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> One for Basis3<S> {
|
||||
#[inline]
|
||||
fn one() -> Basis3<S> {
|
||||
Basis3 {
|
||||
mat: Matrix3::one(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_operator!(<S: BaseFloat> Mul<Basis3<S> > for Basis3<S> {
|
||||
fn mul(lhs, rhs) -> Basis3<S> { Basis3 { mat: lhs.mat * rhs.mat } }
|
||||
});
|
||||
|
||||
impl<S: BaseFloat> approx::AbsDiffEq for Basis3<S> {
|
||||
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<S: BaseFloat> approx::RelativeEq for Basis3<S> {
|
||||
#[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<S: BaseFloat> approx::UlpsEq for Basis3<S> {
|
||||
#[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<S: BaseFloat> Rotation3<S> for Basis3<S> {
|
||||
fn from_axis_angle<A: Into<Rad<S>>>(axis: Vector3<S>, angle: A) -> Basis3<S> {
|
||||
Basis3 {
|
||||
mat: Matrix3::from_axis_angle(axis, angle),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_angle_x<A: Into<Rad<S>>>(theta: A) -> Basis3<S> {
|
||||
Basis3 {
|
||||
mat: Matrix3::from_angle_x(theta),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_angle_y<A: Into<Rad<S>>>(theta: A) -> Basis3<S> {
|
||||
Basis3 {
|
||||
mat: Matrix3::from_angle_y(theta),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_angle_z<A: Into<Rad<S>>>(theta: A) -> Basis3<S> {
|
||||
Basis3 {
|
||||
mat: Matrix3::from_angle_z(theta),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Angle> From<Euler<A>> for Basis3<A::Unitless>
|
||||
where
|
||||
A: Into<Rad<<A as Angle>::Unitless>>,
|
||||
{
|
||||
/// Create a three-dimensional rotation matrix from a set of euler angles.
|
||||
fn from(src: Euler<A>) -> Basis3<A::Unitless> {
|
||||
Basis3 {
|
||||
mat: Matrix3::from(src),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: fmt::Debug> fmt::Debug for Basis3<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(write!(f, "Basis3 "));
|
||||
<[[S; 3]; 3] as fmt::Debug>::fmt(self.mat.as_ref(), f)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,784 @@
|
|||
// 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<usize, Output = <Self as Array>::Element>,
|
||||
Self: IndexMut<usize, Output = <Self as Array>::Element>,
|
||||
{
|
||||
type Element: Copy;
|
||||
|
||||
/// Get the number of elements in the array type
|
||||
///
|
||||
/// ```rust
|
||||
/// use cgmath::prelude::*;
|
||||
/// use cgmath::Vector3;
|
||||
///
|
||||
/// assert_eq!(Vector3::<f32>::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<Output = <Self as Array>::Element>;
|
||||
|
||||
/// The product of the elements of the array.
|
||||
fn product(self) -> Self::Element
|
||||
where
|
||||
Self::Element: Mul<Output = <Self as Array>::Element>;
|
||||
|
||||
/// Whether all elements of the array are finite
|
||||
fn is_finite(&self) -> bool
|
||||
where
|
||||
Self::Element: BaseFloat;
|
||||
}
|
||||
|
||||
/// Element-wise arithmetic operations. These are supplied for pragmatic
|
||||
/// reasons, but will usually fall outside of traditional algebraic properties.
|
||||
pub trait ElementWise<Rhs = Self> {
|
||||
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<Output = Self>`
|
||||
/// - `Sub<Output = Self>`
|
||||
/// - `Neg<Output = Self>`
|
||||
///
|
||||
/// ```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<Self::Scalar, Output = Self>`
|
||||
/// - `Div<Self::Scalar, Output = Self>`
|
||||
/// - `Rem<Self::Scalar, Output = Self>`
|
||||
///
|
||||
/// ```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, Output = Self>,
|
||||
Self: Sub<Self, Output = Self>,
|
||||
Self: iter::Sum<Self>,
|
||||
|
||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||
Self: Mul<<Self as VectorSpace>::Scalar, Output = Self>,
|
||||
Self: Div<<Self as VectorSpace>::Scalar, Output = Self>,
|
||||
Self: Rem<<Self as VectorSpace>::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: BaseFloat;
|
||||
|
||||
/// 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 {
|
||||
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 as VectorSpace>::Scalar: BaseFloat,
|
||||
Self: MetricSpace<Metric = <Self as VectorSpace>::Scalar>,
|
||||
// Self: approx::AbsDiffEq<Epsilon = <Self as VectorSpace>::Scalar>,
|
||||
// Self: approx::RelativeEq<Epsilon = <Self as VectorSpace>::Scalar>,
|
||||
Self: approx::UlpsEq<Epsilon = <Self as VectorSpace>::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 {
|
||||
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)
|
||||
}
|
||||
|
||||
/// The distance from the tail to the tip of the vector.
|
||||
#[inline]
|
||||
fn magnitude(self) -> Self::Scalar {
|
||||
Float::sqrt(self.magnitude2())
|
||||
}
|
||||
|
||||
/// Returns the angle between two vectors in radians.
|
||||
fn angle(self, other: Self) -> Rad<Self::Scalar> {
|
||||
Rad::acos(Self::dot(self, other) / (self.magnitude() * other.magnitude()))
|
||||
}
|
||||
|
||||
/// Returns a vector with the same direction, but with a magnitude of `1`.
|
||||
#[inline]
|
||||
fn normalize(self) -> Self {
|
||||
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 {
|
||||
self * (magnitude / self.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())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<f32> = 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<f32> = p1 - p0;
|
||||
/// let half_dv: Vector3<f32> = dv * 0.5;
|
||||
/// let midpoint: Point3<f32> = 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<Element = <Self as EuclideanSpace>::Scalar>,
|
||||
|
||||
Self: Add<<Self as EuclideanSpace>::Diff, Output = Self>,
|
||||
Self: Sub<<Self as EuclideanSpace>::Diff, Output = Self>,
|
||||
Self: Sub<Self, Output = <Self as EuclideanSpace>::Diff>,
|
||||
|
||||
Self: Mul<<Self as EuclideanSpace>::Scalar, Output = Self>,
|
||||
Self: Div<<Self as EuclideanSpace>::Scalar, Output = Self>,
|
||||
Self: Rem<<Self as EuclideanSpace>::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<Scalar = Self::Scalar>;
|
||||
|
||||
/// 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<Output = Self>`
|
||||
/// - `Sub<Output = Self>`
|
||||
/// - `Neg<Output = Self>`
|
||||
///
|
||||
/// Scalar multiplication:
|
||||
///
|
||||
/// - `Mul<Self::Scalar, Output = Self>`
|
||||
/// - `Div<Self::Scalar, Output = Self>`
|
||||
/// - `Rem<Self::Scalar, Output = Self>`
|
||||
///
|
||||
/// 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: BaseFloat,
|
||||
|
||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||
Self: Index<usize, Output = <Self as Matrix>::Column>,
|
||||
Self: IndexMut<usize, Output = <Self as Matrix>::Column>,
|
||||
Self: approx::AbsDiffEq<Epsilon = <Self as VectorSpace>::Scalar>,
|
||||
Self: approx::RelativeEq<Epsilon = <Self as VectorSpace>::Scalar>,
|
||||
Self: approx::UlpsEq<Epsilon = <Self as VectorSpace>::Scalar>,
|
||||
{
|
||||
/// The row vector of the matrix.
|
||||
type Row: VectorSpace<Scalar = Self::Scalar> + Array<Element = Self::Scalar>;
|
||||
|
||||
/// The column vector of the matrix.
|
||||
type Column: VectorSpace<Scalar = Self::Scalar> + Array<Element = Self::Scalar>;
|
||||
|
||||
/// The result of transposing the matrix
|
||||
type Transpose: Matrix<Scalar = Self::Scalar, Row = Self::Column, Column = Self::Row>;
|
||||
|
||||
/// 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: BaseFloat,
|
||||
|
||||
Self: One,
|
||||
Self: iter::Product<Self>,
|
||||
|
||||
Self: Matrix<
|
||||
// FIXME: Can be cleaned up once equality constraints in where clauses are implemented
|
||||
Column = <Self as SquareMatrix>::ColumnRow,
|
||||
Row = <Self as SquareMatrix>::ColumnRow,
|
||||
Transpose = Self,
|
||||
>,
|
||||
Self: Mul<<Self as SquareMatrix>::ColumnRow, Output = <Self as SquareMatrix>::ColumnRow>,
|
||||
Self: Mul<Self, Output = Self>,
|
||||
{
|
||||
// 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<Scalar = Self::Scalar> + Array<Element = Self::Scalar>;
|
||||
|
||||
/// 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<Self>;
|
||||
|
||||
/// Test if this matrix is invertible.
|
||||
#[inline]
|
||||
fn is_invertible(&self) -> bool {
|
||||
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 {
|
||||
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<Epsilon = <Self as Angle>::Unitless>,
|
||||
Self: approx::RelativeEq<Epsilon = <Self as Angle>::Unitless>,
|
||||
Self: approx::UlpsEq<Epsilon = <Self as Angle>::Unitless>,
|
||||
|
||||
Self: Zero,
|
||||
|
||||
Self: Neg<Output = Self>,
|
||||
Self: Add<Self, Output = Self>,
|
||||
Self: Sub<Self, Output = Self>,
|
||||
Self: Rem<Self, Output = Self>,
|
||||
Self: Mul<<Self as Angle>::Unitless, Output = Self>,
|
||||
Self: Div<Self, Output = <Self as Angle>::Unitless>,
|
||||
Self: Div<<Self as Angle>::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<f32> = 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<f32> = 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<f32> = Rad::atan(0.5);
|
||||
/// ```
|
||||
fn atan(ratio: Self::Unitless) -> Self;
|
||||
|
||||
fn atan2(a: Self::Unitless, b: Self::Unitless) -> Self;
|
||||
}
|
|
@ -0,0 +1,371 @@
|
|||
// 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};
|
||||
|
||||
/// 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<P: EuclideanSpace>: Sized {
|
||||
/// Create an identity transformation. That is, a transformation which
|
||||
/// does nothing.
|
||||
fn one() -> Self;
|
||||
|
||||
/// Create a transformation that rotates a vector to look at `center` from
|
||||
/// `eye`, using `up` for orientation.
|
||||
fn look_at(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<P::Diff> {
|
||||
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<Self>;
|
||||
|
||||
/// 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<V: VectorSpace, R> {
|
||||
pub scale: V::Scalar,
|
||||
pub rot: R,
|
||||
pub disp: V,
|
||||
}
|
||||
|
||||
impl<P: EuclideanSpace, R: Rotation<P>> Transform<P> for Decomposed<P::Diff, R>
|
||||
where
|
||||
P::Scalar: BaseFloat,
|
||||
// FIXME: Investigate why this is needed!
|
||||
P::Diff: VectorSpace,
|
||||
{
|
||||
#[inline]
|
||||
fn one() -> Decomposed<P::Diff, R> {
|
||||
Decomposed {
|
||||
scale: P::Scalar::one(),
|
||||
rot: R::one(),
|
||||
disp: P::Diff::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn look_at(eye: P, center: P, up: P::Diff) -> Decomposed<P::Diff, R> {
|
||||
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 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<P::Diff> {
|
||||
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<P::Diff, R>) -> Decomposed<P::Diff, R> {
|
||||
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<Decomposed<P::Diff, R>> {
|
||||
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<S: BaseNum>: Transform<Point2<S>> + Into<Matrix3<S>> {}
|
||||
pub trait Transform3<S: BaseNum>: Transform<Point3<S>> + Into<Matrix4<S>> {}
|
||||
|
||||
impl<S: BaseFloat, R: Rotation2<S>> From<Decomposed<Vector2<S>, R>> for Matrix3<S> {
|
||||
fn from(dec: Decomposed<Vector2<S>, R>) -> Matrix3<S> {
|
||||
let m: Matrix2<_> = dec.rot.into();
|
||||
let mut m: Matrix3<_> = (&m * dec.scale).into();
|
||||
m.z = dec.disp.extend(S::one());
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat, R: Rotation3<S>> From<Decomposed<Vector3<S>, R>> for Matrix4<S> {
|
||||
fn from(dec: Decomposed<Vector3<S>, R>) -> Matrix4<S> {
|
||||
let m: Matrix3<_> = dec.rot.into();
|
||||
let mut m: Matrix4<_> = (&m * dec.scale).into();
|
||||
m.w = dec.disp.extend(S::one());
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat, R: Rotation2<S>> Transform2<S> for Decomposed<Vector2<S>, R> {}
|
||||
|
||||
impl<S: BaseFloat, R: Rotation3<S>> Transform3<S> for Decomposed<Vector3<S>, R> {}
|
||||
|
||||
impl<S: VectorSpace, R, E: BaseFloat> approx::AbsDiffEq for Decomposed<S, R>
|
||||
where
|
||||
S: approx::AbsDiffEq<Epsilon = E>,
|
||||
S::Scalar: approx::AbsDiffEq<Epsilon = E>,
|
||||
R: approx::AbsDiffEq<Epsilon = E>,
|
||||
{
|
||||
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<S: VectorSpace, R, E: BaseFloat> approx::RelativeEq for Decomposed<S, R>
|
||||
where
|
||||
S: approx::RelativeEq<Epsilon = E>,
|
||||
S::Scalar: approx::RelativeEq<Epsilon = E>,
|
||||
R: approx::RelativeEq<Epsilon = E>,
|
||||
{
|
||||
#[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<S: VectorSpace, R, E: BaseFloat> approx::UlpsEq for Decomposed<S, R>
|
||||
where
|
||||
S: approx::UlpsEq<Epsilon = E>,
|
||||
S::Scalar: approx::UlpsEq<Epsilon = E>,
|
||||
R: approx::UlpsEq<Epsilon = E>,
|
||||
{
|
||||
#[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 structure::VectorSpace;
|
||||
use super::Decomposed;
|
||||
use serde::{self, Serialize};
|
||||
use serde::ser::SerializeStruct;
|
||||
|
||||
impl<V, R> Serialize for Decomposed<V, R>
|
||||
where
|
||||
V: Serialize + VectorSpace,
|
||||
V::Scalar: Serialize,
|
||||
R: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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 structure::VectorSpace;
|
||||
use super::Decomposed;
|
||||
use serde::{self, Deserialize};
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
|
||||
enum DecomposedField {
|
||||
Scale,
|
||||
Rot,
|
||||
Disp,
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for DecomposedField {
|
||||
fn deserialize<D>(deserializer: D) -> Result<DecomposedField, D::Error>
|
||||
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<E>(self, value: &str) -> Result<DecomposedField, E>
|
||||
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<S, R>
|
||||
where
|
||||
S: Deserialize<'a>,
|
||||
S::Scalar: Deserialize<'a>,
|
||||
R: Deserialize<'a>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Decomposed<S, R>, 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<S: VectorSpace, R>(PhantomData<(S, R)>);
|
||||
|
||||
impl<'a, S: VectorSpace, R> serde::de::Visitor<'a> for DecomposedVisitor<S, R>
|
||||
where
|
||||
S: Deserialize<'a>,
|
||||
S::Scalar: Deserialize<'a>,
|
||||
R: Deserialize<'a>,
|
||||
{
|
||||
type Value = Decomposed<S, R>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("`scale`, `rot` and `disp` fields")
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Decomposed<S, R>, 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,101 @@
|
|||
// 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<f64> = Rad::full_turn().normalize();
|
||||
assert_ulps_eq!(&angle, &Rad(0f64));
|
||||
|
||||
let angle: Rad<f64> = (Rad::full_turn() + Rad::turn_div_4()).normalize();
|
||||
assert_ulps_eq!(&angle, &Rad::turn_div_4());
|
||||
|
||||
let angle: Rad<f64> = (-Rad::turn_div_4()).normalize();
|
||||
assert_ulps_eq!(&angle, &(Rad::full_turn() - Rad::turn_div_4()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_signed() {
|
||||
let angle: Rad<f64> = Rad::full_turn().normalize_signed();
|
||||
assert_ulps_eq!(&angle, &Rad(0f64));
|
||||
|
||||
let angle: Rad<f64> = (Rad::full_turn() + Rad::turn_div_4()).normalize_signed();
|
||||
assert_ulps_eq!(&angle, &Rad::turn_div_4());
|
||||
|
||||
let angle: Rad<f64> = (Rad::full_turn() - Rad::turn_div_4()).normalize_signed();
|
||||
assert_ulps_eq!(&angle, &-Rad::turn_div_4());
|
||||
|
||||
let angle: Rad<f64> = Rad::turn_div_2().normalize_signed();
|
||||
assert_ulps_eq!(&angle, &Rad::turn_div_2());
|
||||
|
||||
let angle: Rad<f64> = (-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()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,784 @@
|
|||
// 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;
|
||||
|
||||
pub mod matrix2 {
|
||||
use std::f64;
|
||||
|
||||
use cgmath::*;
|
||||
|
||||
const A: Matrix2<f64> = Matrix2 { x: Vector2 { x: 1.0f64, y: 3.0f64 },
|
||||
y: Vector2 { x: 2.0f64, y: 4.0f64 } };
|
||||
const B: Matrix2<f64> = Matrix2 { x: Vector2 { x: 2.0f64, y: 4.0f64 },
|
||||
y: Vector2 { x: 3.0f64, y: 5.0f64 } };
|
||||
const C: Matrix2<f64> = Matrix2 { x: Vector2 { x: 2.0f64, y: 1.0f64 },
|
||||
y: Vector2 { x: 1.0f64, y: 2.0f64 } };
|
||||
|
||||
const V: Vector2<f64> = 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::<f64>::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::<f64>::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::<f64>::identity().is_identity());
|
||||
assert!(Matrix2::<f64>::identity().is_symmetric());
|
||||
assert!(Matrix2::<f64>::identity().is_diagonal());
|
||||
assert!(Matrix2::<f64>::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<f64> = Matrix2::from_angle(Rad(f64::consts::PI));
|
||||
assert_ulps_eq!(rot3 * Vector2::new(1.0, 1.0), &Vector2::new(-1.0, -1.0));
|
||||
}
|
||||
}
|
||||
|
||||
pub mod matrix3 {
|
||||
use cgmath::*;
|
||||
|
||||
const A: Matrix3<f64> = 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<f64> = 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<f64> = 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<f64> = 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<f64> = 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::<f64>::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::<f64>::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::<f64>::identity().is_identity());
|
||||
assert!(Matrix3::<f64>::identity().is_symmetric());
|
||||
assert!(Matrix3::<f64>::identity().is_diagonal());
|
||||
assert!(Matrix3::<f64>::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());
|
||||
}
|
||||
|
||||
mod from_axis_x {
|
||||
use cgmath::*;
|
||||
|
||||
fn check_from_axis_angle_x(pitch: Rad<f32>) {
|
||||
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<f32>) {
|
||||
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<f32>) {
|
||||
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<f32>) {
|
||||
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<f32>) {
|
||||
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<f32>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod matrix4 {
|
||||
use cgmath::*;
|
||||
|
||||
const A: Matrix4<f64> = 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<f64> = 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<f64> = 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<f64> = 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<f64> = 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::<f64>::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::<f64>::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::<f64>::identity().is_identity());
|
||||
assert!(Matrix4::<f64>::identity().is_symmetric());
|
||||
assert!(Matrix4::<f64>::identity().is_diagonal());
|
||||
assert!(Matrix4::<f64>::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,
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
// 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::{Point1, Point2, Point3};
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// 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<f32> = Vector4::new(-1., -1., -1., 1.);
|
||||
let vec_orig: Vector4<f32> = Vector4::new(0., 0., 0., 1.);
|
||||
let vec_far: Vector4<f32> = Vector4::new(1., 1., 1., 1.);
|
||||
|
||||
let o: Matrix4<f32> = 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<f32> = 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<f32> = Vector4::new(0., 0., 0., 1.);
|
||||
|
||||
let o: Matrix4<f32> = ortho(-1., 1., -1., 1., -1., 1.);
|
||||
let orig = o * vec_orig;
|
||||
assert_eq!(orig, Vector4::new(0., 0., 0., 1.));
|
||||
|
||||
let o: Matrix4<f32> = ortho(0., 2., 0., 2., 0., 2.);
|
||||
let orig = o * vec_orig;
|
||||
assert_eq!(orig, Vector4::new(-1., -1., -1., 1.));
|
||||
|
||||
let o: Matrix4<f32> = ortho(-2., 0., -2., 0., -2., 0.);
|
||||
let orig = o * vec_orig;
|
||||
assert_eq!(orig, Vector4::new(1., 1., 1., 1.));
|
||||
}
|
|
@ -0,0 +1,472 @@
|
|||
// 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;
|
||||
|
||||
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<Rad<f32>>) {
|
||||
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<f32>, y: Rad<f32>, z: Rad<f32>) {
|
||||
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<f32>, dst: Vector3<f32>) {
|
||||
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<f32> = 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)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// 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<f64>>() -> R {
|
||||
Rotation2::from_angle(Deg(30.0))
|
||||
}
|
||||
|
||||
pub fn a3<R: Rotation3<f64>>() -> 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());
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// 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));
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate approx;
|
||||
extern crate cgmath;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
extern crate serde_json;
|
||||
|
||||
use cgmath::*;
|
||||
|
||||
#[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]
|
||||
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<Vector3<f64>, Quaternion<f64>> = Transform::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);
|
||||
}
|
||||
|
||||
#[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<Vector3<f64>, Quaternion<f64>> =
|
||||
serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_ulps_eq!(&t, &deserialized);
|
||||
}
|
|
@ -0,0 +1,370 @@
|
|||
// 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::*;
|
||||
use std::f64;
|
||||
use std::iter;
|
||||
|
||||
#[test]
|
||||
fn test_constructor() {
|
||||
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)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
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)
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue