third_party/cargo: add cgmath

ecs
q3k 2020-01-22 00:34:02 +01:00
parent b6071b0a51
commit 1fc9d9ff1a
42 changed files with 11470 additions and 2 deletions

View File

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

12
third_party/cargo/Cargo.lock generated vendored
View File

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

View File

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

View File

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

View File

@ -39,6 +39,8 @@ rust_library(
],
version = "0.3.2",
crate_features = [
"default",
"std",
],
)

View File

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

View File

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

View File

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

View File

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

202
third_party/cargo/vendor/cgmath-0.17.0/LICENSE vendored Executable file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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