├── .gitignore ├── src ├── prelude.rs ├── num.rs ├── conv.rs ├── lib.rs ├── quaternion_simd.rs ├── euler.rs ├── angle.rs ├── vector_simd.rs ├── projection.rs ├── macros.rs ├── transform.rs ├── rotation.rs └── point.rs ├── Cargo.toml ├── tests ├── rotation.rs ├── swizzle.rs ├── projection.rs ├── point.rs ├── angle.rs ├── transform.rs ├── vector4f32.rs ├── vector.rs └── quaternion.rs ├── benches ├── quat.rs ├── construction.rs ├── common │ └── macros.rs ├── vec.rs └── mat.rs ├── .github └── workflows │ └── tests.yml ├── README.md ├── LICENSE └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vs/ 3 | /target 4 | /Cargo.lock 5 | /bench/bench 6 | .vscode 7 | .idea 8 | *.iml 9 | 10 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the most common traits used in `cgmath`. By 2 | //! glob-importing this module, you can avoid the need to import each trait 3 | //! individually, while still being selective about what types you import. 4 | 5 | pub use structure::*; 6 | 7 | pub use rotation::Rotation; 8 | pub use rotation::Rotation2; 9 | pub use rotation::Rotation3; 10 | 11 | pub use transform::Transform; 12 | pub use transform::Transform2; 13 | pub use transform::Transform3; 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "cgmath" 4 | version = "0.18.0" 5 | authors = ["Rust game-developers"] 6 | license = "Apache-2.0" 7 | description = "A linear algebra and mathematics library for computer graphics." 8 | 9 | documentation = "https://docs.rs/cgmath" 10 | homepage = "https://github.com/rustgd/cgmath" 11 | repository = "https://github.com/rustgd/cgmath" 12 | readme = "README.md" 13 | 14 | keywords = ["gamedev", "math", "matrix", "vector", "quaternion"] 15 | 16 | [lib] 17 | name = "cgmath" 18 | 19 | [features] 20 | unstable = [] 21 | swizzle = [] 22 | 23 | [dependencies] 24 | approx = "0.5" 25 | mint = { version = "0.5.8", optional = true } 26 | num-traits = "0.2" 27 | # small_rng used only for benchmarks 28 | rand = { version = "0.8", features = ["small_rng"], optional = true } 29 | serde = { version = "1.0", features = ["serde_derive"], optional = true } 30 | # works only in rust toolchain up to 1.32, disabled indefinitely 31 | #simd = { version = "0.2", optional = true } 32 | bytemuck = { version = "1.0", optional = true } 33 | 34 | [dev-dependencies] 35 | serde_json = "1.0" 36 | -------------------------------------------------------------------------------- /tests/rotation.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | extern crate cgmath; 17 | 18 | use cgmath::*; 19 | 20 | mod rotation { 21 | use super::cgmath::*; 22 | 23 | pub fn a2>() -> R { 24 | Rotation2::from_angle(Deg(30.0)) 25 | } 26 | 27 | pub fn a3>() -> R { 28 | let axis = Vector3::new(1.0, 1.0, 0.0).normalize(); 29 | Rotation3::from_axis_angle(axis, Deg(30.0)) 30 | } 31 | } 32 | 33 | #[test] 34 | fn test_invert_basis2() { 35 | let a: Basis2<_> = rotation::a2(); 36 | let a = a * a.invert(); 37 | let a: &Matrix2<_> = a.as_ref(); 38 | assert!(a.is_identity()); 39 | } 40 | 41 | #[test] 42 | fn test_invert_basis3() { 43 | let a: Basis3<_> = rotation::a3(); 44 | let a = a * a.invert(); 45 | let a: &Matrix3<_> = a.as_ref(); 46 | assert!(a.is_identity()); 47 | } 48 | -------------------------------------------------------------------------------- /benches/quat.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #![feature(test)] 17 | #![allow(unused_macros)] 18 | 19 | extern crate cgmath; 20 | extern crate rand; 21 | extern crate test; 22 | 23 | use rand::{rngs::SmallRng, Rng, SeedableRng}; 24 | use std::ops::*; 25 | use test::Bencher; 26 | 27 | use cgmath::*; 28 | 29 | #[path = "common/macros.rs"] 30 | #[macro_use] 31 | mod macros; 32 | 33 | bench_binop!(_bench_quat_add_q, Quaternion, Quaternion, add); 34 | bench_binop!(_bench_quat_sub_q, Quaternion, Quaternion, sub); 35 | bench_binop!(_bench_quat_mul_q, Quaternion, Quaternion, mul); 36 | bench_binop!(_bench_quat_mul_v, Quaternion, Vector3, mul); 37 | bench_binop!(_bench_quat_mul_s, Quaternion, f32, mul); 38 | bench_binop!(_bench_quat_div_s, Quaternion, f32, div); 39 | bench_unop!(_bench_quat_invert, Quaternion, invert); 40 | bench_unop!(_bench_quat_conjugate, Quaternion, conjugate); 41 | bench_unop!(_bench_quat_normalize, Quaternion, normalize); 42 | -------------------------------------------------------------------------------- /src/num.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use approx; 17 | 18 | use std::fmt; 19 | use std::ops::*; 20 | 21 | use num_traits::{Float, Num}; 22 | 23 | /// Base numeric types with partial ordering 24 | pub trait BaseNum: 25 | Copy 26 | + Clone 27 | + fmt::Debug 28 | + Num 29 | + PartialOrd 30 | + AddAssign 31 | + SubAssign 32 | + MulAssign 33 | + DivAssign 34 | + RemAssign 35 | { 36 | } 37 | 38 | impl BaseNum for T where 39 | T: Copy 40 | + Clone 41 | + fmt::Debug 42 | + Num 43 | + PartialOrd 44 | + AddAssign 45 | + SubAssign 46 | + MulAssign 47 | + DivAssign 48 | + RemAssign 49 | { 50 | } 51 | 52 | /// Base floating point types 53 | pub trait BaseFloat: 54 | BaseNum 55 | + Float 56 | + approx::AbsDiffEq 57 | + approx::RelativeEq 58 | + approx::UlpsEq 59 | { 60 | } 61 | 62 | impl BaseFloat for T where 63 | T: BaseNum 64 | + Float 65 | + approx::AbsDiffEq 66 | + approx::RelativeEq 67 | + approx::UlpsEq 68 | { 69 | } 70 | -------------------------------------------------------------------------------- /benches/construction.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #![feature(test)] 17 | #![allow(unused_macros)] 18 | 19 | extern crate cgmath; 20 | extern crate rand; 21 | extern crate test; 22 | 23 | use rand::{rngs::SmallRng, Rng, SeedableRng}; 24 | use test::Bencher; 25 | 26 | use cgmath::*; 27 | 28 | #[path = "common/macros.rs"] 29 | #[macro_use] 30 | mod macros; 31 | 32 | fn bench_from_axis_angle>(bh: &mut Bencher) { 33 | const LEN: usize = 1 << 13; 34 | 35 | let mut rng = SmallRng::from_entropy(); 36 | 37 | let axis: Vec<_> = (0..LEN).map(|_| rng.gen::>()).collect(); 38 | let angle: Vec<_> = (0..LEN).map(|_| rng.gen::>()).collect(); 39 | let mut i = 0; 40 | 41 | bh.iter(|| { 42 | i = (i + 1) & (LEN - 1); 43 | 44 | unsafe { 45 | let res: T = 46 | Rotation3::from_axis_angle(*axis.get_unchecked(i), *angle.get_unchecked(i)); 47 | test::black_box(res) 48 | } 49 | }) 50 | } 51 | 52 | #[bench] 53 | fn _bench_quat_from_axisangle(bh: &mut Bencher) { 54 | bench_from_axis_angle::>(bh) 55 | } 56 | 57 | #[bench] 58 | fn _bench_rot3_from_axisangle(bh: &mut Bencher) { 59 | bench_from_axis_angle::>(bh) 60 | } 61 | 62 | bench_construction!( 63 | _bench_rot2_from_axisangle, 64 | Basis2, 65 | Basis2::from_angle[angle: Rad] 66 | ); 67 | 68 | bench_construction!( 69 | _bench_quat_from_euler_angles, 70 | Quaternion, 71 | Quaternion::from[src: Euler>] 72 | ); 73 | bench_construction!( 74 | _bench_rot3_from_euler_angles, 75 | Basis3, 76 | Basis3::from[src: Euler>] 77 | ); 78 | -------------------------------------------------------------------------------- /tests/swizzle.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2017 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #![cfg(feature = "swizzle")] 17 | 18 | extern crate cgmath; 19 | 20 | use cgmath::{Point1, Point2, Point3, Vector1, Vector2, Vector3, Vector4}; 21 | 22 | // Sanity checks 23 | #[test] 24 | fn test_point_swizzle() { 25 | let p1 = Point1::new(1.0); 26 | let p2 = Point2::new(1.0, 2.0); 27 | let p3 = Point3::new(1.0, 2.0, 3.0); 28 | assert_eq!(p1.x(), p1); 29 | assert_eq!(p2.x(), p1); 30 | assert_eq!(p2.y(), Point1::new(2.0)); 31 | assert_eq!(p2.xx(), Point2::new(1.0, 1.0)); 32 | assert_eq!(p2.xy(), p2); 33 | assert_eq!(p2.yx(), Point2::new(2.0, 1.0)); 34 | assert_eq!(p2.yy(), Point2::new(2.0, 2.0)); 35 | assert_eq!(p3.x(), p1); 36 | assert_eq!(p3.y(), Point1::new(2.0)); 37 | assert_eq!(p3.xy(), p2); 38 | assert_eq!(p3.zy(), Point2::new(3.0, 2.0)); 39 | assert_eq!(p3.yyx(), Point3::new(2.0, 2.0, 1.0)); 40 | } 41 | 42 | #[test] 43 | fn test_vector_swizzle() { 44 | let p1 = Vector1::new(1.0); 45 | let p2 = Vector2::new(1.0, 2.0); 46 | let p3 = Vector3::new(1.0, 2.0, 3.0); 47 | let p4 = Vector4::new(1.0, 2.0, 3.0, 4.0); 48 | assert_eq!(p1.x(), p1); 49 | assert_eq!(p2.x(), p1); 50 | assert_eq!(p2.y(), Vector1::new(2.0)); 51 | assert_eq!(p2.xx(), Vector2::new(1.0, 1.0)); 52 | assert_eq!(p2.xy(), p2); 53 | assert_eq!(p2.yx(), Vector2::new(2.0, 1.0)); 54 | assert_eq!(p2.yy(), Vector2::new(2.0, 2.0)); 55 | assert_eq!(p3.x(), p1); 56 | assert_eq!(p3.y(), Vector1::new(2.0)); 57 | assert_eq!(p3.xy(), p2); 58 | assert_eq!(p3.zy(), Vector2::new(3.0, 2.0)); 59 | assert_eq!(p3.yyx(), Vector3::new(2.0, 2.0, 1.0)); 60 | assert_eq!(p4.xyxy(), Vector4::new(1.0, 2.0, 1.0, 2.0)); 61 | } 62 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | on: 3 | schedule: 4 | # build at least once per week 5 | - cron: "0 0 * * 0" 6 | pull_request: 7 | push: 8 | paths-ignore: 9 | - "README.md" 10 | - "CHANGELOG.md" 11 | jobs: 12 | # TODO: enable after running cargo fmt again 13 | # check: 14 | # name: Check formatting 15 | # runs-on: ubuntu-latest 16 | # steps: 17 | # - uses: actions/checkout@v2 18 | # - uses: actions-rs/toolchain@v1 19 | # with: 20 | # toolchain: stable 21 | # override: true 22 | # components: rustfmt 23 | # profile: minimal 24 | # - name: Check formatting 25 | # uses: actions-rs/cargo@v1 26 | # with: 27 | # command: fmt 28 | # args: --all -- --check 29 | test: 30 | name: Test 31 | runs-on: ${{ matrix.os }} 32 | env: 33 | RUST_BACKTRACE: 1 34 | strategy: 35 | matrix: 36 | os: [windows-latest, ubuntu-latest, macos-latest] 37 | toolchain: [stable, nightly] 38 | features: 39 | - "" 40 | - "serde,mint" 41 | steps: 42 | - uses: actions/checkout@v2 43 | - name: Install latest rust 44 | uses: actions-rs/toolchain@v1 45 | id: rust 46 | with: 47 | toolchain: ${{ matrix.toolchain }} 48 | override: true 49 | profile: minimal 50 | - name: Build 51 | uses: actions-rs/cargo@v1 52 | with: 53 | command: build 54 | args: --lib --features "${{ matrix.features }}" 55 | - name: Test 56 | uses: actions-rs/cargo@v1 57 | with: 58 | command: test 59 | args: --features "${{ matrix.features }}" 60 | benchmark: 61 | name: Benchmark 62 | runs-on: ${{ matrix.os }} 63 | env: 64 | RUST_BACKTRACE: 1 65 | strategy: 66 | matrix: 67 | os: [ubuntu-latest] 68 | toolchain: [nightly] 69 | features: 70 | # The benchmark always needs the 'rand' feature 71 | - "rand,serde,mint" 72 | steps: 73 | - uses: actions/checkout@v2 74 | - name: Install latest rust 75 | uses: actions-rs/toolchain@v1 76 | id: rust 77 | with: 78 | toolchain: ${{ matrix.toolchain }} 79 | override: true 80 | profile: minimal 81 | - name: Benchmark 82 | uses: actions-rs/cargo@v1 83 | with: 84 | command: bench 85 | args: --features "${{ matrix.features }}" 86 | 87 | -------------------------------------------------------------------------------- /src/conv.rs: -------------------------------------------------------------------------------- 1 | //! Constrained conversion functions for assisting in situations where type 2 | //! inference is difficult. 3 | //! 4 | //! For example, when declaring `glium` uniforms, we need to convert to fixed 5 | //! length arrays. We can use the `Into` trait directly, but it is rather ugly! 6 | //! 7 | //! --- Doc-test disabled because glium causes problems with nightly-2019-01-01 needed for "simd" 8 | //! ` ` `rust 9 | //! #[macro_use] 10 | //! extern crate glium; 11 | //! extern crate cgmath; 12 | //! 13 | //! use cgmath::{Matrix4, Point2}; 14 | //! use cgmath::prelude::*; 15 | //! 16 | //! # fn main() { 17 | //! let point = Point2::new(1, 2); 18 | //! let matrix = Matrix4::from_scale(2.0); 19 | //! 20 | //! let uniforms = uniform! { 21 | //! point: Into::<[_; 2]>::into(point), 22 | //! matrix: Into::<[[_; 4]; 4]>::into(matrix), 23 | //! // Yuck!! (ノಥ益ಥ)ノ ┻━┻) 24 | //! }; 25 | //! # } 26 | //! ` ` ` 27 | //! 28 | //! Instead, we can use the conversion functions from the `conv` module: 29 | //! 30 | //! --- Doc-test disabled because glium causes problems nightly-2019-01-01 needed for "simd" 31 | //! ` ` `rust 32 | //! #[macro_use] 33 | //! extern crate glium; 34 | //! extern crate cgmath; 35 | //! 36 | //! use cgmath::{Matrix4, Point2}; 37 | //! use cgmath::prelude::*; 38 | //! use cgmath::conv::*; 39 | //! 40 | //! # fn main() { 41 | //! let point = Point2::new(1, 2); 42 | //! let matrix = Matrix4::from_scale(2.0); 43 | //! 44 | //! let uniforms = uniform! { 45 | //! point: array2(point), 46 | //! matrix: array4x4(matrix), 47 | //! // ┬─┬ノ( º _ ºノ) 48 | //! }; 49 | //! # } 50 | //! ` ` ` 51 | 52 | /// Force a conversion into a 2-element array. 53 | #[inline] 54 | pub fn array2>(value: A) -> [T; 2] { 55 | value.into() 56 | } 57 | 58 | /// Force a conversion into a 3-element array. 59 | #[inline] 60 | pub fn array3>(value: A) -> [T; 3] { 61 | value.into() 62 | } 63 | 64 | /// Force a conversion into a 4-element array. 65 | #[inline] 66 | pub fn array4>(value: A) -> [T; 4] { 67 | value.into() 68 | } 69 | 70 | /// Force a conversion into a 2x2-element array. 71 | #[inline] 72 | pub fn array2x2>(value: A) -> [[T; 2]; 2] { 73 | value.into() 74 | } 75 | 76 | /// Force a conversion into a 3x3-element array. 77 | #[inline] 78 | pub fn array3x3>(value: A) -> [[T; 3]; 3] { 79 | value.into() 80 | } 81 | 82 | /// Force a conversion into a 4x4-element array. 83 | #[inline] 84 | pub fn array4x4>(value: A) -> [[T; 4]; 4] { 85 | value.into() 86 | } 87 | -------------------------------------------------------------------------------- /benches/common/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | macro_rules! bench_binop { 17 | ($name: ident, $t1: ty, $t2: ty, $binop: ident) => { 18 | #[bench] 19 | fn $name(bh: &mut Bencher) { 20 | const LEN: usize = 1 << 13; 21 | 22 | let mut rng = SmallRng::from_entropy(); 23 | 24 | let elems1: Vec<$t1> = (0..LEN).map(|_| rng.gen::<$t1>()).collect(); 25 | let elems2: Vec<$t2> = (0..LEN).map(|_| rng.gen::<$t2>()).collect(); 26 | let mut i = 0; 27 | 28 | bh.iter(|| { 29 | i = (i + 1) & (LEN - 1); 30 | 31 | unsafe { test::black_box(elems1.get_unchecked(i).$binop(*elems2.get_unchecked(i))) } 32 | }) 33 | } 34 | }; 35 | } 36 | 37 | macro_rules! bench_unop { 38 | ($name: ident, $t: ty, $unop: ident) => { 39 | #[bench] 40 | fn $name(bh: &mut Bencher) { 41 | const LEN: usize = 1 << 13; 42 | 43 | let mut rng = SmallRng::from_entropy(); 44 | 45 | let mut elems: Vec<$t> = (0..LEN).map(|_| rng.gen::<$t>()).collect(); 46 | let mut i = 0; 47 | 48 | bh.iter(|| { 49 | i = (i + 1) & (LEN - 1); 50 | 51 | unsafe { test::black_box(elems.get_unchecked_mut(i).$unop()) } 52 | }) 53 | } 54 | }; 55 | } 56 | 57 | macro_rules! bench_construction { 58 | ($name: ident, $t: ty, $constructor: path [ $($args: ident: $types: ty),+ ]) => { 59 | #[bench] 60 | fn $name(bh: &mut Bencher) { 61 | const LEN: usize = 1 << 13; 62 | 63 | let mut rng = SmallRng::from_entropy(); 64 | 65 | $(let $args: Vec<$types> = (0..LEN).map(|_| rng.gen::<$types>()).collect();)* 66 | let mut i = 0; 67 | 68 | bh.iter(|| { 69 | i = (i + 1) & (LEN - 1); 70 | 71 | unsafe { 72 | let res: $t = $constructor($(*$args.get_unchecked(i),)*); 73 | test::black_box(res) 74 | } 75 | }) 76 | } 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /benches/vec.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #![feature(test)] 17 | #![allow(unused_macros)] 18 | 19 | extern crate cgmath; 20 | extern crate rand; 21 | extern crate test; 22 | 23 | use rand::{rngs::SmallRng, Rng, SeedableRng}; 24 | use std::ops::*; 25 | use test::Bencher; 26 | 27 | use cgmath::*; 28 | 29 | #[path = "common/macros.rs"] 30 | #[macro_use] 31 | mod macros; 32 | 33 | bench_binop!(_bench_vector2_add_v, Vector2, Vector2, add); 34 | bench_binop!(_bench_vector3_add_v, Vector3, Vector3, add); 35 | bench_binop!(_bench_vector4_add_v, Vector4, Vector4, add); 36 | 37 | bench_binop!(_bench_vector2_sub_v, Vector2, Vector2, sub); 38 | bench_binop!(_bench_vector3_sub_v, Vector3, Vector3, sub); 39 | bench_binop!(_bench_vector4_sub_v, Vector4, Vector4, sub); 40 | 41 | bench_binop!(_bench_vector2_mul_s, Vector2, f32, mul); 42 | bench_binop!(_bench_vector3_mul_s, Vector3, f32, mul); 43 | bench_binop!(_bench_vector4_mul_s, Vector4, f32, mul); 44 | 45 | bench_binop!(_bench_vector2_div_s, Vector2, f32, div); 46 | bench_binop!(_bench_vector3_div_s, Vector3, f32, div); 47 | bench_binop!(_bench_vector4_div_s, Vector4, f32, div); 48 | 49 | bench_binop!(_bench_vector2_rem_s, Vector2, f32, rem); 50 | bench_binop!(_bench_vector3_rem_s, Vector3, f32, rem); 51 | bench_binop!(_bench_vector4_rem_s, Vector4, f32, rem); 52 | 53 | bench_binop!(_bench_vector2_dot, Vector2, Vector2, dot); 54 | bench_binop!(_bench_vector3_dot, Vector3, Vector3, dot); 55 | bench_binop!(_bench_vector4_dot, Vector4, Vector4, dot); 56 | 57 | bench_binop!(_bench_vector3_cross, Vector3, Vector3, cross); 58 | 59 | bench_unop!(_bench_vector2_magnitude, Vector2, magnitude); 60 | bench_unop!(_bench_vector3_magnitude, Vector3, magnitude); 61 | bench_unop!(_bench_vector4_magnitude, Vector4, magnitude); 62 | 63 | bench_unop!(_bench_vector2_normalize, Vector2, normalize); 64 | bench_unop!(_bench_vector3_normalize, Vector3, normalize); 65 | bench_unop!(_bench_vector4_normalize, Vector4, normalize); 66 | -------------------------------------------------------------------------------- /benches/mat.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #![feature(test)] 17 | #![allow(unused_macros)] 18 | 19 | extern crate cgmath; 20 | extern crate rand; 21 | extern crate test; 22 | 23 | use rand::{rngs::SmallRng, Rng, SeedableRng}; 24 | use std::ops::*; 25 | use test::Bencher; 26 | 27 | use cgmath::*; 28 | 29 | #[path = "common/macros.rs"] 30 | #[macro_use] 31 | mod macros; 32 | 33 | bench_binop!(_bench_matrix2_mul_m, Matrix2, Matrix2, mul); 34 | bench_binop!(_bench_matrix3_mul_m, Matrix3, Matrix3, mul); 35 | bench_binop!(_bench_matrix4_mul_m, Matrix4, Matrix4, mul); 36 | 37 | bench_binop!(_bench_matrix2_add_m, Matrix2, Matrix2, add); 38 | bench_binop!(_bench_matrix3_add_m, Matrix3, Matrix3, add); 39 | bench_binop!(_bench_matrix4_add_m, Matrix4, Matrix4, add); 40 | 41 | bench_binop!(_bench_matrix2_sub_m, Matrix2, Matrix2, sub); 42 | bench_binop!(_bench_matrix3_sub_m, Matrix3, Matrix3, sub); 43 | bench_binop!(_bench_matrix4_sub_m, Matrix4, Matrix4, sub); 44 | 45 | bench_binop!(_bench_matrix2_mul_v, Matrix2, Vector2, mul); 46 | bench_binop!(_bench_matrix3_mul_v, Matrix3, Vector3, mul); 47 | bench_binop!(_bench_matrix4_mul_v, Matrix4, Vector4, mul); 48 | 49 | bench_binop!(_bench_matrix2_mul_s, Matrix2, f32, mul); 50 | bench_binop!(_bench_matrix3_mul_s, Matrix3, f32, mul); 51 | bench_binop!(_bench_matrix4_mul_s, Matrix4, f32, mul); 52 | 53 | bench_binop!(_bench_matrix2_div_s, Matrix2, f32, div); 54 | bench_binop!(_bench_matrix3_div_s, Matrix3, f32, div); 55 | bench_binop!(_bench_matrix4_div_s, Matrix4, f32, div); 56 | 57 | bench_unop!(_bench_matrix2_invert, Matrix2, invert); 58 | bench_unop!(_bench_matrix3_invert, Matrix3, invert); 59 | bench_unop!(_bench_matrix4_invert, Matrix4, invert); 60 | 61 | bench_unop!(_bench_matrix2_transpose, Matrix2, transpose); 62 | bench_unop!(_bench_matrix3_transpose, Matrix3, transpose); 63 | bench_unop!(_bench_matrix4_transpose, Matrix4, transpose); 64 | 65 | bench_unop!(_bench_matrix4_determinant, Matrix4, determinant); 66 | -------------------------------------------------------------------------------- /tests/projection.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | extern crate cgmath; 17 | 18 | use cgmath::{ortho, Matrix4, Vector4}; 19 | 20 | #[test] 21 | fn test_ortho_scale() { 22 | // An orthographic projection can be used to scale points 23 | // but this will result in the clip space (Z) being swapped (-1 -> 1) 24 | // this test asserts that this property is true of our ortho projection 25 | 26 | let vec_near: Vector4 = Vector4::new(-1., -1., -1., 1.); 27 | let vec_orig: Vector4 = Vector4::new(0., 0., 0., 1.); 28 | let vec_far: Vector4 = Vector4::new(1., 1., 1., 1.); 29 | 30 | let o: Matrix4 = ortho(-1., 1., -1., 1., -1., 1.); 31 | let near = o * vec_near; 32 | let orig = o * vec_orig; 33 | let far = o * vec_far; 34 | 35 | assert_eq!(near, Vector4::new(-1f32, -1., 1., 1.)); 36 | assert_eq!(orig, Vector4::new(0f32, 0., 0., 1.)); 37 | assert_eq!(far, Vector4::new(1f32, 1., -1., 1.)); 38 | 39 | let o: Matrix4 = ortho(-2., 2., -2., 2., -2., 2.); 40 | let near = o * vec_near; 41 | let orig = o * vec_orig; 42 | let far = o * vec_far; 43 | 44 | assert_eq!(near, Vector4::new(-0.5f32, -0.5, 0.5, 1.)); 45 | assert_eq!(orig, Vector4::new(0f32, 0., 0., 1.)); 46 | assert_eq!(far, Vector4::new(0.5f32, 0.5, -0.5, 1.)); 47 | } 48 | 49 | #[test] 50 | fn test_ortho_translate() { 51 | // An orthographic projection can be used to translate a point 52 | // but this will result in the clip space (Z) being swapped (-1 -> 1) 53 | // this test asserts that this property is true of our ortho projection 54 | 55 | let vec_orig: Vector4 = Vector4::new(0., 0., 0., 1.); 56 | 57 | let o: Matrix4 = ortho(-1., 1., -1., 1., -1., 1.); 58 | let orig = o * vec_orig; 59 | assert_eq!(orig, Vector4::new(0., 0., 0., 1.)); 60 | 61 | let o: Matrix4 = ortho(0., 2., 0., 2., 0., 2.); 62 | let orig = o * vec_orig; 63 | assert_eq!(orig, Vector4::new(-1., -1., -1., 1.)); 64 | 65 | let o: Matrix4 = ortho(-2., 0., -2., 0., -2., 0.); 66 | let orig = o * vec_orig; 67 | assert_eq!(orig, Vector4::new(1., 1., 1., 1.)); 68 | } 69 | -------------------------------------------------------------------------------- /tests/point.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | extern crate approx; 17 | extern crate cgmath; 18 | 19 | use cgmath::*; 20 | 21 | #[test] 22 | fn test_constructor() { 23 | assert_eq!(point1(1f32), Point1::new(1f32)); 24 | assert_eq!(point2(1f32, 2f32), Point2::new(1f32, 2f32)); 25 | assert_eq!(point3(1f64, 2f64, 3f64), Point3::new(1f64, 2f64, 3f64)); 26 | } 27 | 28 | macro_rules! impl_test_mul { 29 | ($PointN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 30 | // point * scalar ops 31 | assert_eq!($v * $s, $PointN::new($($v.$field * $s),+)); 32 | assert_eq!($s * $v, $PointN::new($($s * $v.$field),+)); 33 | assert_eq!(&$v * $s, $v * $s); 34 | assert_eq!($s * &$v, $s * $v); 35 | // commutativity 36 | assert_eq!($v * $s, $s * $v); 37 | ) 38 | } 39 | 40 | macro_rules! impl_test_div { 41 | ($PointN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 42 | // point / scalar ops 43 | assert_eq!($v / $s, $PointN::new($($v.$field / $s),+)); 44 | assert_eq!($s / $v, $PointN::new($($s / $v.$field),+)); 45 | assert_eq!(&$v / $s, $v / $s); 46 | assert_eq!($s / &$v, $s / $v); 47 | ) 48 | } 49 | 50 | macro_rules! impl_test_rem { 51 | ($PointN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 52 | // point % scalar ops 53 | assert_eq!($v % $s, $PointN::new($($v.$field % $s),+)); 54 | assert_eq!($s % $v, $PointN::new($($s % $v.$field),+)); 55 | assert_eq!(&$v % $s, $v % $s); 56 | assert_eq!($s % &$v, $s % $v); 57 | ) 58 | } 59 | 60 | #[test] 61 | fn test_homogeneous() { 62 | let p = Point3::new(1.0f64, 2.0f64, 3.0f64); 63 | assert_ulps_eq!(&p, &Point3::from_homogeneous(p.to_homogeneous())); 64 | } 65 | 66 | #[test] 67 | fn test_mul() { 68 | impl_test_mul!(Point3 { x, y, z }, 2.0f32, Point3::new(2.0f32, 4.0, 6.0)); 69 | impl_test_mul!(Point2 { x, y }, 2.0f32, Point2::new(2.0f32, 4.0)); 70 | } 71 | 72 | #[test] 73 | fn test_div() { 74 | impl_test_div!(Point3 { x, y, z }, 2.0f32, Point3::new(2.0f32, 4.0, 6.0)); 75 | impl_test_div!(Point2 { x, y }, 2.0f32, Point2::new(2.0f32, 4.0)); 76 | } 77 | 78 | #[test] 79 | fn test_rem() { 80 | impl_test_rem!(Point3 { x, y, z }, 2.0f32, Point3::new(2.0f32, 4.0, 6.0)); 81 | impl_test_rem!(Point2 { x, y }, 2.0f32, Point2::new(2.0f32, 4.0)); 82 | } 83 | 84 | #[test] 85 | fn test_cast() { 86 | assert_ulps_eq!(Point1::new(0.9f64).cast().unwrap(), Point1::new(0.9f32)); 87 | assert_ulps_eq!( 88 | Point2::new(0.9f64, 1.5).cast().unwrap(), 89 | Point2::new(0.9f32, 1.5) 90 | ); 91 | assert_ulps_eq!( 92 | Point3::new(1.0f64, 2.4, -3.13).cast().unwrap(), 93 | Point3::new(1.0f32, 2.4, -3.13) 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /tests/angle.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #[macro_use] 17 | extern crate approx; 18 | extern crate cgmath; 19 | 20 | use cgmath::{Angle, Deg, Rad}; 21 | 22 | #[test] 23 | fn test_normalize() { 24 | let angle: Rad = Rad::full_turn().normalize(); 25 | assert_ulps_eq!(&angle, &Rad(0f64)); 26 | 27 | let angle: Rad = (Rad::full_turn() + Rad::turn_div_4()).normalize(); 28 | assert_ulps_eq!(&angle, &Rad::turn_div_4()); 29 | 30 | let angle: Rad = (-Rad::turn_div_4()).normalize(); 31 | assert_ulps_eq!(&angle, &(Rad::full_turn() - Rad::turn_div_4())); 32 | } 33 | 34 | #[test] 35 | fn test_normalize_signed() { 36 | let angle: Rad = Rad::full_turn().normalize_signed(); 37 | assert_ulps_eq!(&angle, &Rad(0f64)); 38 | 39 | let angle: Rad = (Rad::full_turn() + Rad::turn_div_4()).normalize_signed(); 40 | assert_ulps_eq!(&angle, &Rad::turn_div_4()); 41 | 42 | let angle: Rad = (Rad::full_turn() - Rad::turn_div_4()).normalize_signed(); 43 | assert_ulps_eq!(&angle, &-Rad::turn_div_4()); 44 | 45 | let angle: Rad = Rad::turn_div_2().normalize_signed(); 46 | assert_ulps_eq!(&angle, &Rad::turn_div_2()); 47 | 48 | let angle: Rad = (-Rad::turn_div_2()).normalize_signed(); 49 | assert_ulps_eq!(&angle, &Rad::turn_div_2()); 50 | } 51 | 52 | #[test] 53 | fn test_conv() { 54 | let angle: Rad<_> = Deg(-5.0f64).into(); 55 | let angle: Deg<_> = angle.into(); 56 | assert_ulps_eq!(&angle, &Deg(-5.0f64)); 57 | 58 | let angle: Rad<_> = Deg(30.0f64).into(); 59 | let angle: Deg<_> = angle.into(); 60 | assert_ulps_eq!(&angle, &Deg(30.0f64)); 61 | 62 | let angle: Deg<_> = Rad(-5.0f64).into(); 63 | let angle: Rad<_> = angle.into(); 64 | assert_ulps_eq!(&angle, &Rad(-5.0f64)); 65 | 66 | let angle: Deg<_> = Rad(30.0f64).into(); 67 | let angle: Rad<_> = angle.into(); 68 | assert_ulps_eq!(&angle, &Rad(30.0f64)); 69 | } 70 | 71 | mod rad { 72 | use cgmath::Rad; 73 | 74 | #[test] 75 | fn test_iter_sum() { 76 | assert_eq!( 77 | Rad(2.0) + Rad(3.0) + Rad(4.0), 78 | [Rad(2.0), Rad(3.0), Rad(4.0)].iter().sum() 79 | ); 80 | assert_eq!( 81 | Rad(2.0) + Rad(3.0) + Rad(4.0), 82 | [Rad(2.0), Rad(3.0), Rad(4.0)].iter().cloned().sum() 83 | ); 84 | } 85 | } 86 | 87 | mod deg { 88 | use cgmath::Deg; 89 | 90 | #[test] 91 | fn test_iter_sum() { 92 | assert_eq!( 93 | Deg(2.0) + Deg(3.0) + Deg(4.0), 94 | [Deg(2.0), Deg(3.0), Deg(4.0)].iter().sum() 95 | ); 96 | assert_eq!( 97 | Deg(2.0) + Deg(3.0) + Deg(4.0), 98 | [Deg(2.0), Deg(3.0), Deg(4.0)].iter().cloned().sum() 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cgmath-rs 2 | 3 | [![Build Status](https://github.com/rustgd/cgmath/workflows/tests/badge.svg)](https://github.com/rustgd/cgmath/actions) 4 | [![Documentation](https://docs.rs/cgmath/badge.svg)](https://docs.rs/cgmath) 5 | [![Version](https://img.shields.io/crates/v/cgmath.svg)](https://crates.io/crates/cgmath) 6 | [![License](https://img.shields.io/crates/l/cgmath.svg)](https://github.com/rustgd/cgmath/blob/master/LICENSE) 7 | [![Downloads](https://img.shields.io/crates/d/cgmath.svg)](https://crates.io/crates/cgmath) 8 | [![Gitter](https://badges.gitter.im/brendanzab/cgmath.svg)](https://gitter.im/brendanzab/cgmath) 9 | 10 | A linear algebra and mathematics library for computer graphics. 11 | 12 | The library provides: 13 | 14 | - vectors: `Vector2`, `Vector3`, `Vector4` 15 | - square matrices: `Matrix2`, `Matrix3`, `Matrix4` 16 | - a quaternion type: `Quaternion` 17 | - rotation matrices: `Basis2`, `Basis3` 18 | - angle units: `Rad`, `Deg` 19 | - points: `Point2`, `Point3` 20 | - perspective projections: `Perspective`, `PerspectiveFov`, `Ortho`, `PlanarFov` 21 | - spatial transformations: `AffineMatrix3`, `Transform3` 22 | 23 | Not all of the functionality has been implemented yet, and the existing code 24 | is not fully covered by the testsuite. If you encounter any mistakes or 25 | omissions please let me know by posting an issue, or even better: send me a 26 | pull request with a fix. 27 | 28 | ## Conventions 29 | 30 | cgmath interprets its vectors as column matrices (also known as "column 31 | vectors"), meaning when transforming a vector with a matrix, the matrix goes 32 | on the left. This is reflected in the fact that cgmath implements the 33 | multiplication operator for Matrix * Vector, but not Vector * Matrix. 34 | 35 | ## Features 36 | 37 | ### Swizzling 38 | This library offers an optional feature called 39 | ["swizzling"](https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)) 40 | widely familiar to GPU programmers. To enable swizzle operators, pass the 41 | `--features="swizzle"` option to cargo. Enabling this feature will increase 42 | the size of the cgmath library by approximately 0.6MB. This isn't an 43 | issue if the library is linked in the "normal" way by adding cgmath as a 44 | dependency in Cargo.toml, which will link cgmath statically so all unused 45 | swizzle operators will be optimized away by the compiler in release mode. 46 | 47 | #### Example 48 | If we have 49 | ```rust 50 | let v = Vector3::new(1.0, 2.0, 3.0); 51 | ``` 52 | then `v.xyxz()` produces a 53 | ```rust 54 | Vector4 { x: 1.0, y: 2.0, z: 1.0, w: 3.0 } 55 | ``` 56 | and `v.zy()` produces a 57 | ```rust 58 | Vector2 { x: 3.0, y: 2.0 } 59 | ``` 60 | ### SIMD optimizations 61 | 62 | The current SIMD support depends on the deprecated "simd" package as well 63 | as the unstable "specialization" feature. To build this code, a pre-1.33 nightly 64 | build of Rust is required, e.g. 2019-01-01-nightly. Though the code is not 65 | useful in its present form, it has some worth preserving as starting point 66 | for a future migration (see https://github.com/rustgd/cgmath/issues/490). 67 | 68 | ## Limitations 69 | 70 | cgmath is _not_ an n-dimensional library and is aimed at computer graphics 71 | applications rather than general linear algebra. It only offers the 2, 3, and 72 | 4 dimensional structures that are more than enough for most computer graphics 73 | applications. This design decision was made in order to simplify the 74 | implementation (Rust cannot parameterize over constants at compile time), and to 75 | make dimension-specific optimisations easier in the future. 76 | 77 | ## Contributing 78 | 79 | Pull requests are most welcome, especially in the realm of performance 80 | enhancements and fixing any mistakes I may have made along the way. Unit tests 81 | and benchmarks are also required, so help on that front would be most 82 | appreciated. 83 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! A low-dimensional linear algebra library, targeted at computer graphics. 17 | //! 18 | //! # Trait overview 19 | //! 20 | //! In order to make a clean, composable API, we divide operations into traits 21 | //! that are roughly based on mathematical properties. The main ones that we 22 | //! concern ourselves with are listed below: 23 | //! 24 | //! - `VectorSpace`: Specifies the main operators for vectors, quaternions, and 25 | //! matrices. 26 | //! - `MetricSpace`: For types that have a distance function implemented. 27 | //! - `InnerSpace`: For types that have a dot (or inner) product - ie. vectors or 28 | //! quaternions. This also allows for the definition of operations that are 29 | //! based on the dot product, like finding the magnitude or normalizing. 30 | //! - `EuclideanSpace`: Points in euclidean space, with an associated space of 31 | //! displacement vectors. 32 | //! - `Matrix`: Common operations for matrices of arbitrary dimensions. 33 | //! - `SquareMatrix`: A special trait for matrices where the number of columns 34 | //! equal the number of rows. 35 | //! 36 | //! Other traits are included for practical convenience, for example: 37 | //! 38 | //! - `Array`: For contiguous, indexable arrays of elements, specifically 39 | //! vectors. 40 | //! - `ElementWise`: For element-wise addition, subtraction, multiplication, 41 | //! division, and remainder operations. 42 | //! 43 | //! # The prelude 44 | //! 45 | //! Importing each trait individually can become a chore, so we provide a 46 | //! `prelude` module to allow you to import the main traits all at once. For 47 | //! example: 48 | //! 49 | //! ```rust 50 | //! use cgmath::prelude::*; 51 | //! ``` 52 | 53 | #![cfg_attr(feature = "simd", feature(specialization))] 54 | 55 | #[macro_use] 56 | extern crate approx; 57 | 58 | #[cfg(feature = "bytemuck")] 59 | extern crate bytemuck; 60 | 61 | #[cfg(feature = "mint")] 62 | pub extern crate mint; 63 | 64 | pub extern crate num_traits; 65 | #[cfg(feature = "rand")] 66 | extern crate rand; 67 | 68 | #[cfg(feature = "serde")] 69 | #[macro_use] 70 | extern crate serde; 71 | 72 | #[cfg(feature = "simd")] 73 | extern crate simd; 74 | 75 | // Re-exports 76 | 77 | pub use approx::*; 78 | pub use num::*; 79 | pub use structure::*; 80 | 81 | pub use matrix::{Matrix2, Matrix3, Matrix4}; 82 | pub use quaternion::Quaternion; 83 | pub use vector::{dot, vec1, vec2, vec3, vec4, Vector1, Vector2, Vector3, Vector4}; 84 | 85 | pub use angle::{Deg, Rad}; 86 | pub use euler::Euler; 87 | pub use point::{point1, point2, point3, Point1, Point2, Point3}; 88 | pub use rotation::*; 89 | pub use transform::*; 90 | 91 | pub use projection::*; 92 | 93 | // Modules 94 | 95 | pub mod conv; 96 | pub mod prelude; 97 | 98 | mod macros; 99 | 100 | mod num; 101 | mod structure; 102 | 103 | mod matrix; 104 | mod quaternion; 105 | 106 | #[cfg(feature = "simd")] 107 | mod quaternion_simd; 108 | 109 | mod vector; 110 | 111 | #[cfg(feature = "simd")] 112 | mod vector_simd; 113 | 114 | mod angle; 115 | mod euler; 116 | mod point; 117 | mod rotation; 118 | mod transform; 119 | 120 | mod projection; 121 | -------------------------------------------------------------------------------- /src/quaternion_simd.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use quaternion::*; 17 | 18 | use structure::*; 19 | 20 | use std::mem; 21 | use std::ops::*; 22 | 23 | use simd::f32x4 as Simdf32x4; 24 | 25 | impl From for Quaternion { 26 | #[inline] 27 | fn from(f: Simdf32x4) -> Self { 28 | unsafe { 29 | let mut ret: Self = mem::MaybeUninit(); 30 | { 31 | let ret_mut: &mut [f32; 4] = ret.as_mut(); 32 | f.store(ret_mut.as_mut(), 0 as usize); 33 | } 34 | ret 35 | } 36 | } 37 | } 38 | 39 | impl From> for Simdf32x4 { 40 | #[inline] 41 | fn from(v: Quaternion) -> Self { 42 | let v_ref: &[f32; 4] = v.as_ref(); 43 | Simdf32x4::load(v_ref.as_ref(), 0 as usize) 44 | } 45 | } 46 | 47 | impl InnerSpace for Quaternion { 48 | #[inline] 49 | fn dot(self, other: Quaternion) -> f32 { 50 | let lhs: Simdf32x4 = self.into(); 51 | let rhs: Simdf32x4 = other.into(); 52 | let r = lhs * rhs; 53 | r.extract(0) + r.extract(1) + r.extract(2) + r.extract(3) 54 | } 55 | } 56 | 57 | impl_operator_simd! { 58 | [Simdf32x4]; Neg for Quaternion { 59 | fn neg(lhs) -> Quaternion { 60 | (-lhs).into() 61 | } 62 | } 63 | } 64 | 65 | impl_operator_simd! {@rs 66 | [Simdf32x4]; Mul for Quaternion { 67 | fn mul(lhs, rhs) -> Quaternion { 68 | (lhs * rhs).into() 69 | } 70 | } 71 | } 72 | 73 | impl MulAssign for Quaternion { 74 | fn mul_assign(&mut self, other: f32) { 75 | let s: Simdf32x4 = (*self).into(); 76 | let other = Simdf32x4::splat(other); 77 | *self = (s * other).into(); 78 | } 79 | } 80 | 81 | impl_operator_simd! {@rs 82 | [Simdf32x4]; Div for Quaternion { 83 | fn div(lhs, rhs) -> Quaternion { 84 | (lhs / rhs).into() 85 | } 86 | } 87 | } 88 | 89 | impl DivAssign for Quaternion { 90 | fn div_assign(&mut self, other: f32) { 91 | let s: Simdf32x4 = (*self).into(); 92 | let other = Simdf32x4::splat(other); 93 | *self = (s / other).into(); 94 | } 95 | } 96 | 97 | impl_operator_simd! { 98 | [Simdf32x4]; Add> for Quaternion { 99 | fn add(lhs, rhs) -> Quaternion { 100 | (lhs + rhs).into() 101 | } 102 | } 103 | } 104 | 105 | impl AddAssign for Quaternion { 106 | #[inline] 107 | fn add_assign(&mut self, rhs: Self) { 108 | let s: Simdf32x4 = (*self).into(); 109 | let rhs: Simdf32x4 = rhs.into(); 110 | *self = (s + rhs).into(); 111 | } 112 | } 113 | 114 | impl_operator_simd! { 115 | [Simdf32x4]; Sub> for Quaternion { 116 | fn sub(lhs, rhs) -> Quaternion { 117 | (lhs - rhs).into() 118 | } 119 | } 120 | } 121 | 122 | impl SubAssign for Quaternion { 123 | #[inline] 124 | fn sub_assign(&mut self, rhs: Self) { 125 | let s: Simdf32x4 = (*self).into(); 126 | let rhs: Simdf32x4 = rhs.into(); 127 | *self = (s - rhs).into(); 128 | } 129 | } 130 | 131 | impl_operator_simd! { 132 | [Simdf32x4]; Mul> for Quaternion { 133 | fn mul(lhs, rhs) -> Quaternion { 134 | { 135 | let p0 = Simdf32x4::splat(lhs.extract(0)) * rhs; 136 | let p1 = Simdf32x4::splat(lhs.extract(1)) * Simdf32x4::new( 137 | -rhs.extract(1), rhs.extract(0), -rhs.extract(3), rhs.extract(2) 138 | ); 139 | let p2 = Simdf32x4::splat(lhs.extract(2)) * Simdf32x4::new( 140 | -rhs.extract(2), rhs.extract(3), rhs.extract(0), -rhs.extract(1) 141 | ); 142 | let p3 = Simdf32x4::splat(lhs.extract(3)) * Simdf32x4::new( 143 | -rhs.extract(3), -rhs.extract(2), rhs.extract(1), rhs.extract(0) 144 | ); 145 | (p0 + p1 + p2 + p3).into() 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /tests/transform.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | extern crate approx; 17 | extern crate cgmath; 18 | 19 | #[cfg(feature = "serde")] 20 | extern crate serde_json; 21 | 22 | use cgmath::*; 23 | 24 | #[test] 25 | fn test_mul() { 26 | let t1 = Decomposed { 27 | scale: 2.0f64, 28 | rot: Quaternion::new(0.5f64.sqrt(), 0.5f64.sqrt(), 0.0, 0.0), 29 | disp: Vector3::new(1.0f64, 2.0, 3.0), 30 | }; 31 | let t2 = Decomposed { 32 | scale: 3.0f64, 33 | rot: Quaternion::new(0.5f64.sqrt(), 0.0, 0.5f64.sqrt(), 0.0), 34 | disp: Vector3::new(-2.0, 1.0, 0.0), 35 | }; 36 | 37 | let actual = t1 * t2; 38 | 39 | let expected = Decomposed { 40 | scale: 6.0f64, 41 | rot: Quaternion::new(0.5, 0.5, 0.5, 0.5), 42 | disp: Vector3::new(-3.0, 2.0, 5.0), 43 | }; 44 | 45 | assert_ulps_eq!(actual, expected); 46 | } 47 | 48 | #[test] 49 | fn test_mul_one() { 50 | let t = Decomposed { 51 | scale: 2.0f64, 52 | rot: Quaternion::new(0.5f64.sqrt(), 0.5f64.sqrt(), 0.0, 0.0), 53 | disp: Vector3::new(1.0f64, 2.0, 3.0), 54 | }; 55 | let one = Decomposed::one(); 56 | 57 | assert_ulps_eq!(t * one, t); 58 | assert_ulps_eq!(one * t, t); 59 | } 60 | 61 | #[test] 62 | fn test_invert() { 63 | let v = Vector3::new(1.0f64, 2.0, 3.0); 64 | let t = Decomposed { 65 | scale: 1.5f64, 66 | rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5), 67 | disp: Vector3::new(6.0f64, -7.0, 8.0), 68 | }; 69 | let ti = t 70 | .inverse_transform() 71 | .expect("Expected successful inversion"); 72 | let vt = t.transform_vector(v); 73 | assert_ulps_eq!(&v, &ti.transform_vector(vt)); 74 | } 75 | 76 | #[test] 77 | fn test_inverse_vector() { 78 | let v = Vector3::new(1.0f64, 2.0, 3.0); 79 | let t = Decomposed { 80 | scale: 1.5f64, 81 | rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5), 82 | disp: Vector3::new(6.0f64, -7.0, 8.0), 83 | }; 84 | let vt = t 85 | .inverse_transform_vector(v) 86 | .expect("Expected successful inversion"); 87 | assert_ulps_eq!(v, t.transform_vector(vt)); 88 | } 89 | 90 | #[test] 91 | #[allow(deprecated)] 92 | fn test_look_at() { 93 | let eye = Point3::new(0.0f64, 0.0, -5.0); 94 | let center = Point3::new(0.0f64, 0.0, 0.0); 95 | let up = Vector3::new(1.0f64, 0.0, 0.0); 96 | let t: Decomposed, Quaternion> = Transform::look_at(eye, center, up); 97 | assert_ulps_eq!( 98 | t, 99 | Decomposed::, Quaternion>::look_at(eye, center, up) 100 | ); 101 | let point = Point3::new(1.0f64, 0.0, 0.0); 102 | let view_point = Point3::new(0.0f64, 1.0, 5.0); 103 | assert_ulps_eq!(&t.transform_point(point), &view_point); 104 | } 105 | 106 | #[test] 107 | fn test_look_at_lh() { 108 | let eye = Point3::new(0.0f64, 0.0, -5.0); 109 | let center = Point3::new(0.0f64, 0.0, 0.0); 110 | let up = Vector3::new(1.0f64, 0.0, 0.0); 111 | let t: Decomposed, Quaternion> = Transform::look_at_lh(eye, center, up); 112 | assert_ulps_eq!( 113 | t, 114 | Decomposed::, Quaternion>::look_at_lh(eye, center, up) 115 | ); 116 | let point = Point3::new(1.0f64, 0.0, 0.0); 117 | let view_point = Point3::new(0.0f64, 1.0, 5.0); 118 | assert_ulps_eq!(&t.transform_point(point), &view_point); 119 | 120 | // Decomposed::look_at_lh and Matrix4::look_at_lh should be consistent 121 | let t: Matrix4 = Transform::look_at_lh(eye, center, up); 122 | assert_ulps_eq!(t, Matrix4::::look_at_lh(eye, center, up)); 123 | assert_ulps_eq!(&t.transform_point(point), &view_point); 124 | 125 | // Decomposed::look_at is inconsistent and deprecated, but verify that the behavior 126 | // remains the same until removed. 127 | #[allow(deprecated)] 128 | let t: Decomposed, Quaternion> = Transform::look_at(eye, center, up); 129 | assert_ulps_eq!(&t.transform_point(point), &view_point); 130 | } 131 | 132 | #[test] 133 | fn test_look_at_rh() { 134 | let eye = Point3::new(0.0f64, 0.0, -5.0); 135 | let center = Point3::new(0.0f64, 0.0, 0.0); 136 | let up = Vector3::new(1.0f64, 0.0, 0.0); 137 | let t: Decomposed, Quaternion> = Transform::look_at_rh(eye, center, up); 138 | assert_ulps_eq!( 139 | t, 140 | Decomposed::, Quaternion>::look_at_rh(eye, center, up) 141 | ); 142 | let point = Point3::new(1.0f64, 0.0, 0.0); 143 | let view_point = Point3::new(0.0f64, 1.0, -5.0); 144 | assert_ulps_eq!(&t.transform_point(point), &view_point); 145 | 146 | // Decomposed::look_at_rh and Matrix4::look_at_rh should be consistent 147 | let t: Matrix4 = Transform::look_at_rh(eye, center, up); 148 | assert_ulps_eq!(t, Matrix4::::look_at_rh(eye, center, up)); 149 | assert_ulps_eq!(&t.transform_point(point), &view_point); 150 | } 151 | 152 | #[cfg(feature = "serde")] 153 | #[test] 154 | fn test_serialize() { 155 | let t = Decomposed { 156 | scale: 1.5f64, 157 | rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5), 158 | disp: Vector3::new(6.0f64, -7.0, 8.0), 159 | }; 160 | 161 | let serialized = serde_json::to_string(&t).unwrap(); 162 | let deserialized: Decomposed, Quaternion> = 163 | serde_json::from_str(&serialized).unwrap(); 164 | 165 | assert_ulps_eq!(&t, &deserialized); 166 | } 167 | -------------------------------------------------------------------------------- /tests/vector4f32.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0f32 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0f32 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | extern crate approx; 17 | extern crate cgmath; 18 | 19 | use cgmath::*; 20 | use std::f32; 21 | 22 | #[test] 23 | fn test_constructor() { 24 | assert_eq!( 25 | vec4(1f32, 2f32, 3f32, 4f32), 26 | Vector4::new(1f32, 2f32, 3f32, 4f32) 27 | ); 28 | } 29 | 30 | #[test] 31 | fn test_from_value() { 32 | assert_eq!( 33 | Vector4::from_value(76.5f32), 34 | Vector4::new(76.5f32, 76.5f32, 76.5f32, 76.5f32) 35 | ); 36 | } 37 | 38 | macro_rules! impl_test_add { 39 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 40 | // vector + vector ops 41 | assert_eq!($v + $v, $VectorN::new($($v.$field + $v.$field),+)); 42 | assert_eq!(&$v + &$v, $v + $v); 43 | assert_eq!(&$v + $v, $v + $v); 44 | assert_eq!($v + &$v, $v + $v); 45 | ) 46 | } 47 | 48 | macro_rules! impl_test_sub { 49 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 50 | // vector - vector ops 51 | assert_eq!($v - $v, $VectorN::new($($v.$field - $v.$field),+)); 52 | assert_eq!(&$v - &$v, $v - $v); 53 | assert_eq!(&$v - $v, $v - $v); 54 | assert_eq!($v - &$v, $v - $v); 55 | ) 56 | } 57 | 58 | macro_rules! impl_test_mul { 59 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 60 | // vector * scalar ops 61 | assert_eq!($v * $s, $VectorN::new($($v.$field * $s),+)); 62 | assert_eq!($s * $v, $VectorN::new($($s * $v.$field),+)); 63 | assert_eq!(&$v * $s, $v * $s); 64 | assert_eq!($s * &$v, $s * $v); 65 | // commutativity 66 | assert_eq!($v * $s, $s * $v); 67 | ) 68 | } 69 | 70 | macro_rules! impl_test_div { 71 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 72 | // vector / scalar ops 73 | assert_eq!($v / $s, $VectorN::new($($v.$field / $s),+)); 74 | assert_eq!($s / $v, $VectorN::new($($s / $v.$field),+)); 75 | assert_eq!(&$v / $s, $v / $s); 76 | assert_eq!($s / &$v, $s / $v); 77 | ) 78 | } 79 | 80 | macro_rules! impl_test_rem { 81 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 82 | // vector % scalar ops 83 | assert_eq!($v % $s, $VectorN::new($($v.$field % $s),+)); 84 | assert_eq!($s % $v, $VectorN::new($($s % $v.$field),+)); 85 | assert_eq!(&$v % $s, $v % $s); 86 | assert_eq!($s % &$v, $s % $v); 87 | ) 88 | } 89 | 90 | #[test] 91 | fn test_add() { 92 | impl_test_add!( 93 | Vector4 { x, y, z, w }, 94 | 2.0f32, 95 | vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) 96 | ); 97 | } 98 | 99 | #[test] 100 | fn test_sub() { 101 | impl_test_sub!( 102 | Vector4 { x, y, z, w }, 103 | 2.0f32, 104 | vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) 105 | ); 106 | } 107 | 108 | #[test] 109 | fn test_mul() { 110 | impl_test_mul!( 111 | Vector4 { x, y, z, w }, 112 | 2.0f32, 113 | vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) 114 | ); 115 | } 116 | 117 | #[test] 118 | fn test_div() { 119 | impl_test_div!( 120 | Vector4 { x, y, z, w }, 121 | 2.0f32, 122 | vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) 123 | ); 124 | } 125 | 126 | #[test] 127 | fn test_rem() { 128 | impl_test_rem!( 129 | Vector4 { x, y, z, w }, 130 | 2.0f32, 131 | vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32) 132 | ); 133 | } 134 | 135 | #[test] 136 | fn test_dot() { 137 | assert_eq!( 138 | Vector4::new(1.0f32, 2.0f32, 3.0f32, 4.0f32) 139 | .dot(Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32)), 140 | 70.0f32 141 | ); 142 | } 143 | 144 | #[test] 145 | fn test_sum() { 146 | assert_eq!(Vector4::new(1f32, 2f32, 3f32, 4f32).sum(), 10f32); 147 | 148 | assert_eq!(Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32).sum(), 26.0f32); 149 | } 150 | 151 | #[test] 152 | fn test_product() { 153 | assert_eq!(Vector4::new(1f32, 2f32, 3f32, 4f32).product(), 24f32); 154 | 155 | assert_eq!( 156 | Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32).product(), 157 | 1680.0f32 158 | ); 159 | } 160 | 161 | #[test] 162 | fn test_is_perpendicular() { 163 | assert!(Vector4::new(1.0f32, 0.0f32, 0.0f32, 0.0f32) 164 | .is_perpendicular(Vector4::new(0.0f32, 0.0f32, 0.0f32, 1.0f32))); 165 | } 166 | 167 | #[cfg(test)] 168 | mod test_magnitude { 169 | use cgmath::*; 170 | 171 | #[test] 172 | fn test_vector4() { 173 | let (a, a_res) = (Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32), 11.0f32); // (1, 2, 4, 10, 11) Pythagorean quintuple 174 | let (b, b_res) = (Vector4::new(1.0f32, 2.0f32, 8.0f32, 10.0f32), 13.0f32); // (1, 2, 8, 10, 13) Pythagorean quintuple 175 | 176 | assert_eq!(a.magnitude2(), a_res * a_res); 177 | assert_eq!(b.magnitude2(), b_res * b_res); 178 | 179 | assert_eq!(a.magnitude(), a_res); 180 | assert_eq!(b.magnitude(), b_res); 181 | 182 | #[cfg(feature = "simd")] 183 | { 184 | let a = Vector4::new(1f32, 4f32, 9f32, 16f32); 185 | assert_ulps_eq!(a.sqrt_element_wide(), Vector4::new(1f32, 2f32, 3f32, 4f32)); 186 | assert_relative_eq!( 187 | a.sqrt_element_wide().recip_element_wide(), 188 | Vector4::new(1f32, 1f32 / 2f32, 1f32 / 3f32, 1f32 / 4f32), 189 | max_relative = 0.005f32 190 | ); 191 | assert_relative_eq!( 192 | a.rsqrt_element_wide(), 193 | Vector4::new(1f32, 1f32 / 2f32, 1f32 / 3f32, 1f32 / 4f32), 194 | max_relative = 0.005f32 195 | ); 196 | } 197 | } 198 | } 199 | 200 | #[test] 201 | fn test_angle() { 202 | assert_ulps_eq!( 203 | Vector4::new(1.0f32, 0.0f32, 1.0f32, 0.0f32) 204 | .angle(Vector4::new(0.0f32, 1.0f32, 0.0f32, 1.0f32)), 205 | &Rad(f32::consts::FRAC_PI_2) 206 | ); 207 | assert_ulps_eq!( 208 | Vector4::new(10.0f32, 0.0f32, 10.0f32, 0.0f32) 209 | .angle(Vector4::new(0.0f32, 5.0f32, 0.0f32, 5.0f32)), 210 | &Rad(f32::consts::FRAC_PI_2) 211 | ); 212 | assert_ulps_eq!( 213 | Vector4::new(-1.0f32, 0.0f32, -1.0f32, 0.0f32) 214 | .angle(Vector4::new(0.0f32, 1.0f32, 0.0f32, 1.0f32)), 215 | &Rad(f32::consts::FRAC_PI_2) 216 | ); 217 | } 218 | 219 | #[test] 220 | fn test_normalize() { 221 | // TODO: test normalize_to, normalize_sel.0f32, and normalize_self_to 222 | assert_ulps_eq!( 223 | Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32).normalize(), 224 | &Vector4::new( 225 | 1.0f32 / 11.0f32, 226 | 2.0f32 / 11.0f32, 227 | 4.0f32 / 11.0f32, 228 | 10.0f32 / 11.0f32 229 | ) 230 | ); 231 | } 232 | 233 | #[test] 234 | fn test_cast() { 235 | assert_ulps_eq!( 236 | Vector4::new(13.5f32, -4.6, -8.3, 2.41).cast().unwrap(), 237 | Vector4::new(13.5f32, -4.6, -8.3, 2.41) 238 | ); 239 | } 240 | -------------------------------------------------------------------------------- /src/euler.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use num_traits::cast; 17 | #[cfg(feature = "rand")] 18 | use rand::{ 19 | distributions::{Distribution, Standard}, 20 | Rng, 21 | }; 22 | 23 | use structure::*; 24 | 25 | use angle::Rad; 26 | use approx; 27 | #[cfg(feature = "mint")] 28 | use mint; 29 | use num::BaseFloat; 30 | use quaternion::Quaternion; 31 | 32 | /// A set of [Euler angles] representing a rotation in three-dimensional space. 33 | /// 34 | /// This type is marked as `#[repr(C)]`. 35 | /// 36 | /// The axis rotation sequence is XYZ. That is, the rotation is first around 37 | /// the X axis, then the Y axis, and lastly the Z axis (using intrinsic 38 | /// rotations). Since all three rotation axes are used, the angles are 39 | /// Tait–Bryan angles rather than proper Euler angles. 40 | /// 41 | /// # Ranges 42 | /// 43 | /// - x: [-pi, pi] 44 | /// - y: [-pi/2, pi/2] 45 | /// - z: [-pi, pi] 46 | /// 47 | /// # Defining rotations using Euler angles 48 | /// 49 | /// Note that while [Euler angles] are intuitive to define, they are prone to 50 | /// [gimbal lock] and are challenging to interpolate between. Instead we 51 | /// recommend that you convert them to a more robust representation, such as a 52 | /// quaternion or a rotation matrix. To this end, `From>` conversions 53 | /// are provided for the following types: 54 | /// 55 | /// - [`Basis3`](struct.Basis3.html) 56 | /// - [`Matrix3`](struct.Matrix3.html) 57 | /// - [`Matrix4`](struct.Matrix4.html) 58 | /// - [`Quaternion`](struct.Quaternion.html) 59 | /// 60 | /// For example, to define a quaternion that applies the following: 61 | /// 62 | /// 1. a 90° rotation around the _x_ axis 63 | /// 2. a 45° rotation around the _y_ axis 64 | /// 3. a 15° rotation around the _z_ axis 65 | /// 66 | /// you can use the following code: 67 | /// 68 | /// ``` 69 | /// use cgmath::{Deg, Euler, Quaternion}; 70 | /// 71 | /// let rotation = Quaternion::from(Euler { 72 | /// x: Deg(90.0), 73 | /// y: Deg(45.0), 74 | /// z: Deg(15.0), 75 | /// }); 76 | /// ``` 77 | /// 78 | /// [Euler angles]: https://en.wikipedia.org/wiki/Euler_angles 79 | /// [gimbal lock]: https://en.wikipedia.org/wiki/Gimbal_lock#Gimbal_lock_in_applied_mathematics 80 | /// [convert]: #defining-rotations-using-euler-angles 81 | #[repr(C)] 82 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 83 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 84 | pub struct Euler { 85 | /// The angle to apply around the _x_ axis. Also known at the _pitch_. 86 | pub x: A, 87 | /// The angle to apply around the _y_ axis. Also known at the _yaw_. 88 | pub y: A, 89 | /// The angle to apply around the _z_ axis. Also known at the _roll_. 90 | pub z: A, 91 | } 92 | 93 | impl Euler { 94 | /// Construct a set of euler angles. 95 | /// 96 | /// # Arguments 97 | /// 98 | /// * `x` - The angle to apply around the _x_ axis. Also known at the _pitch_. 99 | /// * `y` - The angle to apply around the _y_ axis. Also known at the _yaw_. 100 | /// * `z` - The angle to apply around the _z_ axis. Also known at the _roll_. 101 | pub const fn new(x: A, y: A, z: A) -> Euler { 102 | Euler { x, y, z } 103 | } 104 | } 105 | 106 | impl From> for Euler> { 107 | fn from(src: Quaternion) -> Euler> { 108 | let sig: S = cast(0.499).unwrap(); 109 | let two: S = cast(2).unwrap(); 110 | let one: S = cast(1).unwrap(); 111 | 112 | let (qw, qx, qy, qz) = (src.s, src.v.x, src.v.y, src.v.z); 113 | let (sqw, sqx, sqy, sqz) = (qw * qw, qx * qx, qy * qy, qz * qz); 114 | 115 | let unit = sqx + sqz + sqy + sqw; 116 | let test = qx * qz + qy * qw; 117 | 118 | // We set x to zero and z to the value, but the other way would work too. 119 | if test > sig * unit { 120 | // x + z = 2 * atan(x / w) 121 | Euler { 122 | x: Rad::zero(), 123 | y: Rad::turn_div_4(), 124 | z: Rad::atan2(qx, qw) * two, 125 | } 126 | } else if test < -sig * unit { 127 | // x - z = 2 * atan(x / w) 128 | Euler { 129 | x: Rad::zero(), 130 | y: -Rad::turn_div_4(), 131 | z: -Rad::atan2(qx, qw) * two, 132 | } 133 | } else { 134 | // Using the quat-to-matrix equation from either 135 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm 136 | // or equation 15 on page 7 of 137 | // http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf 138 | // to fill in the equations on page A-2 of the NASA document gives the below. 139 | Euler { 140 | x: Rad::atan2(two * (-qy * qz + qx * qw), one - two * (sqx + sqy)), 141 | y: Rad::asin(two * (qx * qz + qy * qw)), 142 | z: Rad::atan2(two * (-qx * qy + qz * qw), one - two * (sqy + sqz)), 143 | } 144 | } 145 | } 146 | } 147 | 148 | impl approx::AbsDiffEq for Euler { 149 | type Epsilon = A::Epsilon; 150 | 151 | #[inline] 152 | fn default_epsilon() -> A::Epsilon { 153 | A::default_epsilon() 154 | } 155 | 156 | #[inline] 157 | fn abs_diff_eq(&self, other: &Self, epsilon: A::Epsilon) -> bool { 158 | A::abs_diff_eq(&self.x, &other.x, epsilon) 159 | && A::abs_diff_eq(&self.y, &other.y, epsilon) 160 | && A::abs_diff_eq(&self.z, &other.z, epsilon) 161 | } 162 | } 163 | 164 | impl approx::RelativeEq for Euler { 165 | #[inline] 166 | fn default_max_relative() -> A::Epsilon { 167 | A::default_max_relative() 168 | } 169 | 170 | #[inline] 171 | fn relative_eq(&self, other: &Self, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool { 172 | A::relative_eq(&self.x, &other.x, epsilon, max_relative) 173 | && A::relative_eq(&self.y, &other.y, epsilon, max_relative) 174 | && A::relative_eq(&self.z, &other.z, epsilon, max_relative) 175 | } 176 | } 177 | 178 | impl approx::UlpsEq for Euler { 179 | #[inline] 180 | fn default_max_ulps() -> u32 { 181 | A::default_max_ulps() 182 | } 183 | 184 | #[inline] 185 | fn ulps_eq(&self, other: &Self, epsilon: A::Epsilon, max_ulps: u32) -> bool { 186 | A::ulps_eq(&self.x, &other.x, epsilon, max_ulps) 187 | && A::ulps_eq(&self.y, &other.y, epsilon, max_ulps) 188 | && A::ulps_eq(&self.z, &other.z, epsilon, max_ulps) 189 | } 190 | } 191 | 192 | #[cfg(feature = "rand")] 193 | impl Distribution> for Standard 194 | where 195 | Standard: Distribution, 196 | A: Angle, 197 | { 198 | fn sample(&self, rng: &mut R) -> Euler { 199 | Euler { 200 | x: rng.gen(), 201 | y: rng.gen(), 202 | z: rng.gen(), 203 | } 204 | } 205 | } 206 | 207 | #[cfg(feature = "mint")] 208 | type MintEuler = mint::EulerAngles; 209 | 210 | #[cfg(feature = "mint")] 211 | impl> From> for Euler { 212 | fn from(mint: MintEuler) -> Self { 213 | Euler { 214 | x: mint.a.into(), 215 | y: mint.b.into(), 216 | z: mint.c.into(), 217 | } 218 | } 219 | } 220 | 221 | #[cfg(feature = "mint")] 222 | impl> From> for MintEuler { 223 | fn from(v: Euler) -> Self { 224 | MintEuler::from([v.x.into(), v.y.into(), v.z.into()]) 225 | } 226 | } 227 | 228 | #[cfg(feature = "bytemuck")] 229 | impl_bytemuck_cast!(Euler); 230 | -------------------------------------------------------------------------------- /src/angle.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Angle units for type-safe, self-documenting code. 17 | 18 | use std::f64; 19 | use std::fmt; 20 | use std::iter; 21 | use std::ops::*; 22 | 23 | use num_traits::{cast, Bounded}; 24 | #[cfg(feature = "rand")] 25 | use rand::{ 26 | distributions::{uniform::SampleUniform, Distribution, Standard}, 27 | Rng, 28 | }; 29 | 30 | use structure::*; 31 | 32 | use approx; 33 | use num::{BaseFloat, BaseNum}; 34 | 35 | /// An angle, in radians. 36 | /// 37 | /// This type is marked as `#[repr(C)]`. 38 | #[repr(C)] 39 | #[derive(Copy, Clone, PartialEq, PartialOrd)] 40 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 41 | pub struct Rad(pub S); 42 | 43 | /// An angle, in degrees. 44 | /// 45 | /// This type is marked as `#[repr(C)]`. 46 | #[repr(C)] 47 | #[derive(Copy, Clone, PartialEq, PartialOrd)] 48 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 49 | pub struct Deg(pub S); 50 | 51 | impl From> for Deg 52 | where 53 | S: BaseFloat, 54 | { 55 | #[inline] 56 | fn from(rad: Rad) -> Deg { 57 | Deg(rad.0 * cast(180.0 / f64::consts::PI).unwrap()) 58 | } 59 | } 60 | 61 | impl From> for Rad 62 | where 63 | S: BaseFloat, 64 | { 65 | #[inline] 66 | fn from(deg: Deg) -> Rad { 67 | Rad(deg.0 * cast(f64::consts::PI / 180.0).unwrap()) 68 | } 69 | } 70 | 71 | macro_rules! impl_angle { 72 | ($Angle:ident, $fmt:expr, $full_turn:expr, $hi:expr) => { 73 | impl Zero for $Angle { 74 | #[inline] 75 | fn zero() -> $Angle { 76 | $Angle(S::zero()) 77 | } 78 | 79 | #[inline] 80 | fn is_zero(&self) -> bool { 81 | ulps_eq!(self, &Self::zero()) 82 | } 83 | } 84 | 85 | impl iter::Sum<$Angle> for $Angle { 86 | #[inline] 87 | fn sum>>(iter: I) -> $Angle { 88 | iter.fold($Angle::zero(), Add::add) 89 | } 90 | } 91 | 92 | impl<'a, S: 'a + BaseFloat> iter::Sum<&'a $Angle> for $Angle { 93 | #[inline] 94 | fn sum>>(iter: I) -> $Angle { 95 | iter.fold($Angle::zero(), Add::add) 96 | } 97 | } 98 | 99 | impl Angle for $Angle { 100 | type Unitless = S; 101 | 102 | #[inline] fn full_turn() -> $Angle { $Angle(cast($full_turn).unwrap()) } 103 | 104 | #[inline] fn sin(self) -> S { Rad::from(self).0.sin() } 105 | #[inline] fn cos(self) -> S { Rad::from(self).0.cos() } 106 | #[inline] fn tan(self) -> S { Rad::from(self).0.tan() } 107 | #[inline] fn sin_cos(self) -> (S, S) { Rad::from(self).0.sin_cos() } 108 | 109 | #[inline] fn asin(a: S) -> $Angle { Rad(a.asin()).into() } 110 | #[inline] fn acos(a: S) -> $Angle { Rad(a.acos()).into() } 111 | #[inline] fn atan(a: S) -> $Angle { Rad(a.atan()).into() } 112 | #[inline] fn atan2(a: S, b: S) -> $Angle { Rad(a.atan2(b)).into() } 113 | } 114 | 115 | impl Neg for $Angle { 116 | type Output = $Angle; 117 | 118 | #[inline] 119 | fn neg(self) -> $Angle { $Angle(-self.0) } 120 | } 121 | 122 | impl<'a, S: BaseFloat> Neg for &'a $Angle { 123 | type Output = $Angle; 124 | 125 | #[inline] 126 | fn neg(self) -> $Angle { $Angle(-self.0) } 127 | } 128 | 129 | impl Bounded for $Angle { 130 | #[inline] 131 | fn min_value() -> $Angle { 132 | $Angle(S::min_value()) 133 | } 134 | 135 | #[inline] 136 | fn max_value() -> $Angle { 137 | $Angle(S::max_value()) 138 | } 139 | } 140 | 141 | impl_operator!( Add<$Angle > for $Angle { 142 | fn add(lhs, rhs) -> $Angle { $Angle(lhs.0 + rhs.0) } 143 | }); 144 | impl_operator!( Sub<$Angle > for $Angle { 145 | fn sub(lhs, rhs) -> $Angle { $Angle(lhs.0 - rhs.0) } 146 | }); 147 | impl_operator!( Div<$Angle > for $Angle { 148 | fn div(lhs, rhs) -> S { lhs.0 / rhs.0 } 149 | }); 150 | impl_operator!( Rem<$Angle > for $Angle { 151 | fn rem(lhs, rhs) -> $Angle { $Angle(lhs.0 % rhs.0) } 152 | }); 153 | impl_assignment_operator!( AddAssign<$Angle > for $Angle { 154 | fn add_assign(&mut self, other) { self.0 += other.0; } 155 | }); 156 | impl_assignment_operator!( SubAssign<$Angle > for $Angle { 157 | fn sub_assign(&mut self, other) { self.0 -= other.0; } 158 | }); 159 | impl_assignment_operator!( RemAssign<$Angle > for $Angle { 160 | fn rem_assign(&mut self, other) { self.0 %= other.0; } 161 | }); 162 | 163 | impl_operator!( Mul for $Angle { 164 | fn mul(lhs, scalar) -> $Angle { $Angle(lhs.0 * scalar) } 165 | }); 166 | impl_operator!( Div for $Angle { 167 | fn div(lhs, scalar) -> $Angle { $Angle(lhs.0 / scalar) } 168 | }); 169 | impl_assignment_operator!( MulAssign for $Angle { 170 | fn mul_assign(&mut self, scalar) { self.0 *= scalar; } 171 | }); 172 | impl_assignment_operator!( DivAssign for $Angle { 173 | fn div_assign(&mut self, scalar) { self.0 /= scalar; } 174 | }); 175 | 176 | impl approx::AbsDiffEq for $Angle { 177 | type Epsilon = S::Epsilon; 178 | 179 | #[inline] 180 | fn default_epsilon() -> S::Epsilon { 181 | S::default_epsilon() 182 | } 183 | 184 | #[inline] 185 | fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { 186 | S::abs_diff_eq(&self.0, &other.0, epsilon) 187 | } 188 | } 189 | 190 | impl approx::RelativeEq for $Angle { 191 | #[inline] 192 | fn default_max_relative() -> S::Epsilon { 193 | S::default_max_relative() 194 | } 195 | 196 | #[inline] 197 | fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { 198 | S::relative_eq(&self.0, &other.0, epsilon, max_relative) 199 | } 200 | } 201 | 202 | impl approx::UlpsEq for $Angle { 203 | #[inline] 204 | fn default_max_ulps() -> u32 { 205 | S::default_max_ulps() 206 | } 207 | 208 | #[inline] 209 | fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { 210 | S::ulps_eq(&self.0, &other.0, epsilon, max_ulps) 211 | } 212 | } 213 | 214 | #[cfg(feature = "rand")] 215 | impl Distribution<$Angle> for Standard 216 | where Standard: Distribution, 217 | S: BaseFloat + SampleUniform { 218 | #[inline] 219 | fn sample(&self, rng: &mut R) -> $Angle { 220 | $Angle(rng.gen_range(cast::<_, S>(-$hi).unwrap() .. cast::<_, S>($hi).unwrap())) 221 | } 222 | } 223 | 224 | impl fmt::Debug for $Angle { 225 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 226 | write!(f, $fmt, self.0) 227 | } 228 | } 229 | } 230 | } 231 | 232 | impl_angle!(Rad, "{:?} rad", f64::consts::PI * 2.0, f64::consts::PI); 233 | impl_angle!(Deg, "{:?}°", 360, 180); 234 | -------------------------------------------------------------------------------- /src/vector_simd.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use vector::*; 17 | 18 | use structure::*; 19 | 20 | use std::mem; 21 | use std::ops::*; 22 | 23 | use simd::f32x4 as Simdf32x4; 24 | use simd::i32x4 as Simdi32x4; 25 | use simd::u32x4 as Simdu32x4; 26 | 27 | impl From for Vector4 { 28 | #[inline] 29 | fn from(f: Simdf32x4) -> Self { 30 | unsafe { 31 | let mut ret: Self = mem::MaybeUninit(); 32 | { 33 | let ret_mut: &mut [f32; 4] = ret.as_mut(); 34 | f.store(ret_mut.as_mut(), 0 as usize); 35 | } 36 | ret 37 | } 38 | } 39 | } 40 | 41 | impl Vector4 { 42 | /// Compute and return the square root of each element. 43 | #[inline] 44 | pub fn sqrt_element_wide(self) -> Self { 45 | let s: Simdf32x4 = self.into(); 46 | s.sqrt().into() 47 | } 48 | 49 | /// Compute and return the reciprocal of the square root of each element. 50 | #[inline] 51 | pub fn rsqrt_element_wide(self) -> Self { 52 | let s: Simdf32x4 = self.into(); 53 | s.approx_rsqrt().into() 54 | } 55 | 56 | /// Compute and return the reciprocal of each element. 57 | #[inline] 58 | pub fn recip_element_wide(self) -> Self { 59 | let s: Simdf32x4 = self.into(); 60 | s.approx_reciprocal().into() 61 | } 62 | } 63 | 64 | impl From> for Simdf32x4 { 65 | #[inline] 66 | fn from(v: Vector4) -> Self { 67 | let v_ref: &[f32; 4] = v.as_ref(); 68 | Simdf32x4::load(v_ref.as_ref(), 0 as usize) 69 | } 70 | } 71 | 72 | impl_operator_simd! { 73 | [Simdf32x4]; Add> for Vector4 { 74 | fn add(lhs, rhs) -> Vector4 { 75 | (lhs + rhs).into() 76 | } 77 | } 78 | } 79 | 80 | impl_operator_simd! { 81 | [Simdf32x4]; Sub> for Vector4 { 82 | fn sub(lhs, rhs) -> Vector4 { 83 | (lhs - rhs).into() 84 | } 85 | } 86 | } 87 | 88 | impl_operator_simd! {@rs 89 | [Simdf32x4]; Mul for Vector4 { 90 | fn mul(lhs, rhs) -> Vector4 { 91 | (lhs * rhs).into() 92 | } 93 | } 94 | } 95 | 96 | impl_operator_simd! {@rs 97 | [Simdf32x4]; Div for Vector4 { 98 | fn div(lhs, rhs) -> Vector4 { 99 | (lhs / rhs).into() 100 | } 101 | } 102 | } 103 | 104 | impl_operator_simd! { 105 | [Simdf32x4]; Neg for Vector4 { 106 | fn neg(lhs) -> Vector4 { 107 | (-lhs).into() 108 | } 109 | } 110 | } 111 | 112 | impl AddAssign for Vector4 { 113 | #[inline] 114 | fn add_assign(&mut self, rhs: Self) { 115 | let s: Simdf32x4 = (*self).into(); 116 | let rhs: Simdf32x4 = rhs.into(); 117 | *self = (s + rhs).into(); 118 | } 119 | } 120 | 121 | impl SubAssign for Vector4 { 122 | #[inline] 123 | fn sub_assign(&mut self, rhs: Self) { 124 | let s: Simdf32x4 = (*self).into(); 125 | let rhs: Simdf32x4 = rhs.into(); 126 | *self = (s - rhs).into(); 127 | } 128 | } 129 | 130 | impl MulAssign for Vector4 { 131 | fn mul_assign(&mut self, other: f32) { 132 | let s: Simdf32x4 = (*self).into(); 133 | let other = Simdf32x4::splat(other); 134 | *self = (s * other).into(); 135 | } 136 | } 137 | 138 | impl DivAssign for Vector4 { 139 | fn div_assign(&mut self, other: f32) { 140 | let s: Simdf32x4 = (*self).into(); 141 | let other = Simdf32x4::splat(other); 142 | *self = (s / other).into(); 143 | } 144 | } 145 | 146 | impl ElementWise for Vector4 { 147 | #[inline] 148 | fn add_element_wise(self, rhs: Vector4) -> Vector4 { 149 | self + rhs 150 | } 151 | #[inline] 152 | fn sub_element_wise(self, rhs: Vector4) -> Vector4 { 153 | self - rhs 154 | } 155 | #[inline] 156 | fn mul_element_wise(self, rhs: Vector4) -> Vector4 { 157 | let s: Simdf32x4 = self.into(); 158 | let rhs: Simdf32x4 = rhs.into(); 159 | (s * rhs).into() 160 | } 161 | #[inline] 162 | fn div_element_wise(self, rhs: Vector4) -> Vector4 { 163 | let s: Simdf32x4 = self.into(); 164 | let rhs: Simdf32x4 = rhs.into(); 165 | (s / rhs).into() 166 | } 167 | 168 | #[inline] 169 | fn add_assign_element_wise(&mut self, rhs: Vector4) { 170 | (*self) += rhs; 171 | } 172 | 173 | #[inline] 174 | fn sub_assign_element_wise(&mut self, rhs: Vector4) { 175 | (*self) -= rhs; 176 | } 177 | 178 | #[inline] 179 | fn mul_assign_element_wise(&mut self, rhs: Vector4) { 180 | let s: Simdf32x4 = (*self).into(); 181 | let rhs: Simdf32x4 = rhs.into(); 182 | *self = (s * rhs).into(); 183 | } 184 | 185 | #[inline] 186 | fn div_assign_element_wise(&mut self, rhs: Vector4) { 187 | let s: Simdf32x4 = (*self).into(); 188 | let rhs: Simdf32x4 = rhs.into(); 189 | *self = (s * rhs).into(); 190 | } 191 | } 192 | 193 | impl ElementWise for Vector4 { 194 | #[inline] 195 | fn add_element_wise(self, rhs: f32) -> Vector4 { 196 | let s: Simdf32x4 = self.into(); 197 | let rhs = Simdf32x4::splat(rhs); 198 | (s + rhs).into() 199 | } 200 | 201 | #[inline] 202 | fn sub_element_wise(self, rhs: f32) -> Vector4 { 203 | let s: Simdf32x4 = self.into(); 204 | let rhs = Simdf32x4::splat(rhs); 205 | (s - rhs).into() 206 | } 207 | 208 | #[inline] 209 | fn mul_element_wise(self, rhs: f32) -> Vector4 { 210 | self * rhs 211 | } 212 | 213 | #[inline] 214 | fn div_element_wise(self, rhs: f32) -> Vector4 { 215 | self / rhs 216 | } 217 | 218 | #[inline] 219 | fn add_assign_element_wise(&mut self, rhs: f32) { 220 | let s: Simdf32x4 = (*self).into(); 221 | let rhs = Simdf32x4::splat(rhs); 222 | *self = (s + rhs).into(); 223 | } 224 | 225 | #[inline] 226 | fn sub_assign_element_wise(&mut self, rhs: f32) { 227 | let s: Simdf32x4 = (*self).into(); 228 | let rhs = Simdf32x4::splat(rhs); 229 | *self = (s - rhs).into(); 230 | } 231 | 232 | #[inline] 233 | fn mul_assign_element_wise(&mut self, rhs: f32) { 234 | (*self) *= rhs; 235 | } 236 | 237 | #[inline] 238 | fn div_assign_element_wise(&mut self, rhs: f32) { 239 | (*self) /= rhs; 240 | } 241 | } 242 | 243 | impl From for Vector4 { 244 | #[inline] 245 | fn from(f: Simdi32x4) -> Self { 246 | unsafe { 247 | let mut ret: Self = mem::MaybeUninit(); 248 | { 249 | let ret_mut: &mut [i32; 4] = ret.as_mut(); 250 | f.store(ret_mut.as_mut(), 0 as usize); 251 | } 252 | ret 253 | } 254 | } 255 | } 256 | 257 | impl From> for Simdi32x4 { 258 | #[inline] 259 | fn from(v: Vector4) -> Self { 260 | let v_ref: &[i32; 4] = v.as_ref(); 261 | Simdi32x4::load(v_ref.as_ref(), 0 as usize) 262 | } 263 | } 264 | 265 | impl_operator_simd! { 266 | [Simdi32x4]; Add> for Vector4 { 267 | fn add(lhs, rhs) -> Vector4 { 268 | (lhs + rhs).into() 269 | } 270 | } 271 | } 272 | 273 | impl_operator_simd! { 274 | [Simdi32x4]; Sub> for Vector4 { 275 | fn sub(lhs, rhs) -> Vector4 { 276 | (lhs - rhs).into() 277 | } 278 | } 279 | } 280 | 281 | impl_operator_simd! {@rs 282 | [Simdi32x4]; Mul for Vector4 { 283 | fn mul(lhs, rhs) -> Vector4 { 284 | (lhs * rhs).into() 285 | } 286 | } 287 | } 288 | 289 | impl_operator_simd! { 290 | [Simdi32x4]; Neg for Vector4 { 291 | fn neg(lhs) -> Vector4 { 292 | (-lhs).into() 293 | } 294 | } 295 | } 296 | 297 | impl AddAssign for Vector4 { 298 | #[inline] 299 | fn add_assign(&mut self, rhs: Self) { 300 | let s: Simdi32x4 = (*self).into(); 301 | let rhs: Simdi32x4 = rhs.into(); 302 | *self = (s + rhs).into(); 303 | } 304 | } 305 | 306 | impl SubAssign for Vector4 { 307 | #[inline] 308 | fn sub_assign(&mut self, rhs: Self) { 309 | let s: Simdi32x4 = (*self).into(); 310 | let rhs: Simdi32x4 = rhs.into(); 311 | *self = (s - rhs).into(); 312 | } 313 | } 314 | 315 | impl MulAssign for Vector4 { 316 | fn mul_assign(&mut self, other: i32) { 317 | let s: Simdi32x4 = (*self).into(); 318 | let other = Simdi32x4::splat(other); 319 | *self = (s * other).into(); 320 | } 321 | } 322 | 323 | impl From for Vector4 { 324 | #[inline] 325 | fn from(f: Simdu32x4) -> Self { 326 | unsafe { 327 | let mut ret: Self = mem::MaybeUninit(); 328 | { 329 | let ret_mut: &mut [u32; 4] = ret.as_mut(); 330 | f.store(ret_mut.as_mut(), 0 as usize); 331 | } 332 | ret 333 | } 334 | } 335 | } 336 | 337 | impl From> for Simdu32x4 { 338 | #[inline] 339 | fn from(v: Vector4) -> Self { 340 | let v_ref: &[u32; 4] = v.as_ref(); 341 | Simdu32x4::load(v_ref.as_ref(), 0 as usize) 342 | } 343 | } 344 | 345 | impl_operator_simd! { 346 | [Simdu32x4]; Add> for Vector4 { 347 | fn add(lhs, rhs) -> Vector4 { 348 | (lhs + rhs).into() 349 | } 350 | } 351 | } 352 | 353 | impl_operator_simd! { 354 | [Simdu32x4]; Sub> for Vector4 { 355 | fn sub(lhs, rhs) -> Vector4 { 356 | (lhs - rhs).into() 357 | } 358 | } 359 | } 360 | 361 | impl_operator_simd! {@rs 362 | [Simdu32x4]; Mul for Vector4 { 363 | fn mul(lhs, rhs) -> Vector4 { 364 | (lhs * rhs).into() 365 | } 366 | } 367 | } 368 | 369 | impl AddAssign for Vector4 { 370 | #[inline] 371 | fn add_assign(&mut self, rhs: Self) { 372 | let s: Simdu32x4 = (*self).into(); 373 | let rhs: Simdu32x4 = rhs.into(); 374 | *self = (s + rhs).into(); 375 | } 376 | } 377 | 378 | impl SubAssign for Vector4 { 379 | #[inline] 380 | fn sub_assign(&mut self, rhs: Self) { 381 | let s: Simdu32x4 = (*self).into(); 382 | let rhs: Simdu32x4 = rhs.into(); 383 | *self = (s - rhs).into(); 384 | } 385 | } 386 | 387 | impl MulAssign for Vector4 { 388 | fn mul_assign(&mut self, other: u32) { 389 | let s: Simdu32x4 = (*self).into(); 390 | let other = Simdu32x4::splat(other); 391 | *self = (s * other).into(); 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /tests/vector.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | extern crate approx; 17 | extern crate cgmath; 18 | 19 | use cgmath::*; 20 | use std::f64; 21 | use std::iter; 22 | 23 | #[test] 24 | fn test_constructor() { 25 | assert_eq!(vec1(1f32), Vector1::new(1f32)); 26 | assert_eq!(vec2(1f32, 2f32), Vector2::new(1f32, 2f32)); 27 | assert_eq!(vec3(1f64, 2f64, 3f64), Vector3::new(1f64, 2f64, 3f64)); 28 | assert_eq!( 29 | vec4(1isize, 2isize, 3isize, 4isize), 30 | Vector4::new(1isize, 2isize, 3isize, 4isize) 31 | ); 32 | } 33 | 34 | #[test] 35 | fn test_from_value() { 36 | assert_eq!( 37 | Vector2::from_value(102isize), 38 | Vector2::new(102isize, 102isize) 39 | ); 40 | assert_eq!( 41 | Vector3::from_value(22isize), 42 | Vector3::new(22isize, 22isize, 22isize) 43 | ); 44 | assert_eq!( 45 | Vector4::from_value(76.5f64), 46 | Vector4::new(76.5f64, 76.5f64, 76.5f64, 76.5f64) 47 | ); 48 | } 49 | 50 | macro_rules! impl_test_add { 51 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 52 | // vector + vector ops 53 | assert_eq!($v + $v, $VectorN::new($($v.$field + $v.$field),+)); 54 | assert_eq!(&$v + &$v, $v + $v); 55 | assert_eq!(&$v + $v, $v + $v); 56 | assert_eq!($v + &$v, $v + $v); 57 | ) 58 | } 59 | 60 | macro_rules! impl_test_sub { 61 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 62 | // vector - vector ops 63 | assert_eq!($v - $v, $VectorN::new($($v.$field - $v.$field),+)); 64 | assert_eq!(&$v - &$v, $v - $v); 65 | assert_eq!(&$v - $v, $v - $v); 66 | assert_eq!($v - &$v, $v - $v); 67 | ) 68 | } 69 | 70 | macro_rules! impl_test_mul { 71 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 72 | // vector * scalar ops 73 | assert_eq!($v * $s, $VectorN::new($($v.$field * $s),+)); 74 | assert_eq!($s * $v, $VectorN::new($($s * $v.$field),+)); 75 | assert_eq!(&$v * $s, $v * $s); 76 | assert_eq!($s * &$v, $s * $v); 77 | // commutativity 78 | assert_eq!($v * $s, $s * $v); 79 | ) 80 | } 81 | 82 | macro_rules! impl_test_div { 83 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 84 | // vector / scalar ops 85 | assert_eq!($v / $s, $VectorN::new($($v.$field / $s),+)); 86 | assert_eq!($s / $v, $VectorN::new($($s / $v.$field),+)); 87 | assert_eq!(&$v / $s, $v / $s); 88 | assert_eq!($s / &$v, $s / $v); 89 | ) 90 | } 91 | 92 | macro_rules! impl_test_rem { 93 | ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( 94 | // vector % scalar ops 95 | assert_eq!($v % $s, $VectorN::new($($v.$field % $s),+)); 96 | assert_eq!($s % $v, $VectorN::new($($s % $v.$field),+)); 97 | assert_eq!(&$v % $s, $v % $s); 98 | assert_eq!($s % &$v, $s % $v); 99 | ) 100 | } 101 | 102 | macro_rules! impl_test_iter_sum { 103 | ($VectorN:ident { $($field:ident),+ }, $ty:ty, $s:expr, $v:expr) => ( 104 | assert_eq!($VectorN::new($($v.$field * $s),+), 105 | iter::repeat($v).take($s as usize).sum()); 106 | ) 107 | } 108 | 109 | #[test] 110 | fn test_add() { 111 | impl_test_add!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); 112 | impl_test_add!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); 113 | impl_test_add!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); 114 | } 115 | 116 | #[test] 117 | fn test_sub() { 118 | impl_test_sub!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); 119 | impl_test_sub!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); 120 | impl_test_sub!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); 121 | } 122 | 123 | #[test] 124 | fn test_mul() { 125 | impl_test_mul!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); 126 | impl_test_mul!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); 127 | impl_test_mul!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); 128 | } 129 | 130 | #[test] 131 | fn test_div() { 132 | impl_test_div!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); 133 | impl_test_div!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); 134 | impl_test_div!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); 135 | } 136 | 137 | #[test] 138 | fn test_rem() { 139 | impl_test_rem!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0)); 140 | impl_test_rem!(Vector3 { x, y, z }, 2.0f32, vec3(2.0f32, 4.0, 6.0)); 141 | impl_test_rem!(Vector2 { x, y }, 2.0f32, vec2(2.0f32, 4.0)); 142 | } 143 | 144 | #[test] 145 | fn test_dot() { 146 | assert_eq!(Vector2::new(1.0, 2.0).dot(Vector2::new(3.0, 4.0)), 11.0); 147 | assert_eq!( 148 | Vector3::new(1.0, 2.0, 3.0).dot(Vector3::new(4.0, 5.0, 6.0)), 149 | 32.0 150 | ); 151 | assert_eq!( 152 | Vector4::new(1.0, 2.0, 3.0, 4.0).dot(Vector4::new(5.0, 6.0, 7.0, 8.0)), 153 | 70.0 154 | ); 155 | } 156 | 157 | #[test] 158 | fn test_sum() { 159 | assert_eq!(Vector2::new(1isize, 2isize).sum(), 3isize); 160 | assert_eq!(Vector3::new(1isize, 2isize, 3isize).sum(), 6isize); 161 | assert_eq!(Vector4::new(1isize, 2isize, 3isize, 4isize).sum(), 10isize); 162 | 163 | assert_eq!(Vector2::new(3.0f64, 4.0f64).sum(), 7.0f64); 164 | assert_eq!(Vector3::new(4.0f64, 5.0f64, 6.0f64).sum(), 15.0f64); 165 | assert_eq!(Vector4::new(5.0f64, 6.0f64, 7.0f64, 8.0f64).sum(), 26.0f64); 166 | } 167 | 168 | #[test] 169 | fn test_iter_sum() { 170 | impl_test_iter_sum!( 171 | Vector4 { x, y, z, w }, 172 | f32, 173 | 2.0f32, 174 | vec4(2.0f32, 4.0, 6.0, 8.0) 175 | ); 176 | impl_test_iter_sum!(Vector3 { x, y, z }, f32, 2.0f32, vec3(2.0f32, 4.0, 6.0)); 177 | impl_test_iter_sum!(Vector2 { x, y }, f32, 2.0f32, vec2(2.0f32, 4.0)); 178 | 179 | impl_test_iter_sum!(Vector4 { x, y, z, w }, usize, 2usize, vec4(2usize, 4, 6, 8)); 180 | impl_test_iter_sum!(Vector3 { x, y, z }, usize, 2usize, vec3(2usize, 4, 6)); 181 | impl_test_iter_sum!(Vector2 { x, y }, usize, 2usize, vec2(2usize, 4)); 182 | } 183 | 184 | #[test] 185 | fn test_product() { 186 | assert_eq!(Vector2::new(1isize, 2isize).product(), 2isize); 187 | assert_eq!(Vector3::new(1isize, 2isize, 3isize).product(), 6isize); 188 | assert_eq!( 189 | Vector4::new(1isize, 2isize, 3isize, 4isize).product(), 190 | 24isize 191 | ); 192 | 193 | assert_eq!(Vector2::new(3.0f64, 4.0f64).product(), 12.0f64); 194 | assert_eq!(Vector3::new(4.0f64, 5.0f64, 6.0f64).product(), 120.0f64); 195 | assert_eq!( 196 | Vector4::new(5.0f64, 6.0f64, 7.0f64, 8.0f64).product(), 197 | 1680.0f64 198 | ); 199 | } 200 | 201 | #[test] 202 | fn test_cross() { 203 | let a = Vector3::new(1isize, 2isize, 3isize); 204 | let b = Vector3::new(4isize, 5isize, 6isize); 205 | let r = Vector3::new(-3isize, 6isize, -3isize); 206 | assert_eq!(a.cross(b), r); 207 | } 208 | 209 | #[test] 210 | fn test_is_perpendicular() { 211 | assert!(Vector2::new(1.0f64, 0.0f64).is_perpendicular(Vector2::new(0.0f64, 1.0f64))); 212 | assert!( 213 | Vector3::new(0.0f64, 1.0f64, 0.0f64).is_perpendicular(Vector3::new(0.0f64, 0.0f64, 1.0f64)) 214 | ); 215 | assert!(Vector4::new(1.0f64, 0.0f64, 0.0f64, 0.0f64) 216 | .is_perpendicular(Vector4::new(0.0f64, 0.0f64, 0.0f64, 1.0f64))); 217 | } 218 | 219 | #[cfg(test)] 220 | mod test_magnitude { 221 | use cgmath::*; 222 | 223 | #[test] 224 | fn test_vector2() { 225 | let (a, a_res) = (Vector2::new(3.0f64, 4.0f64), 5.0f64); // (3, 4, 5) Pythagorean triple 226 | let (b, b_res) = (Vector2::new(5.0f64, 12.0f64), 13.0f64); // (5, 12, 13) Pythagorean triple 227 | 228 | assert_eq!(a.magnitude2(), a_res * a_res); 229 | assert_eq!(b.magnitude2(), b_res * b_res); 230 | 231 | assert_eq!(a.magnitude(), a_res); 232 | assert_eq!(b.magnitude(), b_res); 233 | } 234 | 235 | #[test] 236 | fn test_vector3() { 237 | let (a, a_res) = (Vector3::new(2.0f64, 3.0f64, 6.0f64), 7.0f64); // (2, 3, 6, 7) Pythagorean quadruple 238 | let (b, b_res) = (Vector3::new(1.0f64, 4.0f64, 8.0f64), 9.0f64); // (1, 4, 8, 9) Pythagorean quadruple 239 | 240 | assert_eq!(a.magnitude2(), a_res * a_res); 241 | assert_eq!(b.magnitude2(), b_res * b_res); 242 | 243 | assert_eq!(a.magnitude(), a_res); 244 | assert_eq!(b.magnitude(), b_res); 245 | } 246 | 247 | #[test] 248 | fn test_vector4() { 249 | let (a, a_res) = (Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64), 11.0f64); // (1, 2, 4, 10, 11) Pythagorean quintuple 250 | let (b, b_res) = (Vector4::new(1.0f64, 2.0f64, 8.0f64, 10.0f64), 13.0f64); // (1, 2, 8, 10, 13) Pythagorean quintuple 251 | 252 | assert_eq!(a.magnitude2(), a_res * a_res); 253 | assert_eq!(b.magnitude2(), b_res * b_res); 254 | 255 | assert_eq!(a.magnitude(), a_res); 256 | assert_eq!(b.magnitude(), b_res); 257 | } 258 | } 259 | 260 | #[test] 261 | fn test_angle() { 262 | assert_ulps_eq!( 263 | Vector2::new(1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)), 264 | &Rad(f64::consts::FRAC_PI_2) 265 | ); 266 | assert_ulps_eq!( 267 | Vector2::new(10.0f64, 0.0f64).angle(Vector2::new(0.0f64, 5.0f64)), 268 | &Rad(f64::consts::FRAC_PI_2) 269 | ); 270 | assert_ulps_eq!( 271 | Vector2::new(-1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)), 272 | &-Rad(f64::consts::FRAC_PI_2) 273 | ); 274 | 275 | assert_ulps_eq!( 276 | Vector3::new(1.0f64, 0.0f64, 1.0f64).angle(Vector3::new(1.0f64, 1.0f64, 0.0f64)), 277 | &Rad(f64::consts::FRAC_PI_3) 278 | ); 279 | assert_ulps_eq!( 280 | Vector3::new(10.0f64, 0.0f64, 10.0f64).angle(Vector3::new(5.0f64, 5.0f64, 0.0f64)), 281 | &Rad(f64::consts::FRAC_PI_3) 282 | ); 283 | assert_ulps_eq!( 284 | Vector3::new(-1.0f64, 0.0f64, -1.0f64).angle(Vector3::new(1.0f64, -1.0f64, 0.0f64)), 285 | &Rad(2.0f64 * f64::consts::FRAC_PI_3) 286 | ); 287 | 288 | assert_ulps_eq!( 289 | Vector4::new(1.0f64, 0.0f64, 1.0f64, 0.0f64) 290 | .angle(Vector4::new(0.0f64, 1.0f64, 0.0f64, 1.0f64)), 291 | &Rad(f64::consts::FRAC_PI_2) 292 | ); 293 | assert_ulps_eq!( 294 | Vector4::new(10.0f64, 0.0f64, 10.0f64, 0.0f64) 295 | .angle(Vector4::new(0.0f64, 5.0f64, 0.0f64, 5.0f64)), 296 | &Rad(f64::consts::FRAC_PI_2) 297 | ); 298 | assert_ulps_eq!( 299 | Vector4::new(-1.0f64, 0.0f64, -1.0f64, 0.0f64) 300 | .angle(Vector4::new(0.0f64, 1.0f64, 0.0f64, 1.0f64)), 301 | &Rad(f64::consts::FRAC_PI_2) 302 | ); 303 | } 304 | 305 | #[test] 306 | fn test_normalize() { 307 | // TODO: test normalize_to, normalize_sel.0, and normalize_self_to 308 | assert_ulps_eq!( 309 | Vector2::new(3.0f64, 4.0f64).normalize(), 310 | &Vector2::new(3.0 / 5.0, 4.0 / 5.0) 311 | ); 312 | assert_ulps_eq!( 313 | Vector3::new(2.0f64, 3.0f64, 6.0f64).normalize(), 314 | &Vector3::new(2.0 / 7.0, 3.0 / 7.0, 6.0 / 7.0) 315 | ); 316 | assert_ulps_eq!( 317 | Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64).normalize(), 318 | &Vector4::new(1.0 / 11.0, 2.0 / 11.0, 4.0 / 11.0, 10.0 / 11.0) 319 | ); 320 | } 321 | 322 | #[test] 323 | fn test_project_on() { 324 | assert_ulps_eq!( 325 | Vector2::new(-1.0f64, 5.0).project_on(Vector2::new(2.0, 4.0)), 326 | &Vector2::new(9.0 / 5.0, 18.0 / 5.0) 327 | ); 328 | assert_ulps_eq!( 329 | Vector3::new(5.0f64, 6.0, 7.0).project_on(Vector3::new(1.0, 1.0, 1.0)), 330 | &Vector3::new(6.0, 6.0, 6.0) 331 | ); 332 | assert_ulps_eq!( 333 | Vector4::new(0.0f64, -5.0, 5.0, 5.0).project_on(Vector4::new(0.0, 1.0, 0.0, 0.5)), 334 | &Vector4::new(0.0, -2.0, 0.0, -1.0) 335 | ); 336 | } 337 | 338 | #[test] 339 | fn test_cast() { 340 | assert_ulps_eq!( 341 | Vector2::new(0.9f64, 1.5).cast().unwrap(), 342 | Vector2::new(0.9f32, 1.5) 343 | ); 344 | assert_ulps_eq!( 345 | Vector3::new(1.0f64, 2.4, -3.13).cast().unwrap(), 346 | Vector3::new(1.0f32, 2.4, -3.13) 347 | ); 348 | assert_ulps_eq!( 349 | Vector4::new(13.5f64, -4.6, -8.3, 2.41).cast().unwrap(), 350 | Vector4::new(13.5f32, -4.6, -8.3, 2.41) 351 | ); 352 | } 353 | -------------------------------------------------------------------------------- /src/projection.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use num_traits::cast; 17 | use num_traits::Zero; 18 | 19 | use structure::Angle; 20 | 21 | use angle::Rad; 22 | use matrix::Matrix4; 23 | use num::BaseFloat; 24 | 25 | /// Create a perspective projection matrix. 26 | /// 27 | /// This is the equivalent to the [`gluPerspective`] function. 28 | /// 29 | /// [`gluPerspective`]: https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml 30 | pub fn perspective>>( 31 | fovy: A, 32 | aspect: S, 33 | near: S, 34 | far: S, 35 | ) -> Matrix4 { 36 | PerspectiveFov { 37 | fovy: fovy.into(), 38 | aspect, 39 | near, 40 | far, 41 | } 42 | .into() 43 | } 44 | 45 | /// Create a perspective matrix from a view frustum. 46 | /// 47 | /// This is the equivalent of the now deprecated [`glFrustum`] function. 48 | /// 49 | /// [`glFrustum`]: http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml 50 | pub fn frustum(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4 { 51 | Perspective { 52 | left, 53 | right, 54 | bottom, 55 | top, 56 | near, 57 | far, 58 | } 59 | .into() 60 | } 61 | 62 | /// Create an orthographic projection matrix. 63 | /// 64 | /// This is the equivalent of the now deprecated [`glOrtho`] function. 65 | /// 66 | /// [`glOrtho`]: http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml 67 | pub fn ortho(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4 { 68 | Ortho { 69 | left, 70 | right, 71 | bottom, 72 | top, 73 | near, 74 | far, 75 | } 76 | .into() 77 | } 78 | 79 | /// Create a planar projection matrix, which can be either perspective or orthographic. 80 | /// 81 | /// The projection frustum is always `height` units high at the origin along the view direction, 82 | /// making the focal point located at `(0.0, 0.0, cot(fovy / 2.0)) * height / 2.0`. Unlike 83 | /// a standard perspective projection, this allows `fovy` to be zero or negative. 84 | pub fn planar>>( 85 | fovy: A, 86 | aspect: S, 87 | height: S, 88 | near: S, 89 | far: S, 90 | ) -> Matrix4 { 91 | PlanarFov { 92 | fovy: fovy.into(), 93 | aspect, 94 | height, 95 | near, 96 | far, 97 | } 98 | .into() 99 | } 100 | 101 | /// A perspective projection based on a vertical field-of-view angle. 102 | #[derive(Copy, Clone, Debug, PartialEq)] 103 | #[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] 104 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 105 | pub struct PerspectiveFov { 106 | pub fovy: Rad, 107 | pub aspect: S, 108 | pub near: S, 109 | pub far: S, 110 | } 111 | 112 | impl PerspectiveFov { 113 | pub fn to_perspective(&self) -> Perspective { 114 | let two: S = cast(2).unwrap(); 115 | let angle = self.fovy / two; 116 | let ymax = self.near * Rad::tan(angle); 117 | let xmax = ymax * self.aspect; 118 | 119 | Perspective { 120 | left: -xmax, 121 | right: xmax, 122 | bottom: -ymax, 123 | top: ymax, 124 | near: self.near, 125 | far: self.far, 126 | } 127 | } 128 | } 129 | 130 | impl From> for Matrix4 { 131 | fn from(persp: PerspectiveFov) -> Matrix4 { 132 | assert!( 133 | persp.fovy > Rad::zero(), 134 | "The vertical field of view cannot be below zero, found: {:?}", 135 | persp.fovy 136 | ); 137 | assert!( 138 | persp.fovy < Rad::turn_div_2(), 139 | "The vertical field of view cannot be greater than a half turn, found: {:?}", 140 | persp.fovy 141 | ); 142 | 143 | assert!( 144 | abs_diff_ne!(persp.aspect.abs(), S::zero()), 145 | "The absolute aspect ratio cannot be zero, found: {:?}", 146 | persp.aspect.abs() 147 | ); 148 | assert!( 149 | persp.near > S::zero(), 150 | "The near plane distance cannot be below zero, found: {:?}", 151 | persp.near 152 | ); 153 | assert!( 154 | persp.far > S::zero(), 155 | "The far plane distance cannot be below zero, found: {:?}", 156 | persp.far 157 | ); 158 | assert!( 159 | abs_diff_ne!(persp.far, persp.near), 160 | "The far plane and near plane are too close, found: far: {:?}, near: {:?}", 161 | persp.far, 162 | persp.near 163 | ); 164 | 165 | let two: S = cast(2).unwrap(); 166 | let f = Rad::cot(persp.fovy / two); 167 | 168 | let c0r0 = f / persp.aspect; 169 | let c0r1 = S::zero(); 170 | let c0r2 = S::zero(); 171 | let c0r3 = S::zero(); 172 | 173 | let c1r0 = S::zero(); 174 | let c1r1 = f; 175 | let c1r2 = S::zero(); 176 | let c1r3 = S::zero(); 177 | 178 | let c2r0 = S::zero(); 179 | let c2r1 = S::zero(); 180 | let c2r2 = (persp.far + persp.near) / (persp.near - persp.far); 181 | let c2r3 = -S::one(); 182 | 183 | let c3r0 = S::zero(); 184 | let c3r1 = S::zero(); 185 | let c3r2 = (two * persp.far * persp.near) / (persp.near - persp.far); 186 | let c3r3 = S::zero(); 187 | 188 | #[cfg_attr(rustfmt, rustfmt_skip)] 189 | Matrix4::new( 190 | c0r0, c0r1, c0r2, c0r3, 191 | c1r0, c1r1, c1r2, c1r3, 192 | c2r0, c2r1, c2r2, c2r3, 193 | c3r0, c3r1, c3r2, c3r3, 194 | ) 195 | } 196 | } 197 | 198 | /// A perspective projection with arbitrary left/right/bottom/top distances 199 | #[derive(Copy, Clone, Debug, PartialEq)] 200 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 201 | pub struct Perspective { 202 | pub left: S, 203 | pub right: S, 204 | pub bottom: S, 205 | pub top: S, 206 | pub near: S, 207 | pub far: S, 208 | } 209 | 210 | impl From> for Matrix4 { 211 | fn from(persp: Perspective) -> Matrix4 { 212 | assert!( 213 | persp.left <= persp.right, 214 | "`left` cannot be greater than `right`, found: left: {:?} right: {:?}", 215 | persp.left, 216 | persp.right 217 | ); 218 | assert!( 219 | persp.bottom <= persp.top, 220 | "`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}", 221 | persp.bottom, 222 | persp.top 223 | ); 224 | assert!( 225 | persp.near <= persp.far, 226 | "`near` cannot be greater than `far`, found: near: {:?} far: {:?}", 227 | persp.near, 228 | persp.far 229 | ); 230 | 231 | let two: S = cast(2i8).unwrap(); 232 | 233 | let c0r0 = (two * persp.near) / (persp.right - persp.left); 234 | let c0r1 = S::zero(); 235 | let c0r2 = S::zero(); 236 | let c0r3 = S::zero(); 237 | 238 | let c1r0 = S::zero(); 239 | let c1r1 = (two * persp.near) / (persp.top - persp.bottom); 240 | let c1r2 = S::zero(); 241 | let c1r3 = S::zero(); 242 | 243 | let c2r0 = (persp.right + persp.left) / (persp.right - persp.left); 244 | let c2r1 = (persp.top + persp.bottom) / (persp.top - persp.bottom); 245 | let c2r2 = -(persp.far + persp.near) / (persp.far - persp.near); 246 | let c2r3 = -S::one(); 247 | 248 | let c3r0 = S::zero(); 249 | let c3r1 = S::zero(); 250 | let c3r2 = -(two * persp.far * persp.near) / (persp.far - persp.near); 251 | let c3r3 = S::zero(); 252 | 253 | #[cfg_attr(rustfmt, rustfmt_skip)] 254 | Matrix4::new( 255 | c0r0, c0r1, c0r2, c0r3, 256 | c1r0, c1r1, c1r2, c1r3, 257 | c2r0, c2r1, c2r2, c2r3, 258 | c3r0, c3r1, c3r2, c3r3, 259 | ) 260 | } 261 | } 262 | 263 | /// An orthographic projection with arbitrary left/right/bottom/top distances 264 | #[derive(Copy, Clone, Debug, PartialEq)] 265 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 266 | pub struct Ortho { 267 | pub left: S, 268 | pub right: S, 269 | pub bottom: S, 270 | pub top: S, 271 | pub near: S, 272 | pub far: S, 273 | } 274 | 275 | impl From> for Matrix4 { 276 | fn from(ortho: Ortho) -> Matrix4 { 277 | let two: S = cast(2).unwrap(); 278 | 279 | let c0r0 = two / (ortho.right - ortho.left); 280 | let c0r1 = S::zero(); 281 | let c0r2 = S::zero(); 282 | let c0r3 = S::zero(); 283 | 284 | let c1r0 = S::zero(); 285 | let c1r1 = two / (ortho.top - ortho.bottom); 286 | let c1r2 = S::zero(); 287 | let c1r3 = S::zero(); 288 | 289 | let c2r0 = S::zero(); 290 | let c2r1 = S::zero(); 291 | let c2r2 = -two / (ortho.far - ortho.near); 292 | let c2r3 = S::zero(); 293 | 294 | let c3r0 = -(ortho.right + ortho.left) / (ortho.right - ortho.left); 295 | let c3r1 = -(ortho.top + ortho.bottom) / (ortho.top - ortho.bottom); 296 | let c3r2 = -(ortho.far + ortho.near) / (ortho.far - ortho.near); 297 | let c3r3 = S::one(); 298 | 299 | #[cfg_attr(rustfmt, rustfmt_skip)] 300 | Matrix4::new( 301 | c0r0, c0r1, c0r2, c0r3, 302 | c1r0, c1r1, c1r2, c1r3, 303 | c2r0, c2r1, c2r2, c2r3, 304 | c3r0, c3r1, c3r2, c3r3, 305 | ) 306 | } 307 | } 308 | 309 | /// A planar projection based on a vertical field-of-view angle. 310 | #[derive(Copy, Clone, Debug, PartialEq)] 311 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 312 | pub struct PlanarFov { 313 | pub fovy: Rad, 314 | pub aspect: S, 315 | pub height: S, 316 | pub near: S, 317 | pub far: S, 318 | } 319 | 320 | impl From> for Matrix4 { 321 | fn from(persp: PlanarFov) -> Matrix4 { 322 | assert!( 323 | persp.fovy > -Rad::turn_div_2(), 324 | "The vertical field of view cannot be less than a negative half turn, found: {:?}", 325 | persp.fovy 326 | ); 327 | assert!( 328 | persp.fovy < Rad::turn_div_2(), 329 | "The vertical field of view cannot be greater than a half turn, found: {:?}", 330 | persp.fovy 331 | ); 332 | assert! { 333 | persp.height >= S::zero(), 334 | "The projection plane height cannot be negative, found: {:?}", 335 | persp.height 336 | } 337 | 338 | let two: S = cast(2).unwrap(); 339 | let inv_f = Rad::tan(persp.fovy / two) * two / persp.height; 340 | 341 | let focal_point = -inv_f.recip(); 342 | 343 | assert!( 344 | abs_diff_ne!(persp.aspect.abs(), S::zero()), 345 | "The absolute aspect ratio cannot be zero, found: {:?}", 346 | persp.aspect.abs() 347 | ); 348 | assert!( 349 | abs_diff_ne!(persp.far, persp.near), 350 | "The far plane and near plane are too close, found: far: {:?}, near: {:?}", 351 | persp.far, 352 | persp.near 353 | ); 354 | assert!( 355 | focal_point < S::min(persp.far, persp.near) || focal_point > S::max(persp.far, persp.near), 356 | "The focal point cannot be between the far and near planes, found: focal: {:?}, far: {:?}, near: {:?}", 357 | focal_point, 358 | persp.far, 359 | persp.near, 360 | ); 361 | 362 | let c0r0 = two / (persp.aspect * persp.height); 363 | let c0r1 = S::zero(); 364 | let c0r2 = S::zero(); 365 | let c0r3 = S::zero(); 366 | 367 | let c1r0 = S::zero(); 368 | let c1r1 = two / persp.height; 369 | let c1r2 = S::zero(); 370 | let c1r3 = S::zero(); 371 | 372 | let c2r0 = S::zero(); 373 | let c2r1 = S::zero(); 374 | let c2r2 = ((persp.far + persp.near) * inv_f + two) / (persp.near - persp.far); 375 | let c2r3 = -inv_f; 376 | 377 | let c3r0 = S::zero(); 378 | let c3r1 = S::zero(); 379 | let c3r2 = (two * persp.far * persp.near * inv_f + (persp.far + persp.near)) 380 | / (persp.near - persp.far); 381 | let c3r3 = S::one(); 382 | 383 | #[cfg_attr(rustfmt, rustfmt_skip)] 384 | Matrix4::new( 385 | c0r0, c0r1, c0r2, c0r3, 386 | c1r0, c1r1, c1r2, c1r3, 387 | c2r0, c2r1, c2r2, c2r3, 388 | c3r0, c3r1, c3r2, c3r3, 389 | ) 390 | } 391 | } 392 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Utility macros for code generation 17 | 18 | #![macro_use] 19 | 20 | #[cfg(feature = "simd")] 21 | macro_rules! default_fn { 22 | { $($tt:tt)* } => { default fn $( $tt )* }; 23 | } 24 | 25 | #[cfg(not(feature = "simd"))] 26 | macro_rules! default_fn { 27 | { $($tt:tt)* } => { 28 | #[inline] 29 | fn $( $tt )* 30 | }; 31 | } 32 | 33 | /// Generates a binary operator implementation for the permutations of by-ref and by-val 34 | macro_rules! impl_operator { 35 | // When it is an unary operator 36 | (<$S:ident: $Constraint:ident> $Op:ident for $Lhs:ty { 37 | fn $op:ident($x:ident) -> $Output:ty { $body:expr } 38 | }) => { 39 | impl<$S: $Constraint> $Op for $Lhs { 40 | type Output = $Output; 41 | default_fn!($op(self) -> $Output { 42 | let $x = self; $body 43 | }); 44 | } 45 | 46 | impl<'a, $S: $Constraint> $Op for &'a $Lhs { 47 | type Output = $Output; 48 | default_fn!($op(self) -> $Output { 49 | let $x = self; $body 50 | }); 51 | } 52 | }; 53 | // When the right operand is a scalar 54 | (<$S:ident: $Constraint:ident> $Op:ident<$Rhs:ident> for $Lhs:ty { 55 | fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } 56 | }) => { 57 | impl<$S: $Constraint> $Op<$Rhs> for $Lhs { 58 | type Output = $Output; 59 | default_fn!($op(self, other: $Rhs) -> $Output { 60 | let ($lhs, $rhs) = (self, other); $body 61 | }); 62 | } 63 | 64 | impl<'a, $S: $Constraint> $Op<$Rhs> for &'a $Lhs { 65 | type Output = $Output; 66 | default_fn!($op(self, other: $Rhs) -> $Output { 67 | let ($lhs, $rhs) = (self, other); $body 68 | }); 69 | } 70 | }; 71 | // When the right operand is a compound type 72 | (<$S:ident: $Constraint:ident> $Op:ident<$Rhs:ty> for $Lhs:ty { 73 | fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } 74 | }) => { 75 | impl<$S: $Constraint> $Op<$Rhs> for $Lhs { 76 | type Output = $Output; 77 | default_fn!( $op(self, other: $Rhs) -> $Output { 78 | let ($lhs, $rhs) = (self, other); $body 79 | }); 80 | } 81 | 82 | impl<'a, $S: $Constraint> $Op<&'a $Rhs> for $Lhs { 83 | type Output = $Output; 84 | default_fn!( $op(self, other: &'a $Rhs) -> $Output { 85 | let ($lhs, $rhs) = (self, other); $body 86 | }); 87 | } 88 | 89 | impl<'a, $S: $Constraint> $Op<$Rhs> for &'a $Lhs { 90 | type Output = $Output; 91 | default_fn!( $op(self, other: $Rhs) -> $Output { 92 | let ($lhs, $rhs) = (self, other); $body 93 | }); 94 | } 95 | 96 | impl<'a, 'b, $S: $Constraint> $Op<&'a $Rhs> for &'b $Lhs { 97 | type Output = $Output; 98 | default_fn!( $op(self, other: &'a $Rhs) -> $Output { 99 | let ($lhs, $rhs) = (self, other); $body 100 | }); 101 | } 102 | }; 103 | // When the left operand is a scalar 104 | ($Op:ident<$Rhs:ident<$S:ident>> for $Lhs:ty { 105 | fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } 106 | }) => { 107 | impl $Op<$Rhs<$S>> for $Lhs { 108 | type Output = $Output; 109 | default_fn!( $op(self, other: $Rhs<$S>) -> $Output { 110 | let ($lhs, $rhs) = (self, other); $body 111 | }); 112 | } 113 | 114 | impl<'a> $Op<&'a $Rhs<$S>> for $Lhs { 115 | type Output = $Output; 116 | default_fn!( $op(self, other: &'a $Rhs<$S>) -> $Output { 117 | let ($lhs, $rhs) = (self, other); $body 118 | }); 119 | } 120 | }; 121 | } 122 | 123 | macro_rules! impl_assignment_operator { 124 | (<$S:ident: $Constraint:ident> $Op:ident<$Rhs:ty> for $Lhs:ty { 125 | fn $op:ident(&mut $lhs:ident, $rhs:ident) $body:block 126 | }) => { 127 | impl<$S: $Constraint + $Op<$S>> $Op<$Rhs> for $Lhs { 128 | default_fn!( $op(&mut $lhs, $rhs: $Rhs) $body ); 129 | } 130 | }; 131 | } 132 | 133 | macro_rules! fold_array { 134 | ($method:ident, $x:expr) => ($x); 135 | ($method:ident, $x:expr, $($y:expr),+) => ($x.$method(fold_array!($method, $($y),+))) 136 | } 137 | 138 | /// Generate array conversion implementations for a compound array type 139 | macro_rules! impl_fixed_array_conversions { 140 | ($ArrayN:ident <$S:ident> { $($field:ident : $index:expr),+ }, $n:expr) => { 141 | impl<$S> From<$ArrayN<$S>> for [$S; $n] { 142 | #[inline] 143 | fn from(v: $ArrayN<$S>) -> Self { 144 | match v { $ArrayN { $($field),+ } => [$($field),+] } 145 | } 146 | } 147 | 148 | impl<$S> AsRef<[$S; $n]> for $ArrayN<$S> { 149 | #[inline] 150 | fn as_ref(&self) -> &[$S; $n] { 151 | unsafe { mem::transmute(self) } 152 | } 153 | } 154 | 155 | impl<$S> AsMut<[$S; $n]> for $ArrayN<$S> { 156 | #[inline] 157 | fn as_mut(&mut self) -> &mut [$S; $n] { 158 | unsafe { mem::transmute(self) } 159 | } 160 | } 161 | 162 | impl<$S: Clone> From<[$S; $n]> for $ArrayN<$S> { 163 | #[inline] 164 | fn from(v: [$S; $n]) -> $ArrayN<$S> { 165 | // We need to use a clone here because we can't pattern match on arrays yet 166 | $ArrayN { $($field: v[$index].clone()),+ } 167 | } 168 | } 169 | 170 | impl<'a, $S> From<&'a [$S; $n]> for &'a $ArrayN<$S> { 171 | #[inline] 172 | fn from(v: &'a [$S; $n]) -> &'a $ArrayN<$S> { 173 | unsafe { mem::transmute(v) } 174 | } 175 | } 176 | 177 | impl<'a, $S> From<&'a mut [$S; $n]> for &'a mut $ArrayN<$S> { 178 | #[inline] 179 | fn from(v: &'a mut [$S; $n]) -> &'a mut $ArrayN<$S> { 180 | unsafe { mem::transmute(v) } 181 | } 182 | } 183 | } 184 | } 185 | 186 | /// Generate homogeneous tuple conversion implementations for a compound array type 187 | macro_rules! impl_tuple_conversions { 188 | ($ArrayN:ident <$S:ident> { $($field:ident),+ }, $Tuple:ty) => { 189 | impl<$S> From<$ArrayN<$S>> for $Tuple { 190 | #[inline] 191 | fn from(v: $ArrayN<$S>) -> Self { 192 | match v { $ArrayN { $($field),+ } => ($($field),+,) } 193 | } 194 | } 195 | 196 | impl<$S> AsRef<$Tuple> for $ArrayN<$S> { 197 | #[inline] 198 | fn as_ref(&self) -> &$Tuple { 199 | unsafe { mem::transmute(self) } 200 | } 201 | } 202 | 203 | impl<$S> AsMut<$Tuple> for $ArrayN<$S> { 204 | #[inline] 205 | fn as_mut(&mut self) -> &mut $Tuple { 206 | unsafe { mem::transmute(self) } 207 | } 208 | } 209 | 210 | impl<$S> From<$Tuple> for $ArrayN<$S> { 211 | #[inline] 212 | fn from(v: $Tuple) -> $ArrayN<$S> { 213 | match v { ($($field),+,) => $ArrayN { $($field),+ } } 214 | } 215 | } 216 | 217 | impl<'a, $S> From<&'a $Tuple> for &'a $ArrayN<$S> { 218 | #[inline] 219 | fn from(v: &'a $Tuple) -> &'a $ArrayN<$S> { 220 | unsafe { mem::transmute(v) } 221 | } 222 | } 223 | 224 | impl<'a, $S> From<&'a mut $Tuple> for &'a mut $ArrayN<$S> { 225 | #[inline] 226 | fn from(v: &'a mut $Tuple) -> &'a mut $ArrayN<$S> { 227 | unsafe { mem::transmute(v) } 228 | } 229 | } 230 | } 231 | } 232 | 233 | /// Generates index operators for a compound type 234 | macro_rules! impl_index_operators { 235 | ($VectorN:ident<$S:ident>, $n:expr, $Output:ty, $I:ty) => { 236 | impl<$S> Index<$I> for $VectorN<$S> { 237 | type Output = $Output; 238 | 239 | #[inline] 240 | fn index<'a>(&'a self, i: $I) -> &'a $Output { 241 | let v: &[$S; $n] = self.as_ref(); 242 | &v[i] 243 | } 244 | } 245 | 246 | impl<$S> IndexMut<$I> for $VectorN<$S> { 247 | #[inline] 248 | fn index_mut<'a>(&'a mut self, i: $I) -> &'a mut $Output { 249 | let v: &mut [$S; $n] = self.as_mut(); 250 | &mut v[i] 251 | } 252 | } 253 | }; 254 | } 255 | 256 | /// Generates a binary operator implementation for the permutations of by-ref and by-val, for simd 257 | #[cfg(feature = "simd")] 258 | macro_rules! impl_operator_simd { 259 | // When it is an unary operator 260 | ([$Simd:ident]; $Op:ident for $Lhs:ty { 261 | fn $op:ident($x:ident) -> $Output:ty { $body:expr } 262 | }) => { 263 | impl $Op for $Lhs { 264 | #[inline] 265 | fn $op(self) -> $Output { 266 | let $x: $Simd = self.into(); 267 | $body 268 | } 269 | } 270 | }; 271 | // When the right operand is a scalar 272 | (@rs [$Simd:ident]; $Op:ident<$Rhs:ty> for $Lhs:ty { 273 | fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } 274 | }) => { 275 | impl $Op<$Rhs> for $Lhs { 276 | #[inline] 277 | fn $op(self, other: $Rhs) -> $Output { 278 | let ($lhs, $rhs): ($Simd, $Simd) = (self.into(), $Simd::splat(other)); 279 | $body 280 | } 281 | } 282 | 283 | impl<'a> $Op<$Rhs> for &'a $Lhs { 284 | #[inline] 285 | fn $op(self, other: $Rhs) -> $Output { 286 | let ($lhs, $rhs): ($Simd, $Simd) = ((*self).into(), $Simd::splat(other)); 287 | $body 288 | } 289 | } 290 | }; 291 | 292 | // When the right operand is a compound type 293 | ([$Simd:ident]; $Op:ident<$Rhs:ty> for $Lhs:ty { 294 | fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } 295 | }) => { 296 | impl $Op<$Rhs> for $Lhs { 297 | #[inline] 298 | fn $op(self, other: $Rhs) -> $Output { 299 | let ($lhs, $rhs): ($Simd, $Simd) = (self.into(), other.into()); 300 | $body 301 | } 302 | } 303 | 304 | impl<'a> $Op<&'a $Rhs> for $Lhs { 305 | #[inline] 306 | fn $op(self, other: &'a $Rhs) -> $Output { 307 | let ($lhs, $rhs): ($Simd, $Simd) = (self.into(), (*other).into()); 308 | $body 309 | } 310 | } 311 | 312 | impl<'a> $Op<$Rhs> for &'a $Lhs { 313 | #[inline] 314 | fn $op(self, other: $Rhs) -> $Output { 315 | let ($lhs, $rhs): ($Simd, $Simd) = ((*self).into(), other.into()); 316 | $body 317 | } 318 | } 319 | 320 | impl<'a, 'b> $Op<&'a $Rhs> for &'b $Lhs { 321 | #[inline] 322 | fn $op(self, other: &'a $Rhs) -> $Output { 323 | let ($lhs, $rhs): ($Simd, $Simd) = ((*self).into(), (*other).into()); 324 | $body 325 | } 326 | } 327 | }; 328 | 329 | // When the left operand is a scalar 330 | (@ls [$Simd:ident]; $Op:ident<$Rhs:ty> for $Lhs:ident { 331 | fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr } 332 | }) => { 333 | impl $Op<$Rhs> for $Lhs { 334 | #[inline] 335 | fn $op(self, other: $Rhs) -> $Output { 336 | let ($lhs, $rhs): ($Simd, $Simd) = ($Simd::splat(self), other.into()); 337 | $body 338 | } 339 | } 340 | 341 | impl<'a> $Op<&'a $Rhs> for $Lhs { 342 | #[inline] 343 | fn $op(self, other: &'a $Rhs) -> $Output { 344 | let ($lhs, $rhs): ($Simd, $Simd) = ($Simd::splat(self), (*other).into()); 345 | $body 346 | } 347 | } 348 | }; 349 | } 350 | 351 | /// Generate `mint` types conversion implementations 352 | #[cfg(feature = "mint")] 353 | macro_rules! impl_mint_conversions { 354 | ($ArrayN:ident { $($field:ident),+ }, $Mint:ident) => { 355 | impl From<$ArrayN> for mint::$Mint { 356 | #[inline] 357 | fn from(v: $ArrayN) -> Self { 358 | mint::$Mint::from([$(v.$field),+]) 359 | } 360 | } 361 | 362 | impl From> for $ArrayN { 363 | #[inline] 364 | fn from(v: mint::$Mint) -> Self { 365 | $ArrayN { $( $field: v.$field, )+ } 366 | } 367 | } 368 | 369 | impl mint::IntoMint for $ArrayN { 370 | type MintType = mint::$Mint; 371 | } 372 | } 373 | } 374 | 375 | /// Generate implementation required to cast using `bytemuck` 376 | #[cfg(feature = "bytemuck")] 377 | macro_rules! impl_bytemuck_cast { 378 | ($ArrayN:ident) => { 379 | unsafe impl bytemuck::Pod for $ArrayN {} 380 | unsafe impl bytemuck::Zeroable for $ArrayN {} 381 | }; 382 | } 383 | 384 | include!(concat!(env!("OUT_DIR"), "/swizzle_operator_macro.rs")); 385 | -------------------------------------------------------------------------------- /tests/quaternion.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | extern crate approx; 17 | extern crate cgmath; 18 | 19 | macro_rules! impl_test_mul { 20 | ($s:expr, $v:expr) => { 21 | // point * scalar ops 22 | assert_eq!($v * $s, Quaternion::from_sv($v.s * $s, $v.v * $s)); 23 | assert_eq!($s * $v, Quaternion::from_sv($s * $v.s, $s * $v.v)); 24 | assert_eq!(&$v * $s, $v * $s); 25 | assert_eq!($s * &$v, $s * $v); 26 | // commutativity 27 | assert_eq!($v * $s, $s * $v); 28 | }; 29 | } 30 | 31 | macro_rules! impl_test_div { 32 | ($s:expr, $v:expr) => { 33 | // point / scalar ops 34 | assert_eq!($v / $s, Quaternion::from_sv($v.s / $s, $v.v / $s)); 35 | assert_eq!($s / $v, Quaternion::from_sv($s / $v.s, $s / $v.v)); 36 | assert_eq!(&$v / $s, $v / $s); 37 | assert_eq!($s / &$v, $s / $v); 38 | }; 39 | } 40 | 41 | mod operators { 42 | use cgmath::*; 43 | 44 | #[test] 45 | fn test_mul() { 46 | impl_test_mul!( 47 | 2.0f32, 48 | Quaternion::from(Euler { 49 | x: Rad(1f32), 50 | y: Rad(1f32), 51 | z: Rad(1f32), 52 | }) 53 | ); 54 | } 55 | 56 | #[test] 57 | fn test_div() { 58 | impl_test_div!( 59 | 2.0f32, 60 | Quaternion::from(Euler { 61 | x: Rad(1f32), 62 | y: Rad(1f32), 63 | z: Rad(1f32), 64 | }) 65 | ); 66 | } 67 | 68 | #[test] 69 | fn test_iter_sum() { 70 | let q1 = Quaternion::from(Euler { 71 | x: Rad(2f32), 72 | y: Rad(1f32), 73 | z: Rad(1f32), 74 | }); 75 | let q2 = Quaternion::from(Euler { 76 | x: Rad(1f32), 77 | y: Rad(2f32), 78 | z: Rad(1f32), 79 | }); 80 | let q3 = Quaternion::from(Euler { 81 | x: Rad(1f32), 82 | y: Rad(1f32), 83 | z: Rad(2f32), 84 | }); 85 | 86 | assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().sum()); 87 | assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().cloned().sum()); 88 | } 89 | 90 | #[test] 91 | fn test_iter_product() { 92 | let q1 = Quaternion::from(Euler { 93 | x: Rad(2f32), 94 | y: Rad(1f32), 95 | z: Rad(1f32), 96 | }); 97 | let q2 = Quaternion::from(Euler { 98 | x: Rad(1f32), 99 | y: Rad(2f32), 100 | z: Rad(1f32), 101 | }); 102 | let q3 = Quaternion::from(Euler { 103 | x: Rad(1f32), 104 | y: Rad(1f32), 105 | z: Rad(2f32), 106 | }); 107 | 108 | assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().product()); 109 | assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().cloned().product()); 110 | } 111 | } 112 | 113 | mod to_from_euler { 114 | use std::f32; 115 | 116 | use cgmath::*; 117 | 118 | fn check_euler(rotation: Euler>) { 119 | assert_relative_eq!( 120 | Euler::from(Quaternion::from(rotation)), 121 | rotation, 122 | epsilon = 0.001 123 | ); 124 | } 125 | 126 | const HPI: f32 = f32::consts::FRAC_PI_2; 127 | 128 | #[test] 129 | fn test_zero() { 130 | check_euler(Euler { 131 | x: Rad(0f32), 132 | y: Rad(0f32), 133 | z: Rad(0f32), 134 | }); 135 | } 136 | #[test] 137 | fn test_yaw_pos_1() { 138 | check_euler(Euler { 139 | x: Rad(0f32), 140 | y: Rad(1f32), 141 | z: Rad(0f32), 142 | }); 143 | } 144 | #[test] 145 | fn test_yaw_neg_1() { 146 | check_euler(Euler { 147 | x: Rad(0f32), 148 | y: Rad(-1f32), 149 | z: Rad(0f32), 150 | }); 151 | } 152 | #[test] 153 | fn test_pitch_pos_1() { 154 | check_euler(Euler { 155 | x: Rad(1f32), 156 | y: Rad(0f32), 157 | z: Rad(0f32), 158 | }); 159 | } 160 | #[test] 161 | fn test_pitch_neg_1() { 162 | check_euler(Euler { 163 | x: Rad(-1f32), 164 | y: Rad(0f32), 165 | z: Rad(0f32), 166 | }); 167 | } 168 | #[test] 169 | fn test_roll_pos_1() { 170 | check_euler(Euler { 171 | x: Rad(0f32), 172 | y: Rad(0f32), 173 | z: Rad(1f32), 174 | }); 175 | } 176 | #[test] 177 | fn test_roll_neg_1() { 178 | check_euler(Euler { 179 | x: Rad(0f32), 180 | y: Rad(0f32), 181 | z: Rad(-1f32), 182 | }); 183 | } 184 | #[test] 185 | fn test_pitch_yaw_roll_pos_1() { 186 | check_euler(Euler { 187 | x: Rad(1f32), 188 | y: Rad(1f32), 189 | z: Rad(1f32), 190 | }); 191 | } 192 | #[test] 193 | fn test_pitch_yaw_roll_neg_1() { 194 | check_euler(Euler { 195 | x: Rad(-1f32), 196 | y: Rad(-1f32), 197 | z: Rad(-1f32), 198 | }); 199 | } 200 | #[test] 201 | fn test_pitch_yaw_roll_pos_hp() { 202 | check_euler(Euler { 203 | x: Rad(0f32), 204 | y: Rad(HPI), 205 | z: Rad(1f32), 206 | }); 207 | } 208 | #[test] 209 | fn test_pitch_yaw_roll_neg_hp() { 210 | check_euler(Euler { 211 | x: Rad(0f32), 212 | y: Rad(-HPI), 213 | z: Rad(1f32), 214 | }); 215 | } 216 | } 217 | 218 | mod from { 219 | mod matrix3 { 220 | use cgmath::*; 221 | 222 | fn check_with_euler(x: Rad, y: Rad, z: Rad) { 223 | let matrix3 = Matrix3::from(Euler { x, y, z }); 224 | let quaternion = Quaternion::from(matrix3); 225 | let quaternion_matrix3 = Matrix3::from(quaternion); 226 | assert_ulps_eq!(matrix3, quaternion_matrix3); 227 | } 228 | 229 | // triggers: trace >= S::zero() 230 | #[test] 231 | fn test_positive_trace() { 232 | check_with_euler(Rad(0.0f32), Rad(0.0), Rad(0.0f32)); 233 | } 234 | 235 | // triggers: (mat[0][0] > mat[1][1]) && (mat[0][0] > mat[2][2]) 236 | #[test] 237 | fn test_xx_maximum() { 238 | check_with_euler(Rad(2.0f32), Rad(1.0), Rad(-1.2f32)); 239 | } 240 | 241 | // triggers: mat[1][1] > mat[2][2] 242 | #[test] 243 | fn test_yy_maximum() { 244 | check_with_euler(Rad(2.0f32), Rad(1.0), Rad(3.0f32)); 245 | } 246 | 247 | // base case 248 | #[test] 249 | fn test_zz_maximum() { 250 | check_with_euler(Rad(1.0f32), Rad(1.0), Rad(3.0f32)); 251 | } 252 | } 253 | } 254 | 255 | mod arc { 256 | use cgmath::*; 257 | 258 | #[inline] 259 | fn test(src: Vector3, dst: Vector3) { 260 | let q = Quaternion::from_arc(src, dst, None); 261 | let v = q.rotate_vector(src); 262 | assert_ulps_eq!(v.normalize(), dst.normalize()); 263 | } 264 | 265 | #[test] 266 | fn test_same() { 267 | let v = Vector3::unit_x(); 268 | let q = Quaternion::from_arc(v.clone(), v, None); 269 | assert_eq!(q, Quaternion::new(1.0, 0.0, 0.0, 0.0)); 270 | } 271 | 272 | #[test] 273 | fn test_opposite() { 274 | let v = Vector3::unit_x(); 275 | test(v, -v); 276 | } 277 | 278 | #[test] 279 | fn test_random() { 280 | test(vec3(1.0, 2.0, 3.0), vec3(-4.0, 5.0, -6.0)); 281 | } 282 | 283 | #[test] 284 | fn test_ortho() { 285 | let q: Quaternion = Quaternion::from_arc(Vector3::unit_x(), Vector3::unit_y(), None); 286 | let q2 = Quaternion::from_axis_angle(Vector3::unit_z(), Rad::turn_div_4()); 287 | assert_ulps_eq!(q, q2); 288 | } 289 | } 290 | 291 | mod rotate_from_euler { 292 | use cgmath::*; 293 | 294 | #[test] 295 | fn test_x() { 296 | let vec = vec3(0.0, 0.0, 1.0); 297 | 298 | let rot = Quaternion::from(Euler::new(Deg(90.0), Deg(0.0), Deg(0.0))); 299 | assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec); 300 | 301 | let rot = Quaternion::from(Euler::new(Deg(-90.0), Deg(0.0), Deg(0.0))); 302 | assert_ulps_eq!(vec3(0.0, 1.0, 0.0), rot * vec); 303 | } 304 | 305 | #[test] 306 | fn test_y() { 307 | let vec = vec3(0.0, 0.0, 1.0); 308 | 309 | let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(90.0), Deg(0.0))); 310 | assert_ulps_eq!(vec3(1.0, 0.0, 0.0), rot * vec); 311 | 312 | let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(-90.0), Deg(0.0))); 313 | assert_ulps_eq!(vec3(-1.0, 0.0, 0.0), rot * vec); 314 | } 315 | 316 | #[test] 317 | fn test_z() { 318 | let vec = vec3(1.0, 0.0, 0.0); 319 | 320 | let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(0.0), Deg(90.0))); 321 | assert_ulps_eq!(vec3(0.0, 1.0, 0.0), rot * vec); 322 | 323 | let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(0.0), Deg(-90.0))); 324 | assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec); 325 | } 326 | 327 | // tests that the Y rotation is done after the X 328 | #[test] 329 | fn test_x_then_y() { 330 | let vec = vec3(0.0, 1.0, 0.0); 331 | 332 | let rot = Quaternion::from(Euler::new(Deg(90.0), Deg(90.0), Deg(0.0))); 333 | assert_ulps_eq!(vec3(0.0f32, 0.0f32, 1.0f32), rot * vec); 334 | } 335 | 336 | // tests that the Z rotation is done after the Y 337 | #[test] 338 | fn test_y_then_z() { 339 | let vec = vec3(0.0f32, 0.0f32, 1.0f32); 340 | 341 | let rot = Quaternion::from(Euler::new(Deg(0.0), Deg(90.0), Deg(90.0))); 342 | assert_ulps_eq!(vec3(1.0, 0.0, 0.0), rot * vec); 343 | } 344 | } 345 | 346 | mod rotate_from_axis_angle { 347 | use cgmath::*; 348 | 349 | #[test] 350 | fn test_x() { 351 | let vec = vec3(0.0, 0.0, 1.0); 352 | 353 | let rot = Quaternion::from_angle_x(Deg(90.0)); 354 | assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec); 355 | } 356 | 357 | #[test] 358 | fn test_y() { 359 | let vec = vec3(0.0, 0.0, 1.0); 360 | 361 | let rot = Quaternion::from_angle_y(Deg(90.0)); 362 | assert_ulps_eq!(vec3(1.0, 0.0, 0.0), rot * vec); 363 | } 364 | 365 | #[test] 366 | fn test_z() { 367 | let vec = vec3(1.0, 0.0, 0.0); 368 | 369 | let rot = Quaternion::from_angle_z(Deg(90.0)); 370 | assert_ulps_eq!(vec3(0.0, 1.0, 0.0), rot * vec); 371 | } 372 | 373 | #[test] 374 | fn test_xy() { 375 | let vec = vec3(0.0, 0.0, 1.0); 376 | 377 | let rot = Quaternion::from_axis_angle(vec3(1.0, 1.0, 0.0).normalize(), Deg(90.0)); 378 | assert_ulps_eq!( 379 | vec3(2.0f32.sqrt() / 2.0, -2.0f32.sqrt() / 2.0, 0.0), 380 | rot * vec 381 | ); 382 | } 383 | 384 | #[test] 385 | fn test_yz() { 386 | let vec = vec3(1.0, 0.0, 0.0); 387 | 388 | let rot = Quaternion::from_axis_angle(vec3(0.0, 1.0, 1.0).normalize(), Deg(-90.0)); 389 | assert_ulps_eq!( 390 | vec3(0.0, -2.0f32.sqrt() / 2.0, 2.0f32.sqrt() / 2.0), 391 | rot * vec 392 | ); 393 | } 394 | 395 | #[test] 396 | fn test_xz() { 397 | let vec = vec3(0.0, 1.0, 0.0); 398 | 399 | let rot = Quaternion::from_axis_angle(vec3(1.0, 0.0, 1.0).normalize(), Deg(90.0)); 400 | assert_ulps_eq!( 401 | vec3(-2.0f32.sqrt() / 2.0, 0.0, 2.0f32.sqrt() / 2.0), 402 | rot * vec 403 | ); 404 | } 405 | } 406 | 407 | mod rotate_between_vectors { 408 | use cgmath::*; 409 | 410 | #[test] 411 | fn test_around_z_0() { 412 | let expected = Quaternion::new(1.0, 0.0, 0.0, 0.0); 413 | 414 | let a = vec3(12.0, 0.0, 0.0); 415 | let b = vec3(1.0, 0.0, 0.0); 416 | 417 | assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); 418 | } 419 | 420 | #[test] 421 | fn test_around_z_90_cw() { 422 | let expected = Quaternion::new(0.5_f32.sqrt(), 0.0, 0.0, 0.5_f32.sqrt()); 423 | 424 | let a = vec3(8.0, 0.0, 0.0); 425 | let b = vec3(0.0, 9.0, 0.0); 426 | 427 | assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); 428 | } 429 | 430 | #[test] 431 | fn test_around_z_90_ccw() { 432 | let expected = Quaternion::new(0.5_f32.sqrt(), 0.0, 0.0, -0.5_f32.sqrt()); 433 | 434 | let a = vec3(-26.0, 0.0, 0.0); 435 | let b = vec3(0.0, 10.0, 0.0); 436 | 437 | assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); 438 | } 439 | 440 | #[test] 441 | fn test_around_z_180_cw() { 442 | let expected = Quaternion::new(0.0, 0.0, 0.0, 1.0); 443 | 444 | let a = vec3(10.0, 0.0, 0.0); 445 | let b = vec3(-5.0, 0.0, 0.0); 446 | 447 | assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); 448 | } 449 | 450 | #[test] 451 | fn test_around_z_180_ccw() { 452 | let expected = Quaternion::new(0.0, 0.0, 0.0, -1.0); 453 | 454 | let a = vec3(-3.0, 0.0, 0.0); 455 | let b = vec3(40.0, 0.0, 0.0); 456 | 457 | assert_ulps_eq!(Quaternion::between_vectors(a, b), expected); 458 | } 459 | } 460 | 461 | mod cast { 462 | use cgmath::*; 463 | 464 | #[test] 465 | fn test_cast() { 466 | assert_ulps_eq!( 467 | Quaternion::new(0.9f64, 1.5, 2.4, 7.6).cast().unwrap(), 468 | Quaternion::new(0.9f32, 1.5, 2.4, 7.6) 469 | ); 470 | } 471 | } 472 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file, following 4 | the format defined at [keepachangelog.com](http://keepachangelog.com/). 5 | This project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [Unreleased] 8 | 9 | ### Changed 10 | 11 | - Refactored dependencies of experimental "specialization" feature into 12 | default_fn! macro to reduce code duplication and complexity. Currently 13 | only needed for non-functional SIMD feature. 14 | - Refactored SIMD code into separate source files. See README.md for details. 15 | - **Breaking**: Quaternion memory layout changed to `[x, y, z, w]`. The 16 | `From` and `Into` impls for `[S; 4]` and `(S, S, S, S)` have been changed 17 | accordingly. 18 | 19 | 20 | ### Added 21 | 22 | - Add `VectorN::zip` and `PointN::zip` 23 | 24 | ## [v0.17.0] - 2019-01-17 25 | 26 | ### Added 27 | 28 | - Add signed `Angle` normalization 29 | 30 | ### Changed 31 | 32 | - Move `lerp()` from `InnerSpace` to `VectorSpace` 33 | - `const` constructors 34 | 35 | ## [v0.16.1] - 2018-03-21 36 | 37 | ### Added 38 | 39 | - Implement `ElementWise` trait for point types 40 | - Add `map` function to points and vectors 41 | 42 | ### Changed 43 | 44 | - Remove `BaseNum` trait requirement for `PointN::new` functions 45 | 46 | ## [v0.16.0] - 2018-01-03 47 | 48 | ### Added 49 | 50 | - Add `InnerSpace::project_on` 51 | - Add `Array::len` 52 | - Re-export `Bounded` and implement for vectors, points, and angles 53 | - Add vector subtraction to `EuclideanSpace` 54 | - Add swizzle functions behind that `"swizzle"` feature 55 | - Add `Matrix4::look_at_dir` 56 | 57 | ### Changed 58 | 59 | - Return `Option` from cast functions 60 | 61 | ## [v0.15.0] - 2017-07-30 62 | 63 | ### Added 64 | 65 | - Implement `mint` conversions behind a feature 66 | - Add `Quaternion::cast` 67 | 68 | ### Changed 69 | 70 | - Rename `use_simd` feature to `simd` 71 | - Rename `eders` feature to `serde` 72 | 73 | ### Fixed 74 | 75 | - Fix matrix inversions for small determinants 76 | 77 | ## [v0.14.1] - 2017-05-02 78 | 79 | ### Fixed 80 | 81 | - Add a workaround for rust-lang/rust#41478, and in the process cleaned up 82 | some type projections for angles 83 | 84 | ## [v0.14.0] - 2017-04-26 85 | 86 | ## Changed 87 | 88 | - Constrain `VectorSpace`, `Rotation`, and `Angle` by `iter::Sum` 89 | - Constrain `SquareMatrix` by `iter::Product` 90 | 91 | ## [v0.13.1] - 2017-04-22 92 | 93 | ### Changed 94 | 95 | - Update `serde` and `serde_derive` to version `1.0`. 96 | 97 | ## [v0.13.0] - 2017-04-14 98 | 99 | ### Added 100 | 101 | - Add optional `use_simd` feature to improve the performance of `Vector4`, 102 | `Matrix4` and `Quaternion`. According to @DaseinPhaos in #394, under 103 | the given benchmark certain operations were able to become up to 60% faster. 104 | - Add component wise casting for the matrix and point types 105 | 106 | ### Changed 107 | 108 | - Update `serde` to version `0.9`, and use `serde_derive` instead of `serde_macros`. 109 | 110 | ## [v0.12.0] - 2016-09-14 111 | 112 | ### Changed 113 | 114 | - Use [approx](https://github.com/brendanzab/approx/) for approximate equality 115 | comparisons 116 | - Remove `#[repr(packed)]` from all structs where it was specified 117 | - Update serde to 0.8 118 | 119 | ## [v0.11.0] - 2016-08-17 120 | 121 | ### Added 122 | 123 | - `Quaternion::from_arc` 124 | 125 | ### Changed 126 | 127 | - Change the angle types to be tuple structs 128 | - Make from-angle constructors take generic `Into>` values 129 | - Fix `Decomposed::concat` implementation 130 | 131 | ## [v0.10.0] - 2016-05-11 132 | 133 | ### Added 134 | 135 | - A `MetricSpace` trait for types that have a distance between elements. 136 | - `EuclideanSpace::{midpoint, centroid}` functions with default 137 | implementations. 138 | - `Vector1` and `Point1` structs. 139 | - Serde support behind the `eders` feature flag. 140 | - An `ApproxEq` implementation for `Decomposed`. 141 | 142 | ### Changed 143 | 144 | - Depend on the `num-traits` crate rather than `num`, seeing as we only use the 145 | traits in `num`. `num_traits` has also been re-exported so that you can more 146 | easily use these in your project. 147 | - Use an `Euler` type for euler angle conversions. 148 | - Constrain `InnerSpace` by `MetricSpace`. 149 | - Constrain `Rotation` by `One` 150 | - Implement `Transform` and `Transform3` for `Matrix4`. 151 | - Implement `Transform`, `Transform2`, and `Transform3` for `Matrix4`. 152 | - Fix `Euler`-`Quaternion` and `Quaternion`-`Euler` conversions. The axes are 153 | now correct, and the angles are applied in _x_-_y_-_z_ order. The conversion now 154 | matches the conversion from axis angle. 155 | - Fix `Euler`-`{Matrix3, Matrix4}` conversions. 156 | 157 | ## Removed 158 | 159 | - `Rotation::transform_as_point` 160 | - `AffineMatrix3` 161 | - `Rotation::invert_self` 162 | - `Matrix::invert_self` 163 | 164 | ## [v0.9.1] - 2016-04-20 165 | 166 | ### Changed 167 | 168 | - Fix angle assignment operators so that they actually mutate `self`. 169 | 170 | ## [v0.9.0] - 2016-04-19 171 | 172 | ### Changed 173 | 174 | - Assignment operators implementations have been stabilised, to coincide with 175 | their [stabilisation in Rust 1.8](http://blog.rust-lang.org/2016/04/14/Rust-1.8.html). 176 | - Renames `Vector` trait to `VectorSpace`. 177 | - Renames `EuclideanVector` to `InnerSpace`. 178 | - Renames `Point` to `EuclideanSpace`, and `Point::Vector` to `EuclideanSpace::Diff`. 179 | - `Quaternion`s now implement `VectorSpace` and `InnerSpace` for the functions 180 | they share. 181 | - The `Matrix` trait is now constraint by `VectorSpace`, with `Matrix::Element` 182 | removed in favor of `VectorSpace::Scalar`. 183 | 184 | ## [v0.8.0] - 2016-04-06 185 | 186 | ### Added 187 | 188 | - Implements `fmt::Debug` for `Basis2`, `Basis3`, and `AffineMatrix3` 189 | - A `prelude` module for easy importing of common traits. 190 | - Constrained conversion functions for assisting in situations where type 191 | inference is difficult. 192 | - An `ElementWise` trait for non-mathematical element-wise operations. 193 | - A default implementation for `EuclideanVector::angle`. 194 | 195 | ### Changed 196 | 197 | - Improves the `fmt::Debug` impls for `Vector`, `Matrix`, `Point`, `Decomposed`, 198 | `Quaternion` and `Angle` to make them easier to derive, and have clearer 199 | formatting. 200 | - Marks vectors, points, matrices, and angles as `#[repr(C, packed)]`. 201 | - Renames the `Vector::{length, length2}` functions to `Vector::{magnitude, magnitude2}`. 202 | - Move `Angle::new` to be directly implemented on the `Rad` and `Deg` types. 203 | - Move `Vector::dot` to `EuclideanVector` trait. 204 | - Move `Vector::from_value` to `Array` trait. 205 | 206 | ### Removed 207 | 208 | - The non-mathematical operator trait implementations have been removed from 209 | the `Vector` trait, in favor of the `ElementWise` trait. 210 | - `Angle::equiv`. 211 | - Remove `neg_self` method on vectors and matrices. 212 | 213 | ## [v0.7.0] - 2015-12-23 214 | 215 | ### Added 216 | - Add missing by-ref and by-val permutations of `Vector`, `Matrix`, `Point`, 217 | `Quaternion` and `Angle` operators. 218 | - Ease lifetime constraints by removing `'static` from some scalar type 219 | parameters. 220 | - Weaken type constraints on `perspective` function to take an `Into>`. 221 | - Add `Angle::new` for constructing angles from a unitless scalar. 222 | - Implement assignment operators for nightly builds, enabled by the `"unstable"` 223 | feature. 224 | 225 | ### Changed 226 | - `Vector`, `Matrix`, `Point`, and `Angle` are now constrained to require 227 | specific operators to be overloaded. This means that generic code can now use 228 | operators, instead of the operator methods. 229 | - Take a `Rad` for `ProjectionFov::fovy`, rather than arbitrary `Angle`s. This 230 | simplifies the signature of `PerspectiveFov` from `PerspectiveFov` to 231 | `PerspectiveFov`. 232 | - The following trait constraints were removed from `Angle`: `Debug`, 233 | `ScalarConv`, `Into>`, `Into>`. 234 | - `Angle` no longer requires `One`, and the implementations have been removed 235 | from `Deg` and `Rad`. This is because angles do not close over multiplication, 236 | and therefore cannot have a multiplicative identity. If we were truly accurate, 237 | `Angle * Angle` would return an `Angle^2` (not supported by the current api). 238 | - Make remainder operators on `Angle`s make sense from the perspective of 239 | dimensional analysis. 240 | - Moved free trigonometric functions onto `Angle`. 241 | 242 | ### Removed 243 | - Remove redundant `Point::{min, max}` methods - these are now covered by the 244 | `Array::{min, max}` methods that were introduced in 0.5.0. 245 | - Removed `ToComponents`, `ToComponents2`, and `ToComponents3`. If you were 246 | relying on `ToComponents::decompose`, you can produce the same effect by 247 | accessing the fields on `Decomposed` directly. To create the scale vector, 248 | use: `Vector::from_value(transform.scale)`. 249 | - Removed `CompositeTransform`, `CompositeTransform2`, and `CompositeTransform3`. 250 | - Remove `Vector::one`. Vectors don't really have a multiplicative identity. 251 | If you really want a `one` vector, you can do something like: 252 | `Vector::from_value(1.0)`. 253 | - Remove operator methods from `Vector`, `Matrix`, `Point`, and `Angle` traits 254 | in favor of operator overloading. 255 | - Remove `*_self` methods from `Vector`, `Matrix`, `Point`, and `Angle`. The 256 | operator methods can be used via the unstable assignment operators. 257 | - Remove `#[derive(Hash)]` from `Deg` and `Rad`. This could never really be used 258 | these types, because they expect to be given a `BaseFloat` under normal 259 | circumstances. 260 | 261 | ## [v0.6.0] - 2015-12-12 262 | 263 | ### Added 264 | - This CHANGELOG for keeping track of notable changes. 265 | - `Matrix4::{from_scale, from_nonuniform_scale}` for easily constructing 266 | homogeneous scale matrices. 267 | 268 | ### Changed 269 | - Renamed `SquareMatrix::one` to `SquareMatrix::identity`. `identity` is easier 270 | to search for, 271 | and the more common name for the multiplicative identity for matrices. 272 | - Matrix impls have now been constrained to `S: BaseFloat`. 273 | 274 | ## [v0.5.0] - 2015-11-20 275 | 276 | ### Changed 277 | - Take many point and vector parameters by value. 278 | - Take point and vector operator overloads by value. 279 | - Divide `Matrix` trait into `Matrix` and `SquareMatrix`, opening the door for 280 | non-square matrices in the future. 281 | - Make many trait type parameters associated types. 282 | - Move element-wise methods from `Vector` and `Point` onto the `Array1` trait, 283 | and rename it to `Array`. 284 | - Make pointer access methods on `Array` match the naming scheme of those in the 285 | standard library. 286 | 287 | ### Removed 288 | - Removed collision types: `Ray`, `Plane`, `Frustum`, `Aabb2`, `Aabb3` `Obb2`, 289 | `Obb3` `Sphere`, `Cylinder`. These can now be found at 290 | [csherratt/collision-rs](https://github.com/csherratt/collision-rs). 291 | - Remove `Array2` trait, moving methods onto the `Matrix` trait. 292 | 293 | ## [v0.4.0] - 2015-10-25 294 | 295 | ## [v0.3.1] - 2015-09-20 296 | 297 | ## [v0.3.0] - 2015-09-20 298 | 299 | ## [v0.2.0] - 2015-05-11 300 | 301 | ## [v0.1.6] - 2015-05-10 302 | 303 | ## [v0.1.5] - 2015-04-25 304 | 305 | ## [v0.1.4] - 2015-04-24 306 | 307 | ## [v0.1.3] - 2015-04-06 308 | 309 | ## [v0.1.2] - 2015-04-01 310 | 311 | ## [v0.1.1] - 2015-03-25 312 | 313 | ## [v0.1.0] - 2015-03-15 314 | 315 | ## [v0.0.8] - 2015-03-09 316 | 317 | ## [v0.0.7] - 2015-03-01 318 | 319 | ## [v0.0.6] - 2015-02-21 320 | 321 | ## [v0.0.5] - 2015-02-16 322 | 323 | ## [v0.0.4] - 2015-02-11 324 | 325 | ## [v0.0.3] - 2015-02-08 326 | 327 | ## v0.0.1 - 2014-06-24 328 | 329 | [Unreleased]: https://github.com/brendanzab/cgmath/compare/v0.16.1...HEAD 330 | [v0.16.1]: https://github.com/brendanzab/cgmath/compare/v0.16.0...v0.16.1 331 | [v0.16.0]: https://github.com/brendanzab/cgmath/compare/v0.15.0...v0.16.0 332 | [v0.15.0]: https://github.com/brendanzab/cgmath/compare/v0.14.1...v0.15.0 333 | [v0.14.1]: https://github.com/brendanzab/cgmath/compare/v0.14.0...v0.14.1 334 | [v0.14.0]: https://github.com/brendanzab/cgmath/compare/v0.13.1...v0.14.0 335 | [v0.13.1]: https://github.com/brendanzab/cgmath/compare/v0.13.0...v0.13.1 336 | [v0.12.0]: https://github.com/brendanzab/cgmath/compare/v0.12.0...v0.13.0 337 | [v0.12.0]: https://github.com/brendanzab/cgmath/compare/v0.11.0...v0.12.0 338 | [v0.11.0]: https://github.com/brendanzab/cgmath/compare/v0.10.0...v0.11.0 339 | [v0.10.0]: https://github.com/brendanzab/cgmath/compare/v0.9.1...v0.10.0 340 | [v0.9.1]: https://github.com/brendanzab/cgmath/compare/v0.9.0...v0.9.1 341 | [v0.9.0]: https://github.com/brendanzab/cgmath/compare/v0.8.0...v0.9.0 342 | [v0.8.0]: https://github.com/brendanzab/cgmath/compare/v0.7.0...v0.8.0 343 | [v0.7.0]: https://github.com/brendanzab/cgmath/compare/v0.6.0...v0.7.0 344 | [v0.6.0]: https://github.com/brendanzab/cgmath/compare/v0.5.0...v0.6.0 345 | [v0.5.0]: https://github.com/brendanzab/cgmath/compare/v0.4.0...v0.5.0 346 | [v0.4.0]: https://github.com/brendanzab/cgmath/compare/v0.3.1...v0.4.0 347 | [v0.3.1]: https://github.com/brendanzab/cgmath/compare/v0.3.0...v0.3.1 348 | [v0.3.0]: https://github.com/brendanzab/cgmath/compare/v0.2.0...v0.3.0 349 | [v0.2.0]: https://github.com/brendanzab/cgmath/compare/v0.1.6...v0.2.0 350 | [v0.1.6]: https://github.com/brendanzab/cgmath/compare/v0.1.5...v0.1.6 351 | [v0.1.5]: https://github.com/brendanzab/cgmath/compare/v0.1.4...v0.1.5 352 | [v0.1.4]: https://github.com/brendanzab/cgmath/compare/v0.1.3...v0.1.4 353 | [v0.1.3]: https://github.com/brendanzab/cgmath/compare/v0.1.2...v0.1.3 354 | [v0.1.2]: https://github.com/brendanzab/cgmath/compare/v0.1.1...v0.1.2 355 | [v0.1.1]: https://github.com/brendanzab/cgmath/compare/v0.1.0...v0.1.1 356 | [v0.1.0]: https://github.com/brendanzab/cgmath/compare/v0.0.8...v0.1.0 357 | [v0.0.8]: https://github.com/brendanzab/cgmath/compare/v0.0.7...v0.0.8 358 | [v0.0.7]: https://github.com/brendanzab/cgmath/compare/v0.0.6...v0.0.7 359 | [v0.0.6]: https://github.com/brendanzab/cgmath/compare/v0.0.5...v0.0.6 360 | [v0.0.5]: https://github.com/brendanzab/cgmath/compare/v0.0.4...v0.0.5 361 | [v0.0.4]: https://github.com/brendanzab/cgmath/compare/v0.0.3...v0.0.4 362 | [v0.0.3]: https://github.com/brendanzab/cgmath/compare/v0.0.1...v0.0.3 363 | -------------------------------------------------------------------------------- /src/transform.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use structure::*; 17 | 18 | use approx; 19 | use matrix::{Matrix2, Matrix3, Matrix4}; 20 | use num::{BaseFloat, BaseNum}; 21 | use point::{Point2, Point3}; 22 | use rotation::*; 23 | use vector::{Vector2, Vector3}; 24 | 25 | use std::ops::Mul; 26 | 27 | /// A trait representing an [affine 28 | /// transformation](https://en.wikipedia.org/wiki/Affine_transformation) that 29 | /// can be applied to points or vectors. An affine transformation is one which 30 | pub trait Transform: Sized + One { 31 | /// Create a transformation that rotates a vector to look at `center` from 32 | /// `eye`, using `up` for orientation. 33 | #[deprecated = "Use look_at_rh or look_at_lh"] 34 | fn look_at(eye: P, center: P, up: P::Diff) -> Self; 35 | 36 | /// Create a transformation that rotates a vector to look at `center` from 37 | /// `eye`, using `up` for orientation. 38 | fn look_at_rh(eye: P, center: P, up: P::Diff) -> Self; 39 | 40 | /// Create a transformation that rotates a vector to look at `center` from 41 | /// `eye`, using `up` for orientation. 42 | fn look_at_lh(eye: P, center: P, up: P::Diff) -> Self; 43 | 44 | /// Transform a vector using this transform. 45 | fn transform_vector(&self, vec: P::Diff) -> P::Diff; 46 | 47 | /// Inverse transform a vector using this transform 48 | fn inverse_transform_vector(&self, vec: P::Diff) -> Option { 49 | self.inverse_transform() 50 | .map(|inverse| inverse.transform_vector(vec)) 51 | } 52 | 53 | /// Transform a point using this transform. 54 | fn transform_point(&self, point: P) -> P; 55 | 56 | /// Combine this transform with another, yielding a new transformation 57 | /// which has the effects of both. 58 | fn concat(&self, other: &Self) -> Self; 59 | 60 | /// Create a transform that "un-does" this one. 61 | fn inverse_transform(&self) -> Option; 62 | 63 | /// Combine this transform with another, in-place. 64 | #[inline] 65 | fn concat_self(&mut self, other: &Self) { 66 | *self = Self::concat(self, other); 67 | } 68 | } 69 | 70 | /// A generic transformation consisting of a rotation, 71 | /// displacement vector and scale amount. 72 | #[derive(Copy, Clone, Debug, PartialEq)] 73 | pub struct Decomposed { 74 | pub scale: V::Scalar, 75 | pub rot: R, 76 | pub disp: V, 77 | } 78 | 79 | impl> One for Decomposed 80 | where 81 | P::Scalar: BaseFloat, 82 | { 83 | fn one() -> Self { 84 | Decomposed { 85 | scale: P::Scalar::one(), 86 | rot: R::one(), 87 | disp: P::Diff::zero(), 88 | } 89 | } 90 | } 91 | 92 | impl> Mul for Decomposed 93 | where 94 | P::Scalar: BaseFloat, 95 | P::Diff: VectorSpace, 96 | { 97 | type Output = Self; 98 | 99 | /// Multiplies the two transforms together. 100 | /// The result should be as if the two transforms were converted 101 | /// to matrices, then multiplied, then converted back with 102 | /// a (currently nonexistent) function that tries to convert 103 | /// a matrix into a `Decomposed`. 104 | fn mul(self, rhs: Decomposed) -> Self::Output { 105 | self.concat(&rhs) 106 | } 107 | } 108 | 109 | impl> Transform

for Decomposed 110 | where 111 | P::Scalar: BaseFloat, 112 | P::Diff: VectorSpace, 113 | { 114 | #[inline] 115 | fn look_at(eye: P, center: P, up: P::Diff) -> Decomposed { 116 | let rot = R::look_at(center - eye, up); 117 | let disp = rot.rotate_vector(P::origin() - eye); 118 | Decomposed { 119 | scale: P::Scalar::one(), 120 | rot, 121 | disp, 122 | } 123 | } 124 | 125 | #[inline] 126 | fn look_at_rh(eye: P, center: P, up: P::Diff) -> Decomposed { 127 | let rot = R::look_at(eye - center, up); 128 | let disp = rot.rotate_vector(P::origin() - eye); 129 | Decomposed { 130 | scale: P::Scalar::one(), 131 | rot, 132 | disp, 133 | } 134 | } 135 | 136 | #[inline] 137 | fn look_at_lh(eye: P, center: P, up: P::Diff) -> Decomposed { 138 | let rot = R::look_at(center - eye, up); 139 | let disp = rot.rotate_vector(P::origin() - eye); 140 | Decomposed { 141 | scale: P::Scalar::one(), 142 | rot, 143 | disp, 144 | } 145 | } 146 | 147 | #[inline] 148 | fn transform_vector(&self, vec: P::Diff) -> P::Diff { 149 | self.rot.rotate_vector(vec * self.scale) 150 | } 151 | 152 | #[inline] 153 | fn inverse_transform_vector(&self, vec: P::Diff) -> Option { 154 | if ulps_eq!(self.scale, &P::Scalar::zero()) { 155 | None 156 | } else { 157 | Some(self.rot.invert().rotate_vector(vec / self.scale)) 158 | } 159 | } 160 | 161 | #[inline] 162 | fn transform_point(&self, point: P) -> P { 163 | self.rot.rotate_point(point * self.scale) + self.disp 164 | } 165 | 166 | fn concat(&self, other: &Decomposed) -> Decomposed { 167 | Decomposed { 168 | scale: self.scale * other.scale, 169 | rot: self.rot * other.rot, 170 | disp: self.rot.rotate_vector(other.disp * self.scale) + self.disp, 171 | } 172 | } 173 | 174 | fn inverse_transform(&self) -> Option> { 175 | if ulps_eq!(self.scale, &P::Scalar::zero()) { 176 | None 177 | } else { 178 | let s = P::Scalar::one() / self.scale; 179 | let r = self.rot.invert(); 180 | let d = r.rotate_vector(self.disp.clone()) * -s; 181 | Some(Decomposed { 182 | scale: s, 183 | rot: r, 184 | disp: d, 185 | }) 186 | } 187 | } 188 | } 189 | 190 | pub trait Transform2: 191 | Transform::Scalar>> + Into::Scalar>> 192 | { 193 | type Scalar: BaseNum; 194 | } 195 | pub trait Transform3: 196 | Transform::Scalar>> + Into::Scalar>> 197 | { 198 | type Scalar: BaseNum; 199 | } 200 | 201 | impl> From, R>> for Matrix3 { 202 | fn from(dec: Decomposed, R>) -> Matrix3 { 203 | let m: Matrix2<_> = dec.rot.into(); 204 | let mut m: Matrix3<_> = (m * dec.scale).into(); 205 | m.z = dec.disp.extend(S::one()); 206 | m 207 | } 208 | } 209 | 210 | impl> From, R>> for Matrix4 { 211 | fn from(dec: Decomposed, R>) -> Matrix4 { 212 | let m: Matrix3<_> = dec.rot.into(); 213 | let mut m: Matrix4<_> = (m * dec.scale).into(); 214 | m.w = dec.disp.extend(S::one()); 215 | m 216 | } 217 | } 218 | 219 | impl> Transform2 for Decomposed, R> { 220 | type Scalar = S; 221 | } 222 | 223 | impl> Transform3 for Decomposed, R> { 224 | type Scalar = S; 225 | } 226 | 227 | impl approx::AbsDiffEq for Decomposed 228 | where 229 | S: approx::AbsDiffEq, 230 | S::Scalar: approx::AbsDiffEq, 231 | R: approx::AbsDiffEq, 232 | { 233 | type Epsilon = E; 234 | 235 | #[inline] 236 | fn default_epsilon() -> E { 237 | E::default_epsilon() 238 | } 239 | 240 | #[inline] 241 | fn abs_diff_eq(&self, other: &Self, epsilon: E) -> bool { 242 | S::Scalar::abs_diff_eq(&self.scale, &other.scale, epsilon) 243 | && R::abs_diff_eq(&self.rot, &other.rot, epsilon) 244 | && S::abs_diff_eq(&self.disp, &other.disp, epsilon) 245 | } 246 | } 247 | 248 | impl approx::RelativeEq for Decomposed 249 | where 250 | S: approx::RelativeEq, 251 | S::Scalar: approx::RelativeEq, 252 | R: approx::RelativeEq, 253 | { 254 | #[inline] 255 | fn default_max_relative() -> E { 256 | E::default_max_relative() 257 | } 258 | 259 | #[inline] 260 | fn relative_eq(&self, other: &Self, epsilon: E, max_relative: E) -> bool { 261 | S::Scalar::relative_eq(&self.scale, &other.scale, epsilon, max_relative) 262 | && R::relative_eq(&self.rot, &other.rot, epsilon, max_relative) 263 | && S::relative_eq(&self.disp, &other.disp, epsilon, max_relative) 264 | } 265 | } 266 | 267 | impl approx::UlpsEq for Decomposed 268 | where 269 | S: approx::UlpsEq, 270 | S::Scalar: approx::UlpsEq, 271 | R: approx::UlpsEq, 272 | { 273 | #[inline] 274 | fn default_max_ulps() -> u32 { 275 | E::default_max_ulps() 276 | } 277 | 278 | #[inline] 279 | fn ulps_eq(&self, other: &Self, epsilon: E, max_ulps: u32) -> bool { 280 | S::Scalar::ulps_eq(&self.scale, &other.scale, epsilon, max_ulps) 281 | && R::ulps_eq(&self.rot, &other.rot, epsilon, max_ulps) 282 | && S::ulps_eq(&self.disp, &other.disp, epsilon, max_ulps) 283 | } 284 | } 285 | 286 | #[cfg(feature = "serde")] 287 | #[doc(hidden)] 288 | mod serde_ser { 289 | use super::Decomposed; 290 | use serde::ser::SerializeStruct; 291 | use serde::{self, Serialize}; 292 | use structure::VectorSpace; 293 | 294 | impl Serialize for Decomposed 295 | where 296 | V: Serialize + VectorSpace, 297 | V::Scalar: Serialize, 298 | R: Serialize, 299 | { 300 | fn serialize(&self, serializer: S) -> Result 301 | where 302 | S: serde::Serializer, 303 | { 304 | let mut struc = serializer.serialize_struct("Decomposed", 3)?; 305 | struc.serialize_field("scale", &self.scale)?; 306 | struc.serialize_field("rot", &self.rot)?; 307 | struc.serialize_field("disp", &self.disp)?; 308 | struc.end() 309 | } 310 | } 311 | } 312 | 313 | #[cfg(feature = "serde")] 314 | #[doc(hidden)] 315 | mod serde_de { 316 | use super::Decomposed; 317 | use serde::{self, Deserialize}; 318 | use std::fmt; 319 | use std::marker::PhantomData; 320 | use structure::VectorSpace; 321 | 322 | enum DecomposedField { 323 | Scale, 324 | Rot, 325 | Disp, 326 | } 327 | 328 | impl<'a> Deserialize<'a> for DecomposedField { 329 | fn deserialize(deserializer: D) -> Result 330 | where 331 | D: serde::Deserializer<'a>, 332 | { 333 | struct DecomposedFieldVisitor; 334 | 335 | impl<'b> serde::de::Visitor<'b> for DecomposedFieldVisitor { 336 | type Value = DecomposedField; 337 | 338 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 339 | formatter.write_str("`scale`, `rot` or `disp`") 340 | } 341 | 342 | fn visit_str(self, value: &str) -> Result 343 | where 344 | E: serde::de::Error, 345 | { 346 | match value { 347 | "scale" => Ok(DecomposedField::Scale), 348 | "rot" => Ok(DecomposedField::Rot), 349 | "disp" => Ok(DecomposedField::Disp), 350 | _ => Err(serde::de::Error::custom("expected scale, rot or disp")), 351 | } 352 | } 353 | } 354 | 355 | deserializer.deserialize_str(DecomposedFieldVisitor) 356 | } 357 | } 358 | 359 | impl<'a, S: VectorSpace, R> Deserialize<'a> for Decomposed 360 | where 361 | S: Deserialize<'a>, 362 | S::Scalar: Deserialize<'a>, 363 | R: Deserialize<'a>, 364 | { 365 | fn deserialize(deserializer: D) -> Result, D::Error> 366 | where 367 | D: serde::de::Deserializer<'a>, 368 | { 369 | const FIELDS: &[&str] = &["scale", "rot", "disp"]; 370 | deserializer.deserialize_struct("Decomposed", FIELDS, DecomposedVisitor(PhantomData)) 371 | } 372 | } 373 | 374 | struct DecomposedVisitor(PhantomData<(S, R)>); 375 | 376 | impl<'a, S: VectorSpace, R> serde::de::Visitor<'a> for DecomposedVisitor 377 | where 378 | S: Deserialize<'a>, 379 | S::Scalar: Deserialize<'a>, 380 | R: Deserialize<'a>, 381 | { 382 | type Value = Decomposed; 383 | 384 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 385 | formatter.write_str("`scale`, `rot` and `disp` fields") 386 | } 387 | 388 | fn visit_map(self, mut visitor: V) -> Result, V::Error> 389 | where 390 | V: serde::de::MapAccess<'a>, 391 | { 392 | let mut scale = None; 393 | let mut rot = None; 394 | let mut disp = None; 395 | 396 | while let Some(key) = visitor.next_key()? { 397 | match key { 398 | DecomposedField::Scale => { 399 | scale = Some(visitor.next_value()?); 400 | } 401 | DecomposedField::Rot => { 402 | rot = Some(visitor.next_value()?); 403 | } 404 | DecomposedField::Disp => { 405 | disp = Some(visitor.next_value()?); 406 | } 407 | } 408 | } 409 | 410 | let scale = match scale { 411 | Some(scale) => scale, 412 | None => return Err(serde::de::Error::missing_field("scale")), 413 | }; 414 | 415 | let rot = match rot { 416 | Some(rot) => rot, 417 | None => return Err(serde::de::Error::missing_field("rot")), 418 | }; 419 | 420 | let disp = match disp { 421 | Some(disp) => disp, 422 | None => return Err(serde::de::Error::missing_field("disp")), 423 | }; 424 | 425 | Ok(Decomposed { scale, rot, disp }) 426 | } 427 | } 428 | } 429 | -------------------------------------------------------------------------------- /src/rotation.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use std::fmt; 17 | use std::iter; 18 | use std::ops::*; 19 | 20 | use structure::*; 21 | 22 | use angle::Rad; 23 | use approx; 24 | use euler::Euler; 25 | use matrix::{Matrix2, Matrix3}; 26 | use num::{BaseFloat, BaseNum}; 27 | use point::{Point2, Point3}; 28 | use quaternion::Quaternion; 29 | use vector::{Vector2, Vector3}; 30 | 31 | /// A trait for a generic rotation. A rotation is a transformation that 32 | /// creates a circular motion, and preserves at least one point in the space. 33 | pub trait Rotation: Sized + Copy + One 34 | where 35 | // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 36 | Self: approx::AbsDiffEq::Space as EuclideanSpace>::Scalar>, 37 | Self: approx::RelativeEq::Space as EuclideanSpace>::Scalar>, 38 | Self: approx::UlpsEq::Space as EuclideanSpace>::Scalar>, 39 | ::Scalar: BaseNum, 40 | Self: iter::Product, 41 | { 42 | type Space: EuclideanSpace; 43 | 44 | /// Create a rotation to a given direction with an 'up' vector. 45 | fn look_at( 46 | dir: ::Diff, 47 | up: ::Diff, 48 | ) -> Self; 49 | 50 | /// Create a shortest rotation to transform vector 'a' into 'b'. 51 | /// Both given vectors are assumed to have unit length. 52 | fn between_vectors( 53 | a: ::Diff, 54 | b: ::Diff, 55 | ) -> Self; 56 | 57 | /// Rotate a vector using this rotation. 58 | fn rotate_vector( 59 | &self, 60 | vec: ::Diff, 61 | ) -> ::Diff; 62 | 63 | /// Rotate a point using this rotation, by converting it to its 64 | /// representation as a vector. 65 | #[inline] 66 | fn rotate_point(&self, point: Self::Space) -> Self::Space { 67 | Self::Space::from_vec(self.rotate_vector(point.to_vec())) 68 | } 69 | 70 | /// Create a new rotation which "un-does" this rotation. That is, 71 | /// `r * r.invert()` is the identity. 72 | fn invert(&self) -> Self; 73 | } 74 | 75 | /// A two-dimensional rotation. 76 | pub trait Rotation2: 77 | Rotation::Scalar>> 78 | + Into::Scalar>> 79 | + Into::Scalar>> 80 | { 81 | type Scalar: BaseFloat; 82 | 83 | /// Create a rotation by a given angle. Thus is a redundant case of both 84 | /// from_axis_angle() and from_euler() for 2D space. 85 | fn from_angle>>(theta: A) -> Self; 86 | } 87 | 88 | /// A three-dimensional rotation. 89 | pub trait Rotation3: 90 | Rotation::Scalar>> 91 | + Into::Scalar>> 92 | + Into::Scalar>> 93 | + Into::Scalar>> 94 | + From::Scalar>>> 95 | { 96 | type Scalar: BaseFloat; 97 | 98 | /// Create a rotation using an angle around a given axis. 99 | /// 100 | /// The specified axis **must be normalized**, or it represents an invalid rotation. 101 | fn from_axis_angle>>(axis: Vector3, angle: A) -> Self; 102 | 103 | /// Create a rotation from an angle around the `x` axis (pitch). 104 | #[inline] 105 | fn from_angle_x>>(theta: A) -> Self { 106 | Rotation3::from_axis_angle(Vector3::unit_x(), theta) 107 | } 108 | 109 | /// Create a rotation from an angle around the `y` axis (yaw). 110 | #[inline] 111 | fn from_angle_y>>(theta: A) -> Self { 112 | Rotation3::from_axis_angle(Vector3::unit_y(), theta) 113 | } 114 | 115 | /// Create a rotation from an angle around the `z` axis (roll). 116 | #[inline] 117 | fn from_angle_z>>(theta: A) -> Self { 118 | Rotation3::from_axis_angle(Vector3::unit_z(), theta) 119 | } 120 | } 121 | 122 | /// A two-dimensional rotation matrix. 123 | /// 124 | /// The matrix is guaranteed to be orthogonal, so some operations can be 125 | /// implemented more efficiently than the implementations for `math::Matrix2`. To 126 | /// enforce orthogonality at the type level the operations have been restricted 127 | /// to a subset of those implemented on `Matrix2`. 128 | /// 129 | /// ## Example 130 | /// 131 | /// Suppose we want to rotate a vector that lies in the x-y plane by some 132 | /// angle. We can accomplish this quite easily with a two-dimensional rotation 133 | /// matrix: 134 | /// 135 | /// ```no_run 136 | /// use cgmath::Rad; 137 | /// use cgmath::Vector2; 138 | /// use cgmath::{Matrix, Matrix2}; 139 | /// use cgmath::{Rotation, Rotation2, Basis2}; 140 | /// use cgmath::UlpsEq; 141 | /// use std::f64; 142 | /// 143 | /// // For simplicity, we will rotate the unit x vector to the unit y vector -- 144 | /// // so the angle is 90 degrees, or π/2. 145 | /// let unit_x: Vector2 = Vector2::unit_x(); 146 | /// let rot: Basis2 = Rotation2::from_angle(Rad(0.5f64 * f64::consts::PI)); 147 | /// 148 | /// // Rotate the vector using the two-dimensional rotation matrix: 149 | /// let unit_y = rot.rotate_vector(unit_x); 150 | /// 151 | /// // Since sin(π/2) may not be exactly zero due to rounding errors, we can 152 | /// // use approx assert_ulps_eq!() feature to show that it is close enough. 153 | /// // assert_ulps_eq!(&unit_y, &Vector2::unit_y()); // TODO: Figure out how to use this 154 | /// 155 | /// // This is exactly equivalent to using the raw matrix itself: 156 | /// let unit_y2: Matrix2<_> = rot.into(); 157 | /// let unit_y2 = unit_y2 * unit_x; 158 | /// assert_eq!(unit_y2, unit_y); 159 | /// 160 | /// // Note that we can also concatenate rotations: 161 | /// let rot_half: Basis2 = Rotation2::from_angle(Rad(0.25f64 * f64::consts::PI)); 162 | /// let unit_y3 = (rot_half * rot_half).rotate_vector(unit_x); 163 | /// // assert_ulps_eq!(&unit_y3, &unit_y2); // TODO: Figure out how to use this 164 | /// ``` 165 | #[derive(PartialEq, Copy, Clone)] 166 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 167 | pub struct Basis2 { 168 | mat: Matrix2, 169 | } 170 | 171 | impl Basis2 { 172 | pub fn look_at_stable(dir: Vector2, flip: bool) -> Basis2 { 173 | Basis2 { 174 | mat: Matrix2::look_at_stable(dir, flip), 175 | } 176 | } 177 | } 178 | 179 | impl AsRef> for Basis2 { 180 | #[inline] 181 | fn as_ref(&self) -> &Matrix2 { 182 | &self.mat 183 | } 184 | } 185 | 186 | impl From> for Matrix2 { 187 | #[inline] 188 | fn from(b: Basis2) -> Matrix2 { 189 | b.mat 190 | } 191 | } 192 | 193 | impl iter::Product> for Basis2 { 194 | #[inline] 195 | fn product>>(iter: I) -> Basis2 { 196 | iter.fold(Basis2::one(), Mul::mul) 197 | } 198 | } 199 | 200 | impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis2> for Basis2 { 201 | #[inline] 202 | fn product>>(iter: I) -> Basis2 { 203 | iter.fold(Basis2::one(), Mul::mul) 204 | } 205 | } 206 | 207 | impl Rotation for Basis2 { 208 | type Space = Point2; 209 | 210 | #[inline] 211 | fn look_at(dir: Vector2, up: Vector2) -> Basis2 { 212 | Basis2 { 213 | mat: Matrix2::look_at(dir, up), 214 | } 215 | } 216 | 217 | #[inline] 218 | fn between_vectors(a: Vector2, b: Vector2) -> Basis2 { 219 | Rotation2::from_angle(Rad::acos(a.dot(b))) 220 | } 221 | 222 | #[inline] 223 | fn rotate_vector(&self, vec: Vector2) -> Vector2 { 224 | self.mat * vec 225 | } 226 | 227 | // TODO: we know the matrix is orthogonal, so this could be re-written 228 | // to be faster 229 | #[inline] 230 | fn invert(&self) -> Basis2 { 231 | Basis2 { 232 | mat: self.mat.invert().unwrap(), 233 | } 234 | } 235 | } 236 | 237 | impl One for Basis2 { 238 | #[inline] 239 | fn one() -> Basis2 { 240 | Basis2 { 241 | mat: Matrix2::one(), 242 | } 243 | } 244 | } 245 | 246 | impl_operator!( Mul > for Basis2 { 247 | fn mul(lhs, rhs) -> Basis2 { Basis2 { mat: lhs.mat * rhs.mat } } 248 | }); 249 | 250 | impl approx::AbsDiffEq for Basis2 { 251 | type Epsilon = S::Epsilon; 252 | 253 | #[inline] 254 | fn default_epsilon() -> S::Epsilon { 255 | S::default_epsilon() 256 | } 257 | 258 | #[inline] 259 | fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { 260 | Matrix2::abs_diff_eq(&self.mat, &other.mat, epsilon) 261 | } 262 | } 263 | 264 | impl approx::RelativeEq for Basis2 { 265 | #[inline] 266 | fn default_max_relative() -> S::Epsilon { 267 | S::default_max_relative() 268 | } 269 | 270 | #[inline] 271 | fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { 272 | Matrix2::relative_eq(&self.mat, &other.mat, epsilon, max_relative) 273 | } 274 | } 275 | 276 | impl approx::UlpsEq for Basis2 { 277 | #[inline] 278 | fn default_max_ulps() -> u32 { 279 | S::default_max_ulps() 280 | } 281 | 282 | #[inline] 283 | fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { 284 | Matrix2::ulps_eq(&self.mat, &other.mat, epsilon, max_ulps) 285 | } 286 | } 287 | 288 | impl Rotation2 for Basis2 { 289 | type Scalar = S; 290 | 291 | fn from_angle>>(theta: A) -> Basis2 { 292 | Basis2 { 293 | mat: Matrix2::from_angle(theta), 294 | } 295 | } 296 | } 297 | 298 | impl fmt::Debug for Basis2 { 299 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 300 | write!(f, "Basis2 ")?; 301 | <[[S; 2]; 2] as fmt::Debug>::fmt(self.mat.as_ref(), f) 302 | } 303 | } 304 | 305 | /// A three-dimensional rotation matrix. 306 | /// 307 | /// The matrix is guaranteed to be orthogonal, so some operations, specifically 308 | /// inversion, can be implemented more efficiently than the implementations for 309 | /// `math::Matrix3`. To ensure orthogonality is maintained, the operations have 310 | /// been restricted to a subset of those implemented on `Matrix3`. 311 | #[derive(PartialEq, Copy, Clone)] 312 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 313 | pub struct Basis3 { 314 | mat: Matrix3, 315 | } 316 | 317 | impl Basis3 { 318 | /// Create a new rotation matrix from a quaternion. 319 | #[inline] 320 | pub fn from_quaternion(quaternion: &Quaternion) -> Basis3 { 321 | Basis3 { 322 | mat: (*quaternion).into(), 323 | } 324 | } 325 | } 326 | 327 | impl AsRef> for Basis3 { 328 | #[inline] 329 | fn as_ref(&self) -> &Matrix3 { 330 | &self.mat 331 | } 332 | } 333 | 334 | impl From> for Matrix3 { 335 | #[inline] 336 | fn from(b: Basis3) -> Matrix3 { 337 | b.mat 338 | } 339 | } 340 | 341 | impl From> for Quaternion { 342 | #[inline] 343 | fn from(b: Basis3) -> Quaternion { 344 | b.mat.into() 345 | } 346 | } 347 | 348 | impl iter::Product> for Basis3 { 349 | #[inline] 350 | fn product>>(iter: I) -> Basis3 { 351 | iter.fold(Basis3::one(), Mul::mul) 352 | } 353 | } 354 | 355 | impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3> for Basis3 { 356 | #[inline] 357 | fn product>>(iter: I) -> Basis3 { 358 | iter.fold(Basis3::one(), Mul::mul) 359 | } 360 | } 361 | 362 | impl Rotation for Basis3 { 363 | type Space = Point3; 364 | 365 | #[inline] 366 | fn look_at(dir: Vector3, up: Vector3) -> Basis3 { 367 | Basis3 { 368 | mat: Matrix3::look_to_lh(dir, up), 369 | } 370 | } 371 | 372 | #[inline] 373 | fn between_vectors(a: Vector3, b: Vector3) -> Basis3 { 374 | let q: Quaternion = Rotation::between_vectors(a, b); 375 | q.into() 376 | } 377 | 378 | #[inline] 379 | fn rotate_vector(&self, vec: Vector3) -> Vector3 { 380 | self.mat * vec 381 | } 382 | 383 | // TODO: we know the matrix is orthogonal, so this could be re-written 384 | // to be faster 385 | #[inline] 386 | fn invert(&self) -> Basis3 { 387 | Basis3 { 388 | mat: self.mat.invert().unwrap(), 389 | } 390 | } 391 | } 392 | 393 | impl One for Basis3 { 394 | #[inline] 395 | fn one() -> Basis3 { 396 | Basis3 { 397 | mat: Matrix3::one(), 398 | } 399 | } 400 | } 401 | 402 | impl_operator!( Mul > for Basis3 { 403 | fn mul(lhs, rhs) -> Basis3 { Basis3 { mat: lhs.mat * rhs.mat } } 404 | }); 405 | 406 | impl approx::AbsDiffEq for Basis3 { 407 | type Epsilon = S::Epsilon; 408 | 409 | #[inline] 410 | fn default_epsilon() -> S::Epsilon { 411 | S::default_epsilon() 412 | } 413 | 414 | #[inline] 415 | fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { 416 | Matrix3::abs_diff_eq(&self.mat, &other.mat, epsilon) 417 | } 418 | } 419 | 420 | impl approx::RelativeEq for Basis3 { 421 | #[inline] 422 | fn default_max_relative() -> S::Epsilon { 423 | S::default_max_relative() 424 | } 425 | 426 | #[inline] 427 | fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { 428 | Matrix3::relative_eq(&self.mat, &other.mat, epsilon, max_relative) 429 | } 430 | } 431 | 432 | impl approx::UlpsEq for Basis3 { 433 | #[inline] 434 | fn default_max_ulps() -> u32 { 435 | S::default_max_ulps() 436 | } 437 | 438 | #[inline] 439 | fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { 440 | Matrix3::ulps_eq(&self.mat, &other.mat, epsilon, max_ulps) 441 | } 442 | } 443 | 444 | impl Rotation3 for Basis3 { 445 | type Scalar = S; 446 | 447 | fn from_axis_angle>>(axis: Vector3, angle: A) -> Basis3 { 448 | Basis3 { 449 | mat: Matrix3::from_axis_angle(axis, angle), 450 | } 451 | } 452 | 453 | fn from_angle_x>>(theta: A) -> Basis3 { 454 | Basis3 { 455 | mat: Matrix3::from_angle_x(theta), 456 | } 457 | } 458 | 459 | fn from_angle_y>>(theta: A) -> Basis3 { 460 | Basis3 { 461 | mat: Matrix3::from_angle_y(theta), 462 | } 463 | } 464 | 465 | fn from_angle_z>>(theta: A) -> Basis3 { 466 | Basis3 { 467 | mat: Matrix3::from_angle_z(theta), 468 | } 469 | } 470 | } 471 | 472 | impl From> for Basis3 473 | where 474 | A: Into::Unitless>>, 475 | { 476 | /// Create a three-dimensional rotation matrix from a set of euler angles. 477 | fn from(src: Euler) -> Basis3 { 478 | Basis3 { 479 | mat: Matrix3::from(src), 480 | } 481 | } 482 | } 483 | 484 | impl fmt::Debug for Basis3 { 485 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 486 | write!(f, "Basis3 ")?; 487 | <[[S; 3]; 3] as fmt::Debug>::fmt(self.mat.as_ref(), f) 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /src/point.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, 2 | // refer to the Cargo.toml file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Points are fixed positions in affine space with no length or direction. This 17 | //! distinguishes them from vectors, which have a length and direction, but do 18 | //! not have a fixed position. 19 | 20 | use num_traits::{Bounded, Float, NumCast}; 21 | use std::fmt; 22 | use std::mem; 23 | use std::ops::*; 24 | 25 | use structure::*; 26 | 27 | use approx; 28 | use num::{BaseFloat, BaseNum}; 29 | use vector::{Vector1, Vector2, Vector3, Vector4}; 30 | 31 | #[cfg(feature = "mint")] 32 | use mint; 33 | 34 | /// A point in 1-dimensional space. 35 | /// 36 | /// This type is marked as `#[repr(C)]`. 37 | #[repr(C)] 38 | #[derive(PartialEq, Eq, Copy, Clone, Hash)] 39 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 40 | pub struct Point1 { 41 | pub x: S, 42 | } 43 | 44 | /// A point in 2-dimensional space. 45 | /// 46 | /// This type is marked as `#[repr(C)]`. 47 | #[repr(C)] 48 | #[derive(PartialEq, Eq, Copy, Clone, Hash)] 49 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 50 | pub struct Point2 { 51 | pub x: S, 52 | pub y: S, 53 | } 54 | 55 | /// A point in 3-dimensional space. 56 | /// 57 | /// This type is marked as `#[repr(C)]`. 58 | #[repr(C)] 59 | #[derive(PartialEq, Eq, Copy, Clone, Hash)] 60 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 61 | pub struct Point3 { 62 | pub x: S, 63 | pub y: S, 64 | pub z: S, 65 | } 66 | 67 | impl Point3 { 68 | #[inline] 69 | pub fn from_homogeneous(v: Vector4) -> Point3 { 70 | let e = v.truncate() * (S::one() / v.w); 71 | Point3::new(e.x, e.y, e.z) //FIXME 72 | } 73 | 74 | #[inline] 75 | pub fn to_homogeneous(self) -> Vector4 { 76 | Vector4::new(self.x, self.y, self.z, S::one()) 77 | } 78 | } 79 | 80 | macro_rules! impl_point { 81 | ($PointN:ident { $($field:ident),+ }, $VectorN:ident, $n:expr, $constructor:ident) => { 82 | impl $PointN { 83 | /// Construct a new point, using the provided values. 84 | #[inline] 85 | pub const fn new($($field: S),+) -> $PointN { 86 | $PointN { $($field),+ } 87 | } 88 | 89 | /// Perform the given operation on each field in the point, returning a new point 90 | /// constructed from the operations. 91 | #[inline] 92 | pub fn map(self, mut f: F) -> $PointN 93 | where F: FnMut(S) -> U 94 | { 95 | $PointN { $($field: f(self.$field)),+ } 96 | } 97 | 98 | /// Construct a new point where each component is the result of 99 | /// applying the given operation to each pair of components of the 100 | /// given points. 101 | #[inline] 102 | pub fn zip(self, p2: $PointN, mut f: F) -> $PointN 103 | where F: FnMut(S, S2) -> S3 104 | { 105 | $PointN { $($field: f(self.$field, p2.$field)),+ } 106 | } 107 | } 108 | 109 | /// The short constructor. 110 | #[inline] 111 | pub const fn $constructor($($field: S),+) -> $PointN { 112 | $PointN::new($($field),+) 113 | } 114 | 115 | impl Array for $PointN { 116 | type Element = S; 117 | 118 | #[inline] 119 | fn len() -> usize { 120 | $n 121 | } 122 | 123 | #[inline] 124 | fn from_value(scalar: S) -> $PointN { 125 | $PointN { $($field: scalar),+ } 126 | } 127 | 128 | #[inline] 129 | fn sum(self) -> S where S: Add { 130 | fold_array!(add, $(self.$field),+ ) 131 | } 132 | 133 | #[inline] 134 | fn product(self) -> S where S: Mul { 135 | fold_array!(mul, $(self.$field),+ ) 136 | } 137 | 138 | fn is_finite(&self) -> bool where S: Float { 139 | $(self.$field.is_finite())&&+ 140 | } 141 | } 142 | 143 | impl $PointN { 144 | /// Component-wise casting to another type 145 | #[inline] 146 | pub fn cast(&self) -> Option<$PointN> { 147 | $( 148 | let $field = match NumCast::from(self.$field) { 149 | Some(field) => field, 150 | None => return None 151 | }; 152 | )+ 153 | Some($PointN { $($field),+ }) 154 | } 155 | } 156 | 157 | impl MetricSpace for $PointN { 158 | type Metric = S; 159 | 160 | #[inline] 161 | fn distance2(self, other: Self) -> S { 162 | (other - self).magnitude2() 163 | } 164 | } 165 | 166 | impl EuclideanSpace for $PointN { 167 | type Scalar = S; 168 | type Diff = $VectorN; 169 | 170 | #[inline] 171 | fn origin() -> $PointN { 172 | $PointN { $($field: S::zero()),+ } 173 | } 174 | 175 | #[inline] 176 | fn from_vec(v: $VectorN) -> $PointN { 177 | $PointN::new($(v.$field),+) 178 | } 179 | 180 | #[inline] 181 | fn to_vec(self) -> $VectorN { 182 | $VectorN::new($(self.$field),+) 183 | } 184 | 185 | #[inline] 186 | fn dot(self, v: $VectorN) -> S { 187 | $VectorN::new($(self.$field * v.$field),+).sum() 188 | } 189 | } 190 | 191 | impl approx::AbsDiffEq for $PointN { 192 | type Epsilon = S::Epsilon; 193 | 194 | #[inline] 195 | fn default_epsilon() -> S::Epsilon { 196 | S::default_epsilon() 197 | } 198 | 199 | #[inline] 200 | fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) 201 | -> bool 202 | { 203 | $(S::abs_diff_eq(&self.$field, &other.$field, epsilon))&&+ 204 | } 205 | } 206 | 207 | impl approx::RelativeEq for $PointN { 208 | #[inline] 209 | fn default_max_relative() -> S::Epsilon { 210 | S::default_max_relative() 211 | } 212 | 213 | #[inline] 214 | fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { 215 | $(S::relative_eq(&self.$field, &other.$field, epsilon, max_relative))&&+ 216 | } 217 | } 218 | 219 | impl approx::UlpsEq for $PointN { 220 | #[inline] 221 | fn default_max_ulps() -> u32 { 222 | S::default_max_ulps() 223 | } 224 | 225 | #[inline] 226 | fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { 227 | $(S::ulps_eq(&self.$field, &other.$field, epsilon, max_ulps))&&+ 228 | } 229 | } 230 | 231 | impl Bounded for $PointN { 232 | #[inline] 233 | fn min_value() -> $PointN { 234 | $PointN { $($field: S::min_value()),+ } 235 | } 236 | 237 | #[inline] 238 | fn max_value() -> $PointN { 239 | $PointN { $($field: S::max_value()),+ } 240 | } 241 | } 242 | 243 | impl_operator!( Add<$VectorN > for $PointN { 244 | fn add(lhs, rhs) -> $PointN { $PointN::new($(lhs.$field + rhs.$field),+) } 245 | }); 246 | impl_operator!( Sub<$VectorN> for $PointN { 247 | fn sub(lhs, rhs) -> $PointN { $PointN::new($(lhs.$field - rhs.$field),+) } 248 | }); 249 | impl_assignment_operator!( AddAssign<$VectorN > for $PointN { 250 | fn add_assign(&mut self, vector) { $(self.$field += vector.$field);+ } 251 | }); 252 | impl_assignment_operator!( SubAssign<$VectorN> for $PointN { 253 | fn sub_assign(&mut self, vector) { $(self.$field -= vector.$field);+ } 254 | }); 255 | 256 | impl_operator!( Sub<$PointN > for $PointN { 257 | fn sub(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field - rhs.$field),+) } 258 | }); 259 | 260 | impl_operator!( Mul for $PointN { 261 | fn mul(point, scalar) -> $PointN { $PointN::new($(point.$field * scalar),+) } 262 | }); 263 | impl_operator!( Div for $PointN { 264 | fn div(point, scalar) -> $PointN { $PointN::new($(point.$field / scalar),+) } 265 | }); 266 | impl_operator!( Rem for $PointN { 267 | fn rem(point, scalar) -> $PointN { $PointN::new($(point.$field % scalar),+) } 268 | }); 269 | impl_assignment_operator!( MulAssign for $PointN { 270 | fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ } 271 | }); 272 | impl_assignment_operator!( DivAssign for $PointN { 273 | fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ } 274 | }); 275 | impl_assignment_operator!( RemAssign for $PointN { 276 | fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ } 277 | }); 278 | 279 | impl ElementWise for $PointN { 280 | #[inline] fn add_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field + rhs.$field),+) } 281 | #[inline] fn sub_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field - rhs.$field),+) } 282 | #[inline] fn mul_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field * rhs.$field),+) } 283 | #[inline] fn div_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field / rhs.$field),+) } 284 | #[inline] fn rem_element_wise(self, rhs: $PointN) -> $PointN { $PointN::new($(self.$field % rhs.$field),+) } 285 | 286 | #[inline] fn add_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field += rhs.$field);+ } 287 | #[inline] fn sub_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field -= rhs.$field);+ } 288 | #[inline] fn mul_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field *= rhs.$field);+ } 289 | #[inline] fn div_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field /= rhs.$field);+ } 290 | #[inline] fn rem_assign_element_wise(&mut self, rhs: $PointN) { $(self.$field %= rhs.$field);+ } 291 | } 292 | 293 | impl ElementWise for $PointN { 294 | #[inline] fn add_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field + rhs),+) } 295 | #[inline] fn sub_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field - rhs),+) } 296 | #[inline] fn mul_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field * rhs),+) } 297 | #[inline] fn div_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field / rhs),+) } 298 | #[inline] fn rem_element_wise(self, rhs: S) -> $PointN { $PointN::new($(self.$field % rhs),+) } 299 | 300 | #[inline] fn add_assign_element_wise(&mut self, rhs: S) { $(self.$field += rhs);+ } 301 | #[inline] fn sub_assign_element_wise(&mut self, rhs: S) { $(self.$field -= rhs);+ } 302 | #[inline] fn mul_assign_element_wise(&mut self, rhs: S) { $(self.$field *= rhs);+ } 303 | #[inline] fn div_assign_element_wise(&mut self, rhs: S) { $(self.$field /= rhs);+ } 304 | #[inline] fn rem_assign_element_wise(&mut self, rhs: S) { $(self.$field %= rhs);+ } 305 | } 306 | 307 | impl_scalar_ops!($PointN { $($field),+ }); 308 | impl_scalar_ops!($PointN { $($field),+ }); 309 | impl_scalar_ops!($PointN { $($field),+ }); 310 | impl_scalar_ops!($PointN { $($field),+ }); 311 | impl_scalar_ops!($PointN { $($field),+ }); 312 | impl_scalar_ops!($PointN { $($field),+ }); 313 | impl_scalar_ops!($PointN { $($field),+ }); 314 | impl_scalar_ops!($PointN { $($field),+ }); 315 | impl_scalar_ops!($PointN { $($field),+ }); 316 | impl_scalar_ops!($PointN { $($field),+ }); 317 | impl_scalar_ops!($PointN { $($field),+ }); 318 | impl_scalar_ops!($PointN { $($field),+ }); 319 | 320 | impl_index_operators!($PointN, $n, S, usize); 321 | impl_index_operators!($PointN, $n, [S], Range); 322 | impl_index_operators!($PointN, $n, [S], RangeTo); 323 | impl_index_operators!($PointN, $n, [S], RangeFrom); 324 | impl_index_operators!($PointN, $n, [S], RangeFull); 325 | } 326 | } 327 | 328 | macro_rules! impl_scalar_ops { 329 | ($PointN:ident<$S:ident> { $($field:ident),+ }) => { 330 | impl_operator!(Mul<$PointN<$S>> for $S { 331 | fn mul(scalar, point) -> $PointN<$S> { $PointN::new($(scalar * point.$field),+) } 332 | }); 333 | impl_operator!(Div<$PointN<$S>> for $S { 334 | fn div(scalar, point) -> $PointN<$S> { $PointN::new($(scalar / point.$field),+) } 335 | }); 336 | impl_operator!(Rem<$PointN<$S>> for $S { 337 | fn rem(scalar, point) -> $PointN<$S> { $PointN::new($(scalar % point.$field),+) } 338 | }); 339 | }; 340 | } 341 | 342 | impl_point!(Point1 { x }, Vector1, 1, point1); 343 | impl_point!(Point2 { x, y }, Vector2, 2, point2); 344 | impl_point!(Point3 { x, y, z }, Vector3, 3, point3); 345 | 346 | impl Point1 { 347 | impl_swizzle_functions!(Point1, Point2, Point3, S, x); 348 | } 349 | 350 | impl Point2 { 351 | impl_swizzle_functions!(Point1, Point2, Point3, S, xy); 352 | } 353 | 354 | impl Point3 { 355 | impl_swizzle_functions!(Point1, Point2, Point3, S, xyz); 356 | } 357 | 358 | impl_fixed_array_conversions!(Point1 { x: 0 }, 1); 359 | impl_fixed_array_conversions!(Point2 { x: 0, y: 1 }, 2); 360 | impl_fixed_array_conversions!(Point3 { x: 0, y: 1, z: 2 }, 3); 361 | 362 | impl_tuple_conversions!(Point1 { x }, (S,)); 363 | impl_tuple_conversions!(Point2 { x, y }, (S, S)); 364 | impl_tuple_conversions!(Point3 { x, y, z }, (S, S, S)); 365 | 366 | #[cfg(feature = "mint")] 367 | impl_mint_conversions!(Point2 { x, y }, Point2); 368 | #[cfg(feature = "mint")] 369 | impl_mint_conversions!(Point3 { x, y, z }, Point3); 370 | 371 | #[cfg(feature = "bytemuck")] 372 | impl_bytemuck_cast!(Point1); 373 | #[cfg(feature = "bytemuck")] 374 | impl_bytemuck_cast!(Point2); 375 | #[cfg(feature = "bytemuck")] 376 | impl_bytemuck_cast!(Point3); 377 | 378 | impl fmt::Debug for Point1 { 379 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 380 | write!(f, "Point1 ")?; 381 | <[S; 1] as fmt::Debug>::fmt(self.as_ref(), f) 382 | } 383 | } 384 | 385 | impl fmt::Debug for Point2 { 386 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 387 | write!(f, "Point2 ")?; 388 | <[S; 2] as fmt::Debug>::fmt(self.as_ref(), f) 389 | } 390 | } 391 | 392 | impl fmt::Debug for Point3 { 393 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 394 | write!(f, "Point3 ")?; 395 | <[S; 3] as fmt::Debug>::fmt(self.as_ref(), f) 396 | } 397 | } 398 | 399 | #[cfg(test)] 400 | mod tests { 401 | mod point2 { 402 | use point::*; 403 | 404 | const POINT2: Point2 = Point2 { x: 1, y: 2 }; 405 | 406 | #[test] 407 | fn test_index() { 408 | assert_eq!(POINT2[0], POINT2.x); 409 | assert_eq!(POINT2[1], POINT2.y); 410 | } 411 | 412 | #[test] 413 | fn test_index_mut() { 414 | let mut p = POINT2; 415 | p[0] = 0; 416 | assert_eq!(p, [0, 2].into()); 417 | } 418 | 419 | #[test] 420 | #[should_panic] 421 | fn test_index_out_of_bounds() { 422 | POINT2[2]; 423 | } 424 | 425 | #[test] 426 | fn test_index_range() { 427 | assert_eq!(&POINT2[..0], &[]); 428 | assert_eq!(&POINT2[..1], &[1]); 429 | assert_eq!(POINT2[..0].len(), 0); 430 | assert_eq!(POINT2[..1].len(), 1); 431 | assert_eq!(&POINT2[2..], &[]); 432 | assert_eq!(&POINT2[1..], &[2]); 433 | assert_eq!(POINT2[2..].len(), 0); 434 | assert_eq!(POINT2[1..].len(), 1); 435 | assert_eq!(&POINT2[..], &[1, 2]); 436 | assert_eq!(POINT2[..].len(), 2); 437 | } 438 | 439 | #[test] 440 | fn test_into() { 441 | let p = POINT2; 442 | { 443 | let p: [i32; 2] = p.into(); 444 | assert_eq!(p, [1, 2]); 445 | } 446 | { 447 | let p: (i32, i32) = p.into(); 448 | assert_eq!(p, (1, 2)); 449 | } 450 | } 451 | 452 | #[test] 453 | fn test_as_ref() { 454 | let p = POINT2; 455 | { 456 | let p: &[i32; 2] = p.as_ref(); 457 | assert_eq!(p, &[1, 2]); 458 | } 459 | { 460 | let p: &(i32, i32) = p.as_ref(); 461 | assert_eq!(p, &(1, 2)); 462 | } 463 | } 464 | 465 | #[test] 466 | fn test_as_mut() { 467 | let mut p = POINT2; 468 | { 469 | let p: &mut [i32; 2] = p.as_mut(); 470 | assert_eq!(p, &mut [1, 2]); 471 | } 472 | { 473 | let p: &mut (i32, i32) = p.as_mut(); 474 | assert_eq!(p, &mut (1, 2)); 475 | } 476 | } 477 | 478 | #[test] 479 | fn test_from() { 480 | assert_eq!(Point2::from([1, 2]), POINT2); 481 | { 482 | let p = &[1, 2]; 483 | let p: &Point2<_> = From::from(p); 484 | assert_eq!(p, &POINT2); 485 | } 486 | { 487 | let p = &mut [1, 2]; 488 | let p: &mut Point2<_> = From::from(p); 489 | assert_eq!(p, &POINT2); 490 | } 491 | assert_eq!(Point2::from((1, 2)), POINT2); 492 | { 493 | let p = &(1, 2); 494 | let p: &Point2<_> = From::from(p); 495 | assert_eq!(p, &POINT2); 496 | } 497 | { 498 | let p = &mut (1, 2); 499 | let p: &mut Point2<_> = From::from(p); 500 | assert_eq!(p, &POINT2); 501 | } 502 | } 503 | 504 | #[test] 505 | fn test_zip() { 506 | assert_eq!( 507 | Point2::new(true, false), 508 | Point2::new(-2, 1).zip(Point2::new(-1, -1), |a, b| a < b) 509 | ); 510 | } 511 | } 512 | 513 | mod point3 { 514 | use point::*; 515 | 516 | const POINT3: Point3 = Point3 { x: 1, y: 2, z: 3 }; 517 | 518 | #[test] 519 | fn test_index() { 520 | assert_eq!(POINT3[0], POINT3.x); 521 | assert_eq!(POINT3[1], POINT3.y); 522 | assert_eq!(POINT3[2], POINT3.z); 523 | } 524 | 525 | #[test] 526 | fn test_index_mut() { 527 | let mut p = POINT3; 528 | p[1] = 0; 529 | assert_eq!(p, [1, 0, 3].into()); 530 | } 531 | 532 | #[test] 533 | #[should_panic] 534 | fn test_index_out_of_bounds() { 535 | POINT3[3]; 536 | } 537 | 538 | #[test] 539 | fn test_index_range() { 540 | assert_eq!(&POINT3[..1], &[1]); 541 | assert_eq!(&POINT3[..2], &[1, 2]); 542 | assert_eq!(POINT3[..1].len(), 1); 543 | assert_eq!(POINT3[..2].len(), 2); 544 | assert_eq!(&POINT3[2..], &[3]); 545 | assert_eq!(&POINT3[1..], &[2, 3]); 546 | assert_eq!(POINT3[2..].len(), 1); 547 | assert_eq!(POINT3[1..].len(), 2); 548 | assert_eq!(&POINT3[..], &[1, 2, 3]); 549 | assert_eq!(POINT3[..].len(), 3); 550 | } 551 | 552 | #[test] 553 | fn test_into() { 554 | let p = POINT3; 555 | { 556 | let p: [i32; 3] = p.into(); 557 | assert_eq!(p, [1, 2, 3]); 558 | } 559 | { 560 | let p: (i32, i32, i32) = p.into(); 561 | assert_eq!(p, (1, 2, 3)); 562 | } 563 | } 564 | 565 | #[test] 566 | fn test_as_ref() { 567 | let p = POINT3; 568 | { 569 | let p: &[i32; 3] = p.as_ref(); 570 | assert_eq!(p, &[1, 2, 3]); 571 | } 572 | { 573 | let p: &(i32, i32, i32) = p.as_ref(); 574 | assert_eq!(p, &(1, 2, 3)); 575 | } 576 | } 577 | 578 | #[test] 579 | fn test_as_mut() { 580 | let mut p = POINT3; 581 | { 582 | let p: &mut [i32; 3] = p.as_mut(); 583 | assert_eq!(p, &mut [1, 2, 3]); 584 | } 585 | { 586 | let p: &mut (i32, i32, i32) = p.as_mut(); 587 | assert_eq!(p, &mut (1, 2, 3)); 588 | } 589 | } 590 | 591 | #[test] 592 | fn test_from() { 593 | assert_eq!(Point3::from([1, 2, 3]), POINT3); 594 | { 595 | let p = &[1, 2, 3]; 596 | let p: &Point3<_> = From::from(p); 597 | assert_eq!(p, &POINT3); 598 | } 599 | { 600 | let p = &mut [1, 2, 3]; 601 | let p: &mut Point3<_> = From::from(p); 602 | assert_eq!(p, &POINT3); 603 | } 604 | assert_eq!(Point3::from((1, 2, 3)), POINT3); 605 | { 606 | let p = &(1, 2, 3); 607 | let p: &Point3<_> = From::from(p); 608 | assert_eq!(p, &POINT3); 609 | } 610 | { 611 | let p = &mut (1, 2, 3); 612 | let p: &mut Point3<_> = From::from(p); 613 | assert_eq!(p, &POINT3); 614 | } 615 | } 616 | 617 | #[test] 618 | fn test_zip() { 619 | assert_eq!( 620 | Point3::new(true, false, false), 621 | Point3::new(-2, 1, 0).zip(Point3::new(-1, -1, -1), |a, b| a < b) 622 | ); 623 | } 624 | } 625 | } 626 | --------------------------------------------------------------------------------