commit
863d21be9a
25 changed files with 1423 additions and 677 deletions
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -6,6 +6,21 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [v0.16.0] - 2018-01-03
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add `InnerSpace::project_on`
|
||||||
|
- Add `Array::len`
|
||||||
|
- Re-export `Bounded` and implement for vectors, points, and angles
|
||||||
|
- Add vector subtraction to `EuclideanSpace`
|
||||||
|
- Add swizzle functions behinde that `"swizzle"` feature
|
||||||
|
- Add `Matrix4::look_at_dir`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Return `Option` from cast functions
|
||||||
|
|
||||||
## [v0.15.0] - 2017-07-30
|
## [v0.15.0] - 2017-07-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -274,7 +289,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
## v0.0.1 - 2014-06-24
|
## v0.0.1 - 2014-06-24
|
||||||
|
|
||||||
[Unreleased]: https://github.com/brendanzab/cgmath/compare/v0.15.0...HEAD
|
[Unreleased]: https://github.com/brendanzab/cgmath/compare/v0.16.0...HEAD
|
||||||
|
[v0.16.0]: https://github.com/brendanzab/cgmath/compare/v0.15.0...v0.16.0
|
||||||
[v0.15.0]: https://github.com/brendanzab/cgmath/compare/v0.14.1...v0.15.0
|
[v0.15.0]: https://github.com/brendanzab/cgmath/compare/v0.14.1...v0.15.0
|
||||||
[v0.14.1]: https://github.com/brendanzab/cgmath/compare/v0.14.0...v0.14.1
|
[v0.14.1]: https://github.com/brendanzab/cgmath/compare/v0.14.0...v0.14.1
|
||||||
[v0.14.0]: https://github.com/brendanzab/cgmath/compare/v0.13.1...v0.14.0
|
[v0.14.0]: https://github.com/brendanzab/cgmath/compare/v0.13.1...v0.14.0
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
|
|
||||||
name = "cgmath"
|
name = "cgmath"
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
authors = ["Brendan Zabarauskas <bjzaba@yahoo.com.au>"]
|
authors = ["Brendan Zabarauskas <bjzaba@yahoo.com.au>"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
description = "A linear algebra and mathematics library for computer graphics."
|
description = "A linear algebra and mathematics library for computer graphics."
|
||||||
|
@ -22,12 +22,12 @@ swizzle = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
approx = "0.1"
|
approx = "0.1"
|
||||||
mint = { version = "0.4.1", optional = true }
|
mint = { version = "0.5", optional = true }
|
||||||
num-traits = "0.1"
|
num-traits = "0.1"
|
||||||
rand = "0.4"
|
rand = "0.4"
|
||||||
serde = { version = "1.0", features = ["serde_derive"], optional = true }
|
serde = { version = "1.0", features = ["serde_derive"], optional = true }
|
||||||
simd = { version = "0.2", optional = true }
|
simd = { version = "0.2", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
glium = "0.17"
|
glium = "0.19"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
|
@ -15,16 +15,17 @@
|
||||||
|
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate cgmath;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
extern crate cgmath;
|
|
||||||
|
|
||||||
use rand::{IsaacRng, Rng};
|
use rand::{IsaacRng, Rng};
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
use cgmath::*;
|
use cgmath::*;
|
||||||
|
|
||||||
#[path="common/macros.rs"]
|
#[path = "common/macros.rs"]
|
||||||
#[macro_use] mod macros;
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
fn bench_from_axis_angle<T: Rotation3<f32>>(bh: &mut Bencher) {
|
fn bench_from_axis_angle<T: Rotation3<f32>>(bh: &mut Bencher) {
|
||||||
const LEN: usize = 1 << 13;
|
const LEN: usize = 1 << 13;
|
||||||
|
@ -39,7 +40,8 @@ fn bench_from_axis_angle<T: Rotation3<f32>>(bh: &mut Bencher) {
|
||||||
i = (i + 1) & (LEN - 1);
|
i = (i + 1) & (LEN - 1);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let res: T = Rotation3::from_axis_angle(*axis.get_unchecked(i), *angle.get_unchecked(i));
|
let res: T =
|
||||||
|
Rotation3::from_axis_angle(*axis.get_unchecked(i), *angle.get_unchecked(i));
|
||||||
test::black_box(res)
|
test::black_box(res)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -55,7 +57,19 @@ fn _bench_rot3_from_axisangle(bh: &mut Bencher) {
|
||||||
bench_from_axis_angle::<Basis3<f32>>(bh)
|
bench_from_axis_angle::<Basis3<f32>>(bh)
|
||||||
}
|
}
|
||||||
|
|
||||||
bench_construction!(_bench_rot2_from_axisangle, Basis2<f32>, Basis2::from_angle [ angle: Rad<f32> ]);
|
bench_construction!(
|
||||||
|
_bench_rot2_from_axisangle,
|
||||||
|
Basis2<f32>,
|
||||||
|
Basis2::from_angle[angle: Rad<f32>]
|
||||||
|
);
|
||||||
|
|
||||||
bench_construction!(_bench_quat_from_euler_angles, Quaternion<f32>, Quaternion::from [src: Euler<Rad<f32>>]);
|
bench_construction!(
|
||||||
bench_construction!(_bench_rot3_from_euler_angles, Basis3<f32>, Basis3::from [src: Euler<Rad<f32>>]);
|
_bench_quat_from_euler_angles,
|
||||||
|
Quaternion<f32>,
|
||||||
|
Quaternion::from[src: Euler<Rad<f32>>]
|
||||||
|
);
|
||||||
|
bench_construction!(
|
||||||
|
_bench_rot3_from_euler_angles,
|
||||||
|
Basis3<f32>,
|
||||||
|
Basis3::from[src: Euler<Rad<f32>>]
|
||||||
|
);
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
|
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate cgmath;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
extern crate cgmath;
|
|
||||||
|
|
||||||
use rand::{IsaacRng, Rng};
|
use rand::{IsaacRng, Rng};
|
||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
|
@ -25,8 +25,9 @@ use test::Bencher;
|
||||||
|
|
||||||
use cgmath::*;
|
use cgmath::*;
|
||||||
|
|
||||||
#[path="common/macros.rs"]
|
#[path = "common/macros.rs"]
|
||||||
#[macro_use] mod macros;
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
bench_binop!(_bench_matrix2_mul_m, Matrix2<f32>, Matrix2<f32>, mul);
|
bench_binop!(_bench_matrix2_mul_m, Matrix2<f32>, Matrix2<f32>, mul);
|
||||||
bench_binop!(_bench_matrix3_mul_m, Matrix3<f32>, Matrix3<f32>, mul);
|
bench_binop!(_bench_matrix3_mul_m, Matrix3<f32>, Matrix3<f32>, mul);
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
|
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate cgmath;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
extern crate cgmath;
|
|
||||||
|
|
||||||
use rand::{IsaacRng, Rng};
|
use rand::{IsaacRng, Rng};
|
||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
|
@ -25,8 +25,9 @@ use test::Bencher;
|
||||||
|
|
||||||
use cgmath::*;
|
use cgmath::*;
|
||||||
|
|
||||||
#[path="common/macros.rs"]
|
#[path = "common/macros.rs"]
|
||||||
#[macro_use] mod macros;
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
bench_binop!(_bench_quat_add_q, Quaternion<f32>, Quaternion<f32>, add);
|
bench_binop!(_bench_quat_add_q, Quaternion<f32>, Quaternion<f32>, add);
|
||||||
bench_binop!(_bench_quat_sub_q, Quaternion<f32>, Quaternion<f32>, sub);
|
bench_binop!(_bench_quat_sub_q, Quaternion<f32>, Quaternion<f32>, sub);
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
|
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate cgmath;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
extern crate cgmath;
|
|
||||||
|
|
||||||
use rand::{IsaacRng, Rng};
|
use rand::{IsaacRng, Rng};
|
||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
|
@ -25,8 +25,9 @@ use test::Bencher;
|
||||||
|
|
||||||
use cgmath::*;
|
use cgmath::*;
|
||||||
|
|
||||||
#[path="common/macros.rs"]
|
#[path = "common/macros.rs"]
|
||||||
#[macro_use] mod macros;
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
bench_binop!(_bench_vector2_add_v, Vector2<f32>, Vector2<f32>, add);
|
bench_binop!(_bench_vector2_add_v, Vector2<f32>, Vector2<f32>, add);
|
||||||
bench_binop!(_bench_vector3_add_v, Vector3<f32>, Vector3<f32>, add);
|
bench_binop!(_bench_vector3_add_v, Vector3<f32>, Vector3<f32>, add);
|
||||||
|
|
10
src/angle.rs
10
src/angle.rs
|
@ -45,14 +45,20 @@ pub struct Rad<S>(pub S);
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct Deg<S>(pub S);
|
pub struct Deg<S>(pub S);
|
||||||
|
|
||||||
impl<S> From<Rad<S>> for Deg<S> where S: BaseFloat {
|
impl<S> From<Rad<S>> for Deg<S>
|
||||||
|
where
|
||||||
|
S: BaseFloat,
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(rad: Rad<S>) -> Deg<S> {
|
fn from(rad: Rad<S>) -> Deg<S> {
|
||||||
Deg(rad.0 * cast(180.0 / f64::consts::PI).unwrap())
|
Deg(rad.0 * cast(180.0 / f64::consts::PI).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> From<Deg<S>> for Rad<S> where S: BaseFloat {
|
impl<S> From<Deg<S>> for Rad<S>
|
||||||
|
where
|
||||||
|
S: BaseFloat,
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(deg: Deg<S>) -> Rad<S> {
|
fn from(deg: Deg<S>) -> Rad<S> {
|
||||||
Rad(deg.0 * cast(f64::consts::PI / 180.0).unwrap())
|
Rad(deg.0 * cast(f64::consts::PI / 180.0).unwrap())
|
||||||
|
|
21
src/euler.rs
21
src/euler.rs
|
@ -75,8 +75,7 @@ use num::BaseFloat;
|
||||||
/// [gimbal lock]: https://en.wikipedia.org/wiki/Gimbal_lock#Gimbal_lock_in_applied_mathematics
|
/// [gimbal lock]: https://en.wikipedia.org/wiki/Gimbal_lock#Gimbal_lock_in_applied_mathematics
|
||||||
/// [convert]: #defining-rotations-using-euler-angles
|
/// [convert]: #defining-rotations-using-euler-angles
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
#[derive(PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct Euler<A: Angle> {
|
pub struct Euler<A: Angle> {
|
||||||
/// The angle to apply around the _x_ axis. Also known at the _pitch_.
|
/// The angle to apply around the _x_ axis. Also known at the _pitch_.
|
||||||
|
@ -162,23 +161,27 @@ impl<A: Angle> ApproxEq for Euler<A> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn relative_eq(&self, other: &Self, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool {
|
fn relative_eq(&self, other: &Self, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool {
|
||||||
A::relative_eq(&self.x, &other.x, epsilon, max_relative) &&
|
A::relative_eq(&self.x, &other.x, epsilon, max_relative)
|
||||||
A::relative_eq(&self.y, &other.y, epsilon, max_relative) &&
|
&& A::relative_eq(&self.y, &other.y, epsilon, max_relative)
|
||||||
A::relative_eq(&self.z, &other.z, epsilon, max_relative)
|
&& A::relative_eq(&self.z, &other.z, epsilon, max_relative)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ulps_eq(&self, other: &Self, epsilon: A::Epsilon, max_ulps: u32) -> bool {
|
fn ulps_eq(&self, other: &Self, epsilon: A::Epsilon, max_ulps: u32) -> bool {
|
||||||
A::ulps_eq(&self.x, &other.x, epsilon, max_ulps) &&
|
A::ulps_eq(&self.x, &other.x, epsilon, max_ulps)
|
||||||
A::ulps_eq(&self.y, &other.y, epsilon, max_ulps) &&
|
&& A::ulps_eq(&self.y, &other.y, epsilon, max_ulps)
|
||||||
A::ulps_eq(&self.z, &other.z, epsilon, max_ulps)
|
&& A::ulps_eq(&self.z, &other.z, epsilon, max_ulps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Angle + Rand> Rand for Euler<A> {
|
impl<A: Angle + Rand> Rand for Euler<A> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rand<R: Rng>(rng: &mut R) -> Euler<A> {
|
fn rand<R: Rng>(rng: &mut R) -> Euler<A> {
|
||||||
Euler { x: rng.gen(), y: rng.gen(), z: rng.gen() }
|
Euler {
|
||||||
|
x: rng.gen(),
|
||||||
|
y: rng.gen(),
|
||||||
|
z: rng.gen(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,8 @@ extern crate approx;
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
pub extern crate mint;
|
pub extern crate mint;
|
||||||
|
|
||||||
pub extern crate num_traits;
|
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
pub extern crate num_traits;
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -76,7 +76,7 @@ pub use structure::*;
|
||||||
|
|
||||||
pub use matrix::{Matrix2, Matrix3, Matrix4};
|
pub use matrix::{Matrix2, Matrix3, Matrix4};
|
||||||
pub use quaternion::Quaternion;
|
pub use quaternion::Quaternion;
|
||||||
pub use vector::{Vector1, Vector2, Vector3, Vector4, dot, vec1, vec2, vec3, vec4};
|
pub use vector::{dot, Vector1, Vector2, Vector3, Vector4, vec1, vec2, vec3, vec4};
|
||||||
|
|
||||||
pub use angle::{Deg, Rad};
|
pub use angle::{Deg, Rad};
|
||||||
pub use euler::Euler;
|
pub use euler::Euler;
|
||||||
|
|
793
src/matrix.rs
793
src/matrix.rs
File diff suppressed because it is too large
Load diff
36
src/num.rs
36
src/num.rs
|
@ -21,11 +21,41 @@ use std::ops::*;
|
||||||
use num_traits::{Float, Num, NumCast};
|
use num_traits::{Float, Num, NumCast};
|
||||||
|
|
||||||
/// Base numeric types with partial ordering
|
/// Base numeric types with partial ordering
|
||||||
pub trait BaseNum: Copy + Clone + fmt::Debug + Num + NumCast + PartialOrd + AddAssign + SubAssign + MulAssign + DivAssign + RemAssign {}
|
pub trait BaseNum
|
||||||
|
: Copy
|
||||||
|
+ Clone
|
||||||
|
+ fmt::Debug
|
||||||
|
+ Num
|
||||||
|
+ NumCast
|
||||||
|
+ PartialOrd
|
||||||
|
+ AddAssign
|
||||||
|
+ SubAssign
|
||||||
|
+ MulAssign
|
||||||
|
+ DivAssign
|
||||||
|
+ RemAssign {
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> BaseNum for T where T: Copy + Clone + fmt::Debug + Num + NumCast + PartialOrd + AddAssign + SubAssign + MulAssign + DivAssign + RemAssign {}
|
impl<T> BaseNum for T
|
||||||
|
where
|
||||||
|
T: Copy
|
||||||
|
+ Clone
|
||||||
|
+ fmt::Debug
|
||||||
|
+ Num
|
||||||
|
+ NumCast
|
||||||
|
+ PartialOrd
|
||||||
|
+ AddAssign
|
||||||
|
+ SubAssign
|
||||||
|
+ MulAssign
|
||||||
|
+ DivAssign
|
||||||
|
+ RemAssign,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// Base floating point types
|
/// Base floating point types
|
||||||
pub trait BaseFloat: BaseNum + Float + ApproxEq<Epsilon = Self> {}
|
pub trait BaseFloat: BaseNum + Float + ApproxEq<Epsilon = Self> {}
|
||||||
|
|
||||||
impl<T> BaseFloat for T where T: BaseNum + Float + ApproxEq<Epsilon = Self> {}
|
impl<T> BaseFloat for T
|
||||||
|
where
|
||||||
|
T: BaseNum + Float + ApproxEq<Epsilon = Self>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
//! distinguishes them from vectors, which have a length and direction, but do
|
//! distinguishes them from vectors, which have a length and direction, but do
|
||||||
//! not have a fixed position.
|
//! not have a fixed position.
|
||||||
|
|
||||||
use num_traits::{NumCast, Bounded};
|
use num_traits::{Bounded, NumCast};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
|
@ -25,7 +25,7 @@ use std::ops::*;
|
||||||
use structure::*;
|
use structure::*;
|
||||||
|
|
||||||
use approx::ApproxEq;
|
use approx::ApproxEq;
|
||||||
use num::{BaseNum, BaseFloat};
|
use num::{BaseFloat, BaseNum};
|
||||||
use vector::{Vector1, Vector2, Vector3, Vector4};
|
use vector::{Vector1, Vector2, Vector3, Vector4};
|
||||||
|
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
|
@ -95,7 +95,7 @@ impl<S: BaseNum> Point3<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_homogeneous(v: Vector4<S>) -> Point3<S> {
|
pub fn from_homogeneous(v: Vector4<S>) -> Point3<S> {
|
||||||
let e = v.truncate() * (S::one() / v.w);
|
let e = v.truncate() * (S::one() / v.w);
|
||||||
Point3::new(e.x, e.y, e.z) //FIXME
|
Point3::new(e.x, e.y, e.z) //FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use num_traits::{Zero};
|
use num_traits::Zero;
|
||||||
use num_traits::cast;
|
use num_traits::cast;
|
||||||
|
|
||||||
use structure::Angle;
|
use structure::Angle;
|
||||||
|
@ -26,12 +26,17 @@ use num::BaseFloat;
|
||||||
///
|
///
|
||||||
/// This is the equivalent to the [gluPerspective]
|
/// This is the equivalent to the [gluPerspective]
|
||||||
/// (http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml) function.
|
/// (http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml) function.
|
||||||
pub fn perspective<S: BaseFloat, A: Into<Rad<S>>>(fovy: A, aspect: S, near: S, far: S) -> Matrix4<S> {
|
pub fn perspective<S: BaseFloat, A: Into<Rad<S>>>(
|
||||||
|
fovy: A,
|
||||||
|
aspect: S,
|
||||||
|
near: S,
|
||||||
|
far: S,
|
||||||
|
) -> Matrix4<S> {
|
||||||
PerspectiveFov {
|
PerspectiveFov {
|
||||||
fovy: fovy.into(),
|
fovy: fovy.into(),
|
||||||
aspect: aspect,
|
aspect: aspect,
|
||||||
near: near,
|
near: near,
|
||||||
far: far,
|
far: far,
|
||||||
}.into()
|
}.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,12 +46,12 @@ pub fn perspective<S: BaseFloat, A: Into<Rad<S>>>(fovy: A, aspect: S, near: S, f
|
||||||
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml) function.
|
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml) function.
|
||||||
pub fn frustum<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
pub fn frustum<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
||||||
Perspective {
|
Perspective {
|
||||||
left: left,
|
left: left,
|
||||||
right: right,
|
right: right,
|
||||||
bottom: bottom,
|
bottom: bottom,
|
||||||
top: top,
|
top: top,
|
||||||
near: near,
|
near: near,
|
||||||
far: far,
|
far: far,
|
||||||
}.into()
|
}.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,12 +61,12 @@ pub fn frustum<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far:
|
||||||
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml) function.
|
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml) function.
|
||||||
pub fn ortho<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
pub fn ortho<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
||||||
Ortho {
|
Ortho {
|
||||||
left: left,
|
left: left,
|
||||||
right: right,
|
right: right,
|
||||||
bottom: bottom,
|
bottom: bottom,
|
||||||
top: top,
|
top: top,
|
||||||
near: near,
|
near: near,
|
||||||
far: far,
|
far: far,
|
||||||
}.into()
|
}.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,10 +75,10 @@ pub fn ortho<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S
|
||||||
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
|
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct PerspectiveFov<S> {
|
pub struct PerspectiveFov<S> {
|
||||||
pub fovy: Rad<S>,
|
pub fovy: Rad<S>,
|
||||||
pub aspect: S,
|
pub aspect: S,
|
||||||
pub near: S,
|
pub near: S,
|
||||||
pub far: S,
|
pub far: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> PerspectiveFov<S> {
|
impl<S: BaseFloat> PerspectiveFov<S> {
|
||||||
|
@ -84,24 +89,49 @@ impl<S: BaseFloat> PerspectiveFov<S> {
|
||||||
let xmax = ymax * self.aspect;
|
let xmax = ymax * self.aspect;
|
||||||
|
|
||||||
Perspective {
|
Perspective {
|
||||||
left: -xmax,
|
left: -xmax,
|
||||||
right: xmax,
|
right: xmax,
|
||||||
bottom: -ymax,
|
bottom: -ymax,
|
||||||
top: ymax,
|
top: ymax,
|
||||||
near: self.near.clone(),
|
near: self.near.clone(),
|
||||||
far: self.far.clone(),
|
far: self.far.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> From<PerspectiveFov<S>> for Matrix4<S> {
|
impl<S: BaseFloat> From<PerspectiveFov<S>> for Matrix4<S> {
|
||||||
fn from(persp: PerspectiveFov<S>) -> Matrix4<S> {
|
fn from(persp: PerspectiveFov<S>) -> Matrix4<S> {
|
||||||
assert!(persp.fovy > Rad::zero(), "The vertical field of view cannot be below zero, found: {:?}", persp.fovy);
|
assert!(
|
||||||
assert!(persp.fovy < Rad::turn_div_2(), "The vertical field of view cannot be greater than a half turn, found: {:?}", persp.fovy);
|
persp.fovy > Rad::zero(),
|
||||||
assert!(persp.aspect > S::zero(), "The aspect ratio cannot be below zero, found: {:?}", persp.aspect);
|
"The vertical field of view cannot be below zero, found: {:?}",
|
||||||
assert!(persp.near > S::zero(), "The near plane distance cannot be below zero, found: {:?}", persp.near);
|
persp.fovy
|
||||||
assert!(persp.far > S::zero(), "The far plane distance cannot be below zero, found: {:?}", persp.far);
|
);
|
||||||
assert!(persp.far > persp.near, "The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}", persp.far, persp.near);
|
assert!(
|
||||||
|
persp.fovy < Rad::turn_div_2(),
|
||||||
|
"The vertical field of view cannot be greater than a half turn, found: {:?}",
|
||||||
|
persp.fovy
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
persp.aspect > S::zero(),
|
||||||
|
"The aspect ratio cannot be below zero, found: {:?}",
|
||||||
|
persp.aspect
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
persp.near > S::zero(),
|
||||||
|
"The near plane distance cannot be below zero, found: {:?}",
|
||||||
|
persp.near
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
persp.far > S::zero(),
|
||||||
|
"The far plane distance cannot be below zero, found: {:?}",
|
||||||
|
persp.far
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
persp.far > persp.near,
|
||||||
|
"The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}",
|
||||||
|
persp.far,
|
||||||
|
persp.near
|
||||||
|
);
|
||||||
|
|
||||||
let two: S = cast(2).unwrap();
|
let two: S = cast(2).unwrap();
|
||||||
let f = Rad::cot(persp.fovy / two);
|
let f = Rad::cot(persp.fovy / two);
|
||||||
|
@ -126,10 +156,13 @@ impl<S: BaseFloat> From<PerspectiveFov<S>> for Matrix4<S> {
|
||||||
let c3r2 = (two * persp.far * persp.near) / (persp.near - persp.far);
|
let c3r2 = (two * persp.far * persp.near) / (persp.near - persp.far);
|
||||||
let c3r3 = S::zero();
|
let c3r3 = S::zero();
|
||||||
|
|
||||||
Matrix4::new(c0r0, c0r1, c0r2, c0r3,
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
c1r0, c1r1, c1r2, c1r3,
|
Matrix4::new(
|
||||||
c2r0, c2r1, c2r2, c2r3,
|
c0r0, c0r1, c0r2, c0r3,
|
||||||
c3r0, c3r1, c3r2, c3r3)
|
c1r0, c1r1, c1r2, c1r3,
|
||||||
|
c2r0, c2r1, c2r2, c2r3,
|
||||||
|
c3r0, c3r1, c3r2, c3r3,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,19 +170,34 @@ impl<S: BaseFloat> From<PerspectiveFov<S>> for Matrix4<S> {
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct Perspective<S> {
|
pub struct Perspective<S> {
|
||||||
pub left: S,
|
pub left: S,
|
||||||
pub right: S,
|
pub right: S,
|
||||||
pub bottom: S,
|
pub bottom: S,
|
||||||
pub top: S,
|
pub top: S,
|
||||||
pub near: S,
|
pub near: S,
|
||||||
pub far: S,
|
pub far: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> From<Perspective<S>> for Matrix4<S> {
|
impl<S: BaseFloat> From<Perspective<S>> for Matrix4<S> {
|
||||||
fn from(persp: Perspective<S>) -> Matrix4<S> {
|
fn from(persp: Perspective<S>) -> Matrix4<S> {
|
||||||
assert!(persp.left <= persp.right, "`left` cannot be greater than `right`, found: left: {:?} right: {:?}", persp.left, persp.right);
|
assert!(
|
||||||
assert!(persp.bottom <= persp.top, "`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}", persp.bottom, persp.top);
|
persp.left <= persp.right,
|
||||||
assert!(persp.near <= persp.far, "`near` cannot be greater than `far`, found: near: {:?} far: {:?}", persp.near, persp.far);
|
"`left` cannot be greater than `right`, found: left: {:?} right: {:?}",
|
||||||
|
persp.left,
|
||||||
|
persp.right
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
persp.bottom <= persp.top,
|
||||||
|
"`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}",
|
||||||
|
persp.bottom,
|
||||||
|
persp.top
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
persp.near <= persp.far,
|
||||||
|
"`near` cannot be greater than `far`, found: near: {:?} far: {:?}",
|
||||||
|
persp.near,
|
||||||
|
persp.far
|
||||||
|
);
|
||||||
|
|
||||||
let two: S = cast(2i8).unwrap();
|
let two: S = cast(2i8).unwrap();
|
||||||
|
|
||||||
|
@ -173,10 +221,13 @@ impl<S: BaseFloat> From<Perspective<S>> for Matrix4<S> {
|
||||||
let c3r2 = -(two * persp.far * persp.near) / (persp.far - persp.near);
|
let c3r2 = -(two * persp.far * persp.near) / (persp.far - persp.near);
|
||||||
let c3r3 = S::zero();
|
let c3r3 = S::zero();
|
||||||
|
|
||||||
Matrix4::new(c0r0, c0r1, c0r2, c0r3,
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
c1r0, c1r1, c1r2, c1r3,
|
Matrix4::new(
|
||||||
c2r0, c2r1, c2r2, c2r3,
|
c0r0, c0r1, c0r2, c0r3,
|
||||||
c3r0, c3r1, c3r2, c3r3)
|
c1r0, c1r1, c1r2, c1r3,
|
||||||
|
c2r0, c2r1, c2r2, c2r3,
|
||||||
|
c3r0, c3r1, c3r2, c3r3,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,12 +235,12 @@ impl<S: BaseFloat> From<Perspective<S>> for Matrix4<S> {
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct Ortho<S> {
|
pub struct Ortho<S> {
|
||||||
pub left: S,
|
pub left: S,
|
||||||
pub right: S,
|
pub right: S,
|
||||||
pub bottom: S,
|
pub bottom: S,
|
||||||
pub top: S,
|
pub top: S,
|
||||||
pub near: S,
|
pub near: S,
|
||||||
pub far: S,
|
pub far: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> From<Ortho<S>> for Matrix4<S> {
|
impl<S: BaseFloat> From<Ortho<S>> for Matrix4<S> {
|
||||||
|
@ -216,9 +267,12 @@ impl<S: BaseFloat> From<Ortho<S>> for Matrix4<S> {
|
||||||
let c3r2 = -(ortho.far + ortho.near) / (ortho.far - ortho.near);
|
let c3r2 = -(ortho.far + ortho.near) / (ortho.far - ortho.near);
|
||||||
let c3r3 = S::one();
|
let c3r3 = S::one();
|
||||||
|
|
||||||
Matrix4::new(c0r0, c0r1, c0r2, c0r3,
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
c1r0, c1r1, c1r2, c1r3,
|
Matrix4::new(
|
||||||
c2r0, c2r1, c2r2, c2r3,
|
c0r0, c0r1, c0r2, c0r3,
|
||||||
c3r0, c3r1, c3r2, c3r3)
|
c1r0, c1r1, c1r2, c1r3,
|
||||||
|
c2r0, c2r1, c2r2, c2r3,
|
||||||
|
c3r0, c3r1, c3r2, c3r3,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::mem;
|
||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
|
|
||||||
use rand::{Rand, Rng};
|
use rand::{Rand, Rng};
|
||||||
use num_traits::{NumCast, cast};
|
use num_traits::{cast, NumCast};
|
||||||
|
|
||||||
use structure::*;
|
use structure::*;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ use euler::Euler;
|
||||||
use matrix::{Matrix3, Matrix4};
|
use matrix::{Matrix3, Matrix4};
|
||||||
use num::BaseFloat;
|
use num::BaseFloat;
|
||||||
use point::Point3;
|
use point::Point3;
|
||||||
use rotation::{Rotation, Rotation3, Basis3};
|
use rotation::{Basis3, Rotation, Rotation3};
|
||||||
use vector::Vector3;
|
use vector::Vector3;
|
||||||
|
|
||||||
#[cfg(feature = "simd")]
|
#[cfg(feature = "simd")]
|
||||||
|
@ -97,8 +97,11 @@ impl<S: BaseFloat> Quaternion<S> {
|
||||||
/// (http://stackoverflow.com/questions/1171849/finding-quaternion-representing-the-rotation-from-one-vector-to-another)
|
/// (http://stackoverflow.com/questions/1171849/finding-quaternion-representing-the-rotation-from-one-vector-to-another)
|
||||||
/// - [Ogre implementation for normalized vectors]
|
/// - [Ogre implementation for normalized vectors]
|
||||||
/// (https://bitbucket.org/sinbad/ogre/src/9db75e3ba05c/OgreMain/include/OgreVector3.h?fileviewer=file-view-default#cl-651)
|
/// (https://bitbucket.org/sinbad/ogre/src/9db75e3ba05c/OgreMain/include/OgreVector3.h?fileviewer=file-view-default#cl-651)
|
||||||
pub fn from_arc(src: Vector3<S>, dst: Vector3<S>, fallback: Option<Vector3<S>>)
|
pub fn from_arc(
|
||||||
-> Quaternion<S> {
|
src: Vector3<S>,
|
||||||
|
dst: Vector3<S>,
|
||||||
|
fallback: Option<Vector3<S>>,
|
||||||
|
) -> Quaternion<S> {
|
||||||
let mag_avg = (src.magnitude2() * dst.magnitude2()).sqrt();
|
let mag_avg = (src.magnitude2() * dst.magnitude2()).sqrt();
|
||||||
let dot = src.dot(dst);
|
let dot = src.dot(dst);
|
||||||
if ulps_eq!(dot, &mag_avg) {
|
if ulps_eq!(dot, &mag_avg) {
|
||||||
|
@ -193,28 +196,28 @@ impl<S: BaseFloat> One for Quaternion<S> {
|
||||||
|
|
||||||
impl<S: BaseFloat> iter::Sum<Quaternion<S>> for Quaternion<S> {
|
impl<S: BaseFloat> iter::Sum<Quaternion<S>> for Quaternion<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sum<I: Iterator<Item=Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
fn sum<I: Iterator<Item = Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||||
iter.fold(Quaternion::<S>::zero(), Add::add)
|
iter.fold(Quaternion::<S>::zero(), Add::add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: 'a + BaseFloat> iter::Sum<&'a Quaternion<S>> for Quaternion<S> {
|
impl<'a, S: 'a + BaseFloat> iter::Sum<&'a Quaternion<S>> for Quaternion<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sum<I: Iterator<Item=&'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
fn sum<I: Iterator<Item = &'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||||
iter.fold(Quaternion::<S>::zero(), Add::add)
|
iter.fold(Quaternion::<S>::zero(), Add::add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> iter::Product<Quaternion<S>> for Quaternion<S> {
|
impl<S: BaseFloat> iter::Product<Quaternion<S>> for Quaternion<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn product<I: Iterator<Item=Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
fn product<I: Iterator<Item = Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||||
iter.fold(Quaternion::<S>::one(), Mul::mul)
|
iter.fold(Quaternion::<S>::one(), Mul::mul)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Quaternion<S>> for Quaternion<S> {
|
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Quaternion<S>> for Quaternion<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn product<I: Iterator<Item=&'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
fn product<I: Iterator<Item = &'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||||
iter.fold(Quaternion::<S>::one(), Mul::mul)
|
iter.fold(Quaternion::<S>::one(), Mul::mul)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,11 +240,11 @@ impl<S: NumCast + Copy> Quaternion<S> {
|
||||||
pub fn cast<T: BaseFloat>(&self) -> Option<Quaternion<T>> {
|
pub fn cast<T: BaseFloat>(&self) -> Option<Quaternion<T>> {
|
||||||
let s = match NumCast::from(self.s) {
|
let s = match NumCast::from(self.s) {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => return None
|
None => return None,
|
||||||
};
|
};
|
||||||
let v = match self.v.cast() {
|
let v = match self.v.cast() {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return None
|
None => return None,
|
||||||
};
|
};
|
||||||
Some(Quaternion::from_sv(s, v))
|
Some(Quaternion::from_sv(s, v))
|
||||||
}
|
}
|
||||||
|
@ -274,7 +277,8 @@ impl InnerSpace for Quaternion<f32> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A> From<Euler<A>> for Quaternion<A::Unitless> where
|
impl<A> From<Euler<A>> for Quaternion<A::Unitless>
|
||||||
|
where
|
||||||
A: Angle + Into<Rad<<A as Angle>::Unitless>>,
|
A: Angle + Into<Rad<<A as Angle>::Unitless>>,
|
||||||
{
|
{
|
||||||
fn from(src: Euler<A>) -> Quaternion<A::Unitless> {
|
fn from(src: Euler<A>) -> Quaternion<A::Unitless> {
|
||||||
|
@ -288,10 +292,12 @@ impl<A> From<Euler<A>> for Quaternion<A::Unitless> where
|
||||||
let (s_y, c_y) = Rad::sin_cos(src.y.into() * half);
|
let (s_y, c_y) = Rad::sin_cos(src.y.into() * half);
|
||||||
let (s_z, c_z) = Rad::sin_cos(src.z.into() * half);
|
let (s_z, c_z) = Rad::sin_cos(src.z.into() * half);
|
||||||
|
|
||||||
Quaternion::new(-s_x * s_y * s_z + c_x * c_y * c_z,
|
Quaternion::new(
|
||||||
s_x * c_y * c_z + s_y * s_z * c_x,
|
-s_x * s_y * s_z + c_x * c_y * c_z,
|
||||||
-s_x * s_z * c_y + s_y * c_x * c_z,
|
s_x * c_y * c_z + s_y * s_z * c_x,
|
||||||
s_x * s_y * c_z + s_z * c_x * c_y)
|
-s_x * s_z * c_y + s_y * c_x * c_z,
|
||||||
|
s_x * s_y * c_z + s_z * c_x * c_y,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,20 +516,24 @@ impl SubAssign for Quaternion<f32> {
|
||||||
#[cfg(not(feature = "simd"))]
|
#[cfg(not(feature = "simd"))]
|
||||||
impl_operator!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
|
impl_operator!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
|
||||||
fn mul(lhs, rhs) -> Quaternion<S> {
|
fn mul(lhs, rhs) -> Quaternion<S> {
|
||||||
Quaternion::new(lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z,
|
Quaternion::new(
|
||||||
lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y,
|
lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z,
|
||||||
lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z,
|
lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y,
|
||||||
lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x)
|
lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z,
|
||||||
|
lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "simd")]
|
#[cfg(feature = "simd")]
|
||||||
impl_operator_default!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
|
impl_operator_default!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
|
||||||
fn mul(lhs, rhs) -> Quaternion<S> {
|
fn mul(lhs, rhs) -> Quaternion<S> {
|
||||||
Quaternion::new(lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z,
|
Quaternion::new(
|
||||||
lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y,
|
lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z,
|
||||||
lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z,
|
lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y,
|
||||||
lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x)
|
lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z,
|
||||||
|
lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -593,14 +603,14 @@ impl<S: BaseFloat> ApproxEq for Quaternion<S> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool {
|
fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool {
|
||||||
S::relative_eq(&self.s, &other.s, epsilon, max_relative) &&
|
S::relative_eq(&self.s, &other.s, epsilon, max_relative)
|
||||||
Vector3::relative_eq(&self.v, &other.v, epsilon, max_relative)
|
&& Vector3::relative_eq(&self.v, &other.v, epsilon, max_relative)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool {
|
fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool {
|
||||||
S::ulps_eq(&self.s, &other.s, epsilon, max_ulps) &&
|
S::ulps_eq(&self.s, &other.s, epsilon, max_ulps)
|
||||||
Vector3::ulps_eq(&self.v, &other.v, epsilon, max_ulps)
|
&& Vector3::ulps_eq(&self.v, &other.v, epsilon, max_ulps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,9 +633,12 @@ impl<S: BaseFloat> From<Quaternion<S>> for Matrix3<S> {
|
||||||
let sz2 = z2 * quat.s;
|
let sz2 = z2 * quat.s;
|
||||||
let sx2 = x2 * quat.s;
|
let sx2 = x2 * quat.s;
|
||||||
|
|
||||||
Matrix3::new(S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2,
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2,
|
Matrix3::new(
|
||||||
xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2)
|
S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2,
|
||||||
|
xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2,
|
||||||
|
xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,10 +661,13 @@ impl<S: BaseFloat> From<Quaternion<S>> for Matrix4<S> {
|
||||||
let sz2 = z2 * quat.s;
|
let sz2 = z2 * quat.s;
|
||||||
let sx2 = x2 * quat.s;
|
let sx2 = x2 * quat.s;
|
||||||
|
|
||||||
Matrix4::new(S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2, S::zero(),
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2, S::zero(),
|
Matrix4::new(
|
||||||
xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2, S::zero(),
|
S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2, S::zero(),
|
||||||
S::zero(), S::zero(), S::zero(), S::one())
|
xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2, S::zero(),
|
||||||
|
xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2, S::zero(),
|
||||||
|
S::zero(), S::zero(), S::zero(), S::one(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,7 +675,9 @@ impl<S: BaseFloat> From<Quaternion<S>> for Matrix4<S> {
|
||||||
|
|
||||||
impl<S: BaseFloat> From<Quaternion<S>> for Basis3<S> {
|
impl<S: BaseFloat> From<Quaternion<S>> for Basis3<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(quat: Quaternion<S>) -> Basis3<S> { Basis3::from_quaternion(&quat) }
|
fn from(quat: Quaternion<S>) -> Basis3<S> {
|
||||||
|
Basis3::from_quaternion(&quat)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> Rotation<Point3<S>> for Quaternion<S> {
|
impl<S: BaseFloat> Rotation<Point3<S>> for Quaternion<S> {
|
||||||
|
@ -695,10 +713,14 @@ impl<S: BaseFloat> Rotation<Point3<S>> for Quaternion<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rotate_vector(&self, vec: Vector3<S>) -> Vector3<S> { self * vec }
|
fn rotate_vector(&self, vec: Vector3<S>) -> Vector3<S> {
|
||||||
|
self * vec
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn invert(&self) -> Quaternion<S> { self.conjugate() / self.magnitude2() }
|
fn invert(&self) -> Quaternion<S> {
|
||||||
|
self.conjugate() / self.magnitude2()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> Rotation3<S> for Quaternion<S> {
|
impl<S: BaseFloat> Rotation3<S> for Quaternion<S> {
|
||||||
|
@ -712,7 +734,9 @@ impl<S: BaseFloat> Rotation3<S> for Quaternion<S> {
|
||||||
impl<S: BaseFloat> Into<[S; 4]> for Quaternion<S> {
|
impl<S: BaseFloat> Into<[S; 4]> for Quaternion<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into(self) -> [S; 4] {
|
fn into(self) -> [S; 4] {
|
||||||
match self.into() { (w, xi, yj, zk) => [w, xi, yj, zk] }
|
match self.into() {
|
||||||
|
(w, xi, yj, zk) => [w, xi, yj, zk],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,7 +778,12 @@ impl<'a, S: BaseFloat> From<&'a mut [S; 4]> for &'a mut Quaternion<S> {
|
||||||
impl<S: BaseFloat> Into<(S, S, S, S)> for Quaternion<S> {
|
impl<S: BaseFloat> Into<(S, S, S, S)> for Quaternion<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into(self) -> (S, S, S, S) {
|
fn into(self) -> (S, S, S, S) {
|
||||||
match self { Quaternion { s, v: Vector3 { x, y, z } } => (s, x, y, z) }
|
match self {
|
||||||
|
Quaternion {
|
||||||
|
s,
|
||||||
|
v: Vector3 { x, y, z },
|
||||||
|
} => (s, x, y, z),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -775,7 +804,9 @@ impl<S: BaseFloat> AsMut<(S, S, S, S)> for Quaternion<S> {
|
||||||
impl<S: BaseFloat> From<(S, S, S, S)> for Quaternion<S> {
|
impl<S: BaseFloat> From<(S, S, S, S)> for Quaternion<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(v: (S, S, S, S)) -> Quaternion<S> {
|
fn from(v: (S, S, S, S)) -> Quaternion<S> {
|
||||||
match v { (w, xi, yj, zk) => Quaternion::new(w, xi, yj, zk) }
|
match v {
|
||||||
|
(w, xi, yj, zk) => Quaternion::new(w, xi, yj, zk),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,7 +853,7 @@ index_operators!(S, [S], RangeFull);
|
||||||
impl<S: BaseFloat + Rand> Rand for Quaternion<S> {
|
impl<S: BaseFloat + Rand> Rand for Quaternion<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rand<R: Rng>(rng: &mut R) -> Quaternion<S> {
|
fn rand<R: Rng>(rng: &mut R) -> Quaternion<S> {
|
||||||
Quaternion::from_sv(rng.gen(), rng.gen())
|
Quaternion::from_sv(rng.gen(), rng.gen())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,7 +877,6 @@ impl<S: Clone> Into<mint::Quaternion<S>> for Quaternion<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use quaternion::*;
|
use quaternion::*;
|
||||||
|
@ -854,7 +884,11 @@ mod tests {
|
||||||
|
|
||||||
const QUATERNION: Quaternion<f32> = Quaternion {
|
const QUATERNION: Quaternion<f32> = Quaternion {
|
||||||
s: 1.0,
|
s: 1.0,
|
||||||
v: Vector3 { x: 2.0, y: 3.0, z: 4.0 },
|
v: Vector3 {
|
||||||
|
x: 2.0,
|
||||||
|
y: 3.0,
|
||||||
|
z: 4.0,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -887,11 +921,11 @@ mod tests {
|
||||||
fn test_as_mut() {
|
fn test_as_mut() {
|
||||||
let mut v = QUATERNION;
|
let mut v = QUATERNION;
|
||||||
{
|
{
|
||||||
let v: &mut[f32; 4] = v.as_mut();
|
let v: &mut [f32; 4] = v.as_mut();
|
||||||
assert_eq!(v, &mut [1.0, 2.0, 3.0, 4.0]);
|
assert_eq!(v, &mut [1.0, 2.0, 3.0, 4.0]);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let v: &mut(f32, f32, f32, f32) = v.as_mut();
|
let v: &mut (f32, f32, f32, f32) = v.as_mut();
|
||||||
assert_eq!(v, &mut (1.0, 2.0, 3.0, 4.0));
|
assert_eq!(v, &mut (1.0, 2.0, 3.0, 4.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
112
src/rotation.rs
112
src/rotation.rs
|
@ -30,7 +30,8 @@ use vector::{Vector2, Vector3};
|
||||||
|
|
||||||
/// A trait for a generic rotation. A rotation is a transformation that
|
/// A trait for a generic rotation. A rotation is a transformation that
|
||||||
/// creates a circular motion, and preserves at least one point in the space.
|
/// creates a circular motion, and preserves at least one point in the space.
|
||||||
pub trait Rotation<P: EuclideanSpace>: Sized + Copy + One where
|
pub trait Rotation<P: EuclideanSpace>: Sized + Copy + One
|
||||||
|
where
|
||||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||||
Self: ApproxEq<Epsilon = P::Scalar>,
|
Self: ApproxEq<Epsilon = P::Scalar>,
|
||||||
P::Scalar: BaseFloat,
|
P::Scalar: BaseFloat,
|
||||||
|
@ -59,20 +60,17 @@ pub trait Rotation<P: EuclideanSpace>: Sized + Copy + One where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A two-dimensional rotation.
|
/// A two-dimensional rotation.
|
||||||
pub trait Rotation2<S: BaseFloat>: Rotation<Point2<S>>
|
pub trait Rotation2<S: BaseFloat>
|
||||||
+ Into<Matrix2<S>>
|
: Rotation<Point2<S>> + Into<Matrix2<S>> + Into<Basis2<S>> {
|
||||||
+ Into<Basis2<S>> {
|
|
||||||
/// Create a rotation by a given angle. Thus is a redundant case of both
|
/// Create a rotation by a given angle. Thus is a redundant case of both
|
||||||
/// from_axis_angle() and from_euler() for 2D space.
|
/// from_axis_angle() and from_euler() for 2D space.
|
||||||
fn from_angle<A: Into<Rad<S>>>(theta: A) -> Self;
|
fn from_angle<A: Into<Rad<S>>>(theta: A) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A three-dimensional rotation.
|
/// A three-dimensional rotation.
|
||||||
pub trait Rotation3<S: BaseFloat>: Rotation<Point3<S>>
|
pub trait Rotation3<S: BaseFloat>
|
||||||
+ Into<Matrix3<S>>
|
: Rotation<Point3<S>> + Into<Matrix3<S>> + Into<Basis3<S>> + Into<Quaternion<S>> + From<Euler<Rad<S>>>
|
||||||
+ Into<Basis3<S>>
|
{
|
||||||
+ Into<Quaternion<S>>
|
|
||||||
+ From<Euler<Rad<S>>> {
|
|
||||||
/// Create a rotation using an angle around a given axis.
|
/// Create a rotation using an angle around a given axis.
|
||||||
///
|
///
|
||||||
/// The specified axis **must be normalized**, or it represents an invalid rotation.
|
/// The specified axis **must be normalized**, or it represents an invalid rotation.
|
||||||
|
@ -97,7 +95,6 @@ pub trait Rotation3<S: BaseFloat>: Rotation<Point3<S>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A two-dimensional rotation matrix.
|
/// A two-dimensional rotation matrix.
|
||||||
///
|
///
|
||||||
/// The matrix is guaranteed to be orthogonal, so some operations can be
|
/// The matrix is guaranteed to be orthogonal, so some operations can be
|
||||||
|
@ -144,7 +141,7 @@ pub trait Rotation3<S: BaseFloat>: Rotation<Point3<S>>
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct Basis2<S> {
|
pub struct Basis2<S> {
|
||||||
mat: Matrix2<S>
|
mat: Matrix2<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> AsRef<Matrix2<S>> for Basis2<S> {
|
impl<S: BaseFloat> AsRef<Matrix2<S>> for Basis2<S> {
|
||||||
|
@ -156,19 +153,21 @@ impl<S: BaseFloat> AsRef<Matrix2<S>> for Basis2<S> {
|
||||||
|
|
||||||
impl<S: BaseFloat> From<Basis2<S>> for Matrix2<S> {
|
impl<S: BaseFloat> From<Basis2<S>> for Matrix2<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(b: Basis2<S>) -> Matrix2<S> { b.mat }
|
fn from(b: Basis2<S>) -> Matrix2<S> {
|
||||||
|
b.mat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> iter::Product<Basis2<S>> for Basis2<S> {
|
impl<S: BaseFloat> iter::Product<Basis2<S>> for Basis2<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn product<I: Iterator<Item=Basis2<S>>>(iter: I) -> Basis2<S> {
|
fn product<I: Iterator<Item = Basis2<S>>>(iter: I) -> Basis2<S> {
|
||||||
iter.fold(Basis2::one(), Mul::mul)
|
iter.fold(Basis2::one(), Mul::mul)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis2<S>> for Basis2<S> {
|
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis2<S>> for Basis2<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn product<I: Iterator<Item=&'a Basis2<S>>>(iter: I) -> Basis2<S> {
|
fn product<I: Iterator<Item = &'a Basis2<S>>>(iter: I) -> Basis2<S> {
|
||||||
iter.fold(Basis2::one(), Mul::mul)
|
iter.fold(Basis2::one(), Mul::mul)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,26 +175,38 @@ impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis2<S>> for Basis2<S> {
|
||||||
impl<S: BaseFloat> Rotation<Point2<S>> for Basis2<S> {
|
impl<S: BaseFloat> Rotation<Point2<S>> for Basis2<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn look_at(dir: Vector2<S>, up: Vector2<S>) -> Basis2<S> {
|
fn look_at(dir: Vector2<S>, up: Vector2<S>) -> Basis2<S> {
|
||||||
Basis2 { mat: Matrix2::look_at(dir, up) }
|
Basis2 {
|
||||||
|
mat: Matrix2::look_at(dir, up),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn between_vectors(a: Vector2<S>, b: Vector2<S>) -> Basis2<S> {
|
fn between_vectors(a: Vector2<S>, b: Vector2<S>) -> Basis2<S> {
|
||||||
Rotation2::from_angle(Rad::acos(a.dot(b)) )
|
Rotation2::from_angle(Rad::acos(a.dot(b)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rotate_vector(&self, vec: Vector2<S>) -> Vector2<S> { self.mat * vec }
|
fn rotate_vector(&self, vec: Vector2<S>) -> Vector2<S> {
|
||||||
|
self.mat * vec
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: we know the matrix is orthogonal, so this could be re-written
|
// TODO: we know the matrix is orthogonal, so this could be re-written
|
||||||
// to be faster
|
// to be faster
|
||||||
#[inline]
|
#[inline]
|
||||||
fn invert(&self) -> Basis2<S> { Basis2 { mat: self.mat.invert().unwrap() } }
|
fn invert(&self) -> Basis2<S> {
|
||||||
|
Basis2 {
|
||||||
|
mat: self.mat.invert().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> One for Basis2<S> {
|
impl<S: BaseFloat> One for Basis2<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn one() -> Basis2<S> { Basis2 { mat: Matrix2::one() } }
|
fn one() -> Basis2<S> {
|
||||||
|
Basis2 {
|
||||||
|
mat: Matrix2::one(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_operator!(<S: BaseFloat> Mul<Basis2<S> > for Basis2<S> {
|
impl_operator!(<S: BaseFloat> Mul<Basis2<S> > for Basis2<S> {
|
||||||
|
@ -232,7 +243,11 @@ impl<S: BaseFloat> ApproxEq for Basis2<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> Rotation2<S> for Basis2<S> {
|
impl<S: BaseFloat> Rotation2<S> for Basis2<S> {
|
||||||
fn from_angle<A: Into<Rad<S>>>(theta: A) -> Basis2<S> { Basis2 { mat: Matrix2::from_angle(theta) } }
|
fn from_angle<A: Into<Rad<S>>>(theta: A) -> Basis2<S> {
|
||||||
|
Basis2 {
|
||||||
|
mat: Matrix2::from_angle(theta),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: fmt::Debug> fmt::Debug for Basis2<S> {
|
impl<S: fmt::Debug> fmt::Debug for Basis2<S> {
|
||||||
|
@ -251,14 +266,16 @@ impl<S: fmt::Debug> fmt::Debug for Basis2<S> {
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct Basis3<S> {
|
pub struct Basis3<S> {
|
||||||
mat: Matrix3<S>
|
mat: Matrix3<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> Basis3<S> {
|
impl<S: BaseFloat> Basis3<S> {
|
||||||
/// Create a new rotation matrix from a quaternion.
|
/// Create a new rotation matrix from a quaternion.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_quaternion(quaternion: &Quaternion<S>) -> Basis3<S> {
|
pub fn from_quaternion(quaternion: &Quaternion<S>) -> Basis3<S> {
|
||||||
Basis3 { mat: quaternion.clone().into() }
|
Basis3 {
|
||||||
|
mat: quaternion.clone().into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,24 +288,28 @@ impl<S> AsRef<Matrix3<S>> for Basis3<S> {
|
||||||
|
|
||||||
impl<S: BaseFloat> From<Basis3<S>> for Matrix3<S> {
|
impl<S: BaseFloat> From<Basis3<S>> for Matrix3<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(b: Basis3<S>) -> Matrix3<S> { b.mat }
|
fn from(b: Basis3<S>) -> Matrix3<S> {
|
||||||
|
b.mat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> From<Basis3<S>> for Quaternion<S> {
|
impl<S: BaseFloat> From<Basis3<S>> for Quaternion<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(b: Basis3<S>) -> Quaternion<S> { b.mat.into() }
|
fn from(b: Basis3<S>) -> Quaternion<S> {
|
||||||
|
b.mat.into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> iter::Product<Basis3<S>> for Basis3<S> {
|
impl<S: BaseFloat> iter::Product<Basis3<S>> for Basis3<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn product<I: Iterator<Item=Basis3<S>>>(iter: I) -> Basis3<S> {
|
fn product<I: Iterator<Item = Basis3<S>>>(iter: I) -> Basis3<S> {
|
||||||
iter.fold(Basis3::one(), Mul::mul)
|
iter.fold(Basis3::one(), Mul::mul)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3<S>> for Basis3<S> {
|
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3<S>> for Basis3<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn product<I: Iterator<Item=&'a Basis3<S>>>(iter: I) -> Basis3<S> {
|
fn product<I: Iterator<Item = &'a Basis3<S>>>(iter: I) -> Basis3<S> {
|
||||||
iter.fold(Basis3::one(), Mul::mul)
|
iter.fold(Basis3::one(), Mul::mul)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,7 +317,9 @@ impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3<S>> for Basis3<S> {
|
||||||
impl<S: BaseFloat> Rotation<Point3<S>> for Basis3<S> {
|
impl<S: BaseFloat> Rotation<Point3<S>> for Basis3<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn look_at(dir: Vector3<S>, up: Vector3<S>) -> Basis3<S> {
|
fn look_at(dir: Vector3<S>, up: Vector3<S>) -> Basis3<S> {
|
||||||
Basis3 { mat: Matrix3::look_at(dir, up) }
|
Basis3 {
|
||||||
|
mat: Matrix3::look_at(dir, up),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -306,17 +329,27 @@ impl<S: BaseFloat> Rotation<Point3<S>> for Basis3<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rotate_vector(&self, vec: Vector3<S>) -> Vector3<S> { self.mat * vec }
|
fn rotate_vector(&self, vec: Vector3<S>) -> Vector3<S> {
|
||||||
|
self.mat * vec
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: we know the matrix is orthogonal, so this could be re-written
|
// TODO: we know the matrix is orthogonal, so this could be re-written
|
||||||
// to be faster
|
// to be faster
|
||||||
#[inline]
|
#[inline]
|
||||||
fn invert(&self) -> Basis3<S> { Basis3 { mat: self.mat.invert().unwrap() } }
|
fn invert(&self) -> Basis3<S> {
|
||||||
|
Basis3 {
|
||||||
|
mat: self.mat.invert().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseFloat> One for Basis3<S> {
|
impl<S: BaseFloat> One for Basis3<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn one() -> Basis3<S> { Basis3 { mat: Matrix3::one() } }
|
fn one() -> Basis3<S> {
|
||||||
|
Basis3 {
|
||||||
|
mat: Matrix3::one(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_operator!(<S: BaseFloat> Mul<Basis3<S> > for Basis3<S> {
|
impl_operator!(<S: BaseFloat> Mul<Basis3<S> > for Basis3<S> {
|
||||||
|
@ -354,23 +387,32 @@ impl<S: BaseFloat> ApproxEq for Basis3<S> {
|
||||||
|
|
||||||
impl<S: BaseFloat> Rotation3<S> for Basis3<S> {
|
impl<S: BaseFloat> Rotation3<S> for Basis3<S> {
|
||||||
fn from_axis_angle<A: Into<Rad<S>>>(axis: Vector3<S>, angle: A) -> Basis3<S> {
|
fn from_axis_angle<A: Into<Rad<S>>>(axis: Vector3<S>, angle: A) -> Basis3<S> {
|
||||||
Basis3 { mat: Matrix3::from_axis_angle(axis, angle) }
|
Basis3 {
|
||||||
|
mat: Matrix3::from_axis_angle(axis, angle),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_angle_x<A: Into<Rad<S>>>(theta: A) -> Basis3<S> {
|
fn from_angle_x<A: Into<Rad<S>>>(theta: A) -> Basis3<S> {
|
||||||
Basis3 { mat: Matrix3::from_angle_x(theta) }
|
Basis3 {
|
||||||
|
mat: Matrix3::from_angle_x(theta),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_angle_y<A: Into<Rad<S>>>(theta: A) -> Basis3<S> {
|
fn from_angle_y<A: Into<Rad<S>>>(theta: A) -> Basis3<S> {
|
||||||
Basis3 { mat: Matrix3::from_angle_y(theta) }
|
Basis3 {
|
||||||
|
mat: Matrix3::from_angle_y(theta),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_angle_z<A: Into<Rad<S>>>(theta: A) -> Basis3<S> {
|
fn from_angle_z<A: Into<Rad<S>>>(theta: A) -> Basis3<S> {
|
||||||
Basis3 { mat: Matrix3::from_angle_z(theta) }
|
Basis3 {
|
||||||
|
mat: Matrix3::from_angle_z(theta),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Angle> From<Euler<A>> for Basis3<A::Unitless> where
|
impl<A: Angle> From<Euler<A>> for Basis3<A::Unitless>
|
||||||
|
where
|
||||||
A: Into<Rad<<A as Angle>::Unitless>>,
|
A: Into<Rad<<A as Angle>::Unitless>>,
|
||||||
{
|
{
|
||||||
/// Create a three-dimensional rotation matrix from a set of euler angles.
|
/// Create a three-dimensional rotation matrix from a set of euler angles.
|
||||||
|
|
|
@ -23,12 +23,13 @@ use std::ops::*;
|
||||||
use approx::ApproxEq;
|
use approx::ApproxEq;
|
||||||
|
|
||||||
use angle::Rad;
|
use angle::Rad;
|
||||||
use num::{BaseNum, BaseFloat};
|
use num::{BaseFloat, BaseNum};
|
||||||
|
|
||||||
pub use num_traits::{One, Zero, Bounded};
|
pub use num_traits::{Bounded, One, Zero};
|
||||||
|
|
||||||
/// An array containing elements of type `Element`
|
/// An array containing elements of type `Element`
|
||||||
pub trait Array where
|
pub trait Array
|
||||||
|
where
|
||||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||||
Self: Index<usize, Output = <Self as Array>::Element>,
|
Self: Index<usize, Output = <Self as Array>::Element>,
|
||||||
Self: IndexMut<usize, Output = <Self as Array>::Element>,
|
Self: IndexMut<usize, Output = <Self as Array>::Element>,
|
||||||
|
@ -78,10 +79,14 @@ pub trait Array where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The sum of the elements of the array.
|
/// The sum of the elements of the array.
|
||||||
fn sum(self) -> Self::Element where Self::Element: Add<Output = <Self as Array>::Element>;
|
fn sum(self) -> Self::Element
|
||||||
|
where
|
||||||
|
Self::Element: Add<Output = <Self as Array>::Element>;
|
||||||
|
|
||||||
/// The product of the elements of the array.
|
/// The product of the elements of the array.
|
||||||
fn product(self) -> Self::Element where Self::Element: Mul<Output = <Self as Array>::Element>;
|
fn product(self) -> Self::Element
|
||||||
|
where
|
||||||
|
Self::Element: Mul<Output = <Self as Array>::Element>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Element-wise arithmetic operations. These are supplied for pragmatic
|
/// Element-wise arithmetic operations. These are supplied for pragmatic
|
||||||
|
@ -156,7 +161,8 @@ pub trait ElementWise<Rhs = Self> {
|
||||||
/// let upscaled_translation = translation * scale_factor;
|
/// let upscaled_translation = translation * scale_factor;
|
||||||
/// let downscaled_translation = translation / scale_factor;
|
/// let downscaled_translation = translation / scale_factor;
|
||||||
/// ```
|
/// ```
|
||||||
pub trait VectorSpace: Copy + Clone where
|
pub trait VectorSpace: Copy + Clone
|
||||||
|
where
|
||||||
Self: Zero,
|
Self: Zero,
|
||||||
|
|
||||||
Self: Add<Self, Output = Self>,
|
Self: Add<Self, Output = Self>,
|
||||||
|
@ -199,7 +205,8 @@ pub trait MetricSpace: Sized {
|
||||||
/// finding the magnitude of a vector or normalizing it.
|
/// finding the magnitude of a vector or normalizing it.
|
||||||
///
|
///
|
||||||
/// Examples include vectors and quaternions.
|
/// Examples include vectors and quaternions.
|
||||||
pub trait InnerSpace: VectorSpace where
|
pub trait InnerSpace: VectorSpace
|
||||||
|
where
|
||||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||||
<Self as VectorSpace>::Scalar: BaseFloat,
|
<Self as VectorSpace>::Scalar: BaseFloat,
|
||||||
Self: MetricSpace<Metric = <Self as VectorSpace>::Scalar>,
|
Self: MetricSpace<Metric = <Self as VectorSpace>::Scalar>,
|
||||||
|
@ -237,14 +244,12 @@ pub trait InnerSpace: VectorSpace where
|
||||||
|
|
||||||
/// Returns a vector with the same direction, but with a magnitude of `1`.
|
/// Returns a vector with the same direction, but with a magnitude of `1`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn normalize(self) -> Self {
|
fn normalize(self) -> Self {
|
||||||
self.normalize_to(Self::Scalar::one())
|
self.normalize_to(Self::Scalar::one())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a vector with the same direction and a given magnitude.
|
/// Returns a vector with the same direction and a given magnitude.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn normalize_to(self, magnitude: Self::Scalar) -> Self {
|
fn normalize_to(self, magnitude: Self::Scalar) -> Self {
|
||||||
self * (magnitude / self.magnitude())
|
self * (magnitude / self.magnitude())
|
||||||
}
|
}
|
||||||
|
@ -252,7 +257,6 @@ pub trait InnerSpace: VectorSpace where
|
||||||
/// Returns the result of linearly interpolating the magnitude of the vector
|
/// Returns the result of linearly interpolating the magnitude of the vector
|
||||||
/// towards the magnitude of `other` by the specified amount.
|
/// towards the magnitude of `other` by the specified amount.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn lerp(self, other: Self, amount: Self::Scalar) -> Self {
|
fn lerp(self, other: Self, amount: Self::Scalar) -> Self {
|
||||||
self + ((other - self) * amount)
|
self + ((other - self) * amount)
|
||||||
}
|
}
|
||||||
|
@ -261,7 +265,6 @@ pub trait InnerSpace: VectorSpace where
|
||||||
/// [vector projection](https://en.wikipedia.org/wiki/Vector_projection)
|
/// [vector projection](https://en.wikipedia.org/wiki/Vector_projection)
|
||||||
/// of the current inner space projected onto the supplied argument.
|
/// of the current inner space projected onto the supplied argument.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn project_on(self, other: Self) -> Self {
|
fn project_on(self, other: Self) -> Self {
|
||||||
other * (self.dot(other) / other.magnitude2())
|
other * (self.dot(other) / other.magnitude2())
|
||||||
}
|
}
|
||||||
|
@ -314,7 +317,8 @@ pub trait InnerSpace: VectorSpace where
|
||||||
/// - [CGAL 4.7 - 2D and 3D Linear Geometry Kernel: 3.1 Points and Vectors](http://doc.cgal.org/latest/Kernel_23/index.html#Kernel_23PointsandVectors)
|
/// - [CGAL 4.7 - 2D and 3D Linear Geometry Kernel: 3.1 Points and Vectors](http://doc.cgal.org/latest/Kernel_23/index.html#Kernel_23PointsandVectors)
|
||||||
/// - [What is the difference between a point and a vector](http://math.stackexchange.com/q/645827)
|
/// - [What is the difference between a point and a vector](http://math.stackexchange.com/q/645827)
|
||||||
///
|
///
|
||||||
pub trait EuclideanSpace: Copy + Clone where
|
pub trait EuclideanSpace: Copy + Clone
|
||||||
|
where
|
||||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||||
Self: Array<Element = <Self as EuclideanSpace>::Scalar>,
|
Self: Array<Element = <Self as EuclideanSpace>::Scalar>,
|
||||||
|
|
||||||
|
@ -382,10 +386,9 @@ pub trait EuclideanSpace: Copy + Clone where
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
fn centroid(points: &[Self]) -> Self {
|
fn centroid(points: &[Self]) -> Self {
|
||||||
let total_displacement =
|
let total_displacement = points
|
||||||
points.iter().fold(Self::Diff::zero(), |acc, p| {
|
.iter()
|
||||||
acc + p.to_vec()
|
.fold(Self::Diff::zero(), |acc, p| acc + p.to_vec());
|
||||||
});
|
|
||||||
|
|
||||||
Self::from_vec(total_displacement / cast(points.len()).unwrap())
|
Self::from_vec(total_displacement / cast(points.len()).unwrap())
|
||||||
}
|
}
|
||||||
|
@ -415,7 +418,8 @@ pub trait EuclideanSpace: Copy + Clone where
|
||||||
/// trait. This is due to the complexities of implementing these operators with
|
/// trait. This is due to the complexities of implementing these operators with
|
||||||
/// Rust's current type system. For the multiplication of square matrices,
|
/// Rust's current type system. For the multiplication of square matrices,
|
||||||
/// see `SquareMatrix`.
|
/// see `SquareMatrix`.
|
||||||
pub trait Matrix: VectorSpace where
|
pub trait Matrix: VectorSpace
|
||||||
|
where
|
||||||
Self::Scalar: BaseFloat,
|
Self::Scalar: BaseFloat,
|
||||||
|
|
||||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||||
|
@ -467,7 +471,8 @@ pub trait Matrix: VectorSpace where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A column-major major matrix where the rows and column vectors are of the same dimensions.
|
/// A column-major major matrix where the rows and column vectors are of the same dimensions.
|
||||||
pub trait SquareMatrix where
|
pub trait SquareMatrix
|
||||||
|
where
|
||||||
Self::Scalar: BaseFloat,
|
Self::Scalar: BaseFloat,
|
||||||
|
|
||||||
Self: One,
|
Self: One,
|
||||||
|
@ -518,22 +523,27 @@ pub trait SquareMatrix where
|
||||||
|
|
||||||
/// Return the trace of this matrix. That is, the sum of the diagonal.
|
/// Return the trace of this matrix. That is, the sum of the diagonal.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn trace(&self) -> Self::Scalar { self.diagonal().sum() }
|
fn trace(&self) -> Self::Scalar {
|
||||||
|
self.diagonal().sum()
|
||||||
|
}
|
||||||
|
|
||||||
/// Invert this matrix, returning a new matrix. `m.mul_m(m.invert())` is
|
/// Invert this matrix, returning a new matrix. `m.mul_m(m.invert())` is
|
||||||
/// the identity matrix. Returns `None` if this matrix is not invertible
|
/// the identity matrix. Returns `None` if this matrix is not invertible
|
||||||
/// (has a determinant of zero).
|
/// (has a determinant of zero).
|
||||||
#[must_use]
|
|
||||||
fn invert(&self) -> Option<Self>;
|
fn invert(&self) -> Option<Self>;
|
||||||
|
|
||||||
/// Test if this matrix is invertible.
|
/// Test if this matrix is invertible.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_invertible(&self) -> bool { ulps_ne!(self.determinant(), &Self::Scalar::zero()) }
|
fn is_invertible(&self) -> bool {
|
||||||
|
ulps_ne!(self.determinant(), &Self::Scalar::zero())
|
||||||
|
}
|
||||||
|
|
||||||
/// Test if this matrix is the identity matrix. That is, it is diagonal
|
/// Test if this matrix is the identity matrix. That is, it is diagonal
|
||||||
/// and every element in the diagonal is one.
|
/// and every element in the diagonal is one.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_identity(&self) -> bool { ulps_eq!(self, &Self::identity()) }
|
fn is_identity(&self) -> bool {
|
||||||
|
ulps_eq!(self, &Self::identity())
|
||||||
|
}
|
||||||
|
|
||||||
/// Test if this is a diagonal matrix. That is, every element outside of
|
/// Test if this is a diagonal matrix. That is, every element outside of
|
||||||
/// the diagonal is 0.
|
/// the diagonal is 0.
|
||||||
|
@ -550,7 +560,8 @@ pub trait SquareMatrix where
|
||||||
/// clear when semantic violations have occured - for example, adding degrees to
|
/// clear when semantic violations have occured - for example, adding degrees to
|
||||||
/// radians, or adding a number to an angle.
|
/// radians, or adding a number to an angle.
|
||||||
///
|
///
|
||||||
pub trait Angle where
|
pub trait Angle
|
||||||
|
where
|
||||||
Self: Copy + Clone,
|
Self: Copy + Clone,
|
||||||
Self: PartialEq + cmp::PartialOrd,
|
Self: PartialEq + cmp::PartialOrd,
|
||||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||||
|
@ -574,7 +585,11 @@ pub trait Angle where
|
||||||
#[inline]
|
#[inline]
|
||||||
fn normalize(self) -> Self {
|
fn normalize(self) -> Self {
|
||||||
let rem = self % Self::full_turn();
|
let rem = self % Self::full_turn();
|
||||||
if rem < Self::zero() { rem + Self::full_turn() } else { rem }
|
if rem < Self::zero() {
|
||||||
|
rem + Self::full_turn()
|
||||||
|
} else {
|
||||||
|
rem
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the angle rotated by half a turn.
|
/// Return the angle rotated by half a turn.
|
||||||
|
|
|
@ -39,7 +39,8 @@ pub trait Transform<P: EuclideanSpace>: Sized {
|
||||||
|
|
||||||
/// Inverse transform a vector using this transform
|
/// Inverse transform a vector using this transform
|
||||||
fn inverse_transform_vector(&self, vec: P::Diff) -> Option<P::Diff> {
|
fn inverse_transform_vector(&self, vec: P::Diff) -> Option<P::Diff> {
|
||||||
self.inverse_transform().and_then(|inverse| Some(inverse.transform_vector(vec)))
|
self.inverse_transform()
|
||||||
|
.and_then(|inverse| Some(inverse.transform_vector(vec)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transform a point using this transform.
|
/// Transform a point using this transform.
|
||||||
|
@ -69,9 +70,10 @@ pub struct Decomposed<V: VectorSpace, R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: EuclideanSpace, R: Rotation<P>> Transform<P> for Decomposed<P::Diff, R>
|
impl<P: EuclideanSpace, R: Rotation<P>> Transform<P> for Decomposed<P::Diff, R>
|
||||||
where P::Scalar: BaseFloat,
|
where
|
||||||
// FIXME: Investigate why this is needed!
|
P::Scalar: BaseFloat,
|
||||||
P::Diff: VectorSpace
|
// FIXME: Investigate why this is needed!
|
||||||
|
P::Diff: VectorSpace,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn one() -> Decomposed<P::Diff, R> {
|
fn one() -> Decomposed<P::Diff, R> {
|
||||||
|
@ -128,10 +130,10 @@ impl<P: EuclideanSpace, R: Rotation<P>> Transform<P> for Decomposed<P::Diff, R>
|
||||||
let r = self.rot.invert();
|
let r = self.rot.invert();
|
||||||
let d = r.rotate_vector(self.disp.clone()) * -s;
|
let d = r.rotate_vector(self.disp.clone()) * -s;
|
||||||
Some(Decomposed {
|
Some(Decomposed {
|
||||||
scale: s,
|
scale: s,
|
||||||
rot: r,
|
rot: r,
|
||||||
disp: d,
|
disp: d,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,9 +164,10 @@ impl<S: BaseFloat, R: Rotation2<S>> Transform2<S> for Decomposed<Vector2<S>, R>
|
||||||
impl<S: BaseFloat, R: Rotation3<S>> Transform3<S> for Decomposed<Vector3<S>, R> {}
|
impl<S: BaseFloat, R: Rotation3<S>> Transform3<S> for Decomposed<Vector3<S>, R> {}
|
||||||
|
|
||||||
impl<S: VectorSpace, R, E: BaseFloat> ApproxEq for Decomposed<S, R>
|
impl<S: VectorSpace, R, E: BaseFloat> ApproxEq for Decomposed<S, R>
|
||||||
where S: ApproxEq<Epsilon = E>,
|
where
|
||||||
S::Scalar: ApproxEq<Epsilon = E>,
|
S: ApproxEq<Epsilon = E>,
|
||||||
R: ApproxEq<Epsilon = E>
|
S::Scalar: ApproxEq<Epsilon = E>,
|
||||||
|
R: ApproxEq<Epsilon = E>,
|
||||||
{
|
{
|
||||||
type Epsilon = E;
|
type Epsilon = E;
|
||||||
|
|
||||||
|
@ -185,16 +188,16 @@ impl<S: VectorSpace, R, E: BaseFloat> ApproxEq for Decomposed<S, R>
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn relative_eq(&self, other: &Self, epsilon: E, max_relative: E) -> bool {
|
fn relative_eq(&self, other: &Self, epsilon: E, max_relative: E) -> bool {
|
||||||
S::Scalar::relative_eq(&self.scale, &other.scale, epsilon, max_relative) &&
|
S::Scalar::relative_eq(&self.scale, &other.scale, epsilon, max_relative)
|
||||||
R::relative_eq(&self.rot, &other.rot, epsilon, max_relative) &&
|
&& R::relative_eq(&self.rot, &other.rot, epsilon, max_relative)
|
||||||
S::relative_eq(&self.disp, &other.disp, epsilon, max_relative)
|
&& S::relative_eq(&self.disp, &other.disp, epsilon, max_relative)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ulps_eq(&self, other: &Self, epsilon: E, max_ulps: u32) -> bool {
|
fn ulps_eq(&self, other: &Self, epsilon: E, max_ulps: u32) -> bool {
|
||||||
S::Scalar::ulps_eq(&self.scale, &other.scale, epsilon, max_ulps) &&
|
S::Scalar::ulps_eq(&self.scale, &other.scale, epsilon, max_ulps)
|
||||||
R::ulps_eq(&self.rot, &other.rot, epsilon, max_ulps) &&
|
&& R::ulps_eq(&self.rot, &other.rot, epsilon, max_ulps)
|
||||||
S::ulps_eq(&self.disp, &other.disp, epsilon, max_ulps)
|
&& S::ulps_eq(&self.disp, &other.disp, epsilon, max_ulps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,12 +210,14 @@ mod serde_ser {
|
||||||
use serde::ser::SerializeStruct;
|
use serde::ser::SerializeStruct;
|
||||||
|
|
||||||
impl<V, R> Serialize for Decomposed<V, R>
|
impl<V, R> Serialize for Decomposed<V, R>
|
||||||
where V: Serialize + VectorSpace,
|
where
|
||||||
V::Scalar: Serialize,
|
V: Serialize + VectorSpace,
|
||||||
R: Serialize
|
V::Scalar: Serialize,
|
||||||
|
R: Serialize,
|
||||||
{
|
{
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where S: serde::Serializer
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
let mut struc = serializer.serialize_struct("Decomposed", 3)?;
|
let mut struc = serializer.serialize_struct("Decomposed", 3)?;
|
||||||
struc.serialize_field("scale", &self.scale)?;
|
struc.serialize_field("scale", &self.scale)?;
|
||||||
|
@ -240,7 +245,8 @@ mod serde_de {
|
||||||
|
|
||||||
impl<'a> Deserialize<'a> for DecomposedField {
|
impl<'a> Deserialize<'a> for DecomposedField {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<DecomposedField, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<DecomposedField, D::Error>
|
||||||
where D: serde::Deserializer<'a>
|
where
|
||||||
|
D: serde::Deserializer<'a>,
|
||||||
{
|
{
|
||||||
struct DecomposedFieldVisitor;
|
struct DecomposedFieldVisitor;
|
||||||
|
|
||||||
|
@ -252,7 +258,8 @@ mod serde_de {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<DecomposedField, E>
|
fn visit_str<E>(self, value: &str) -> Result<DecomposedField, E>
|
||||||
where E: serde::de::Error
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
match value {
|
match value {
|
||||||
"scale" => Ok(DecomposedField::Scale),
|
"scale" => Ok(DecomposedField::Scale),
|
||||||
|
@ -268,12 +275,14 @@ mod serde_de {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: VectorSpace, R> Deserialize<'a> for Decomposed<S, R>
|
impl<'a, S: VectorSpace, R> Deserialize<'a> for Decomposed<S, R>
|
||||||
where S: Deserialize<'a>,
|
where
|
||||||
S::Scalar: Deserialize<'a>,
|
S: Deserialize<'a>,
|
||||||
R: Deserialize<'a>
|
S::Scalar: Deserialize<'a>,
|
||||||
|
R: Deserialize<'a>,
|
||||||
{
|
{
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Decomposed<S, R>, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Decomposed<S, R>, D::Error>
|
||||||
where D: serde::de::Deserializer<'a>
|
where
|
||||||
|
D: serde::de::Deserializer<'a>,
|
||||||
{
|
{
|
||||||
const FIELDS: &'static [&'static str] = &["scale", "rot", "disp"];
|
const FIELDS: &'static [&'static str] = &["scale", "rot", "disp"];
|
||||||
deserializer.deserialize_struct("Decomposed", FIELDS, DecomposedVisitor(PhantomData))
|
deserializer.deserialize_struct("Decomposed", FIELDS, DecomposedVisitor(PhantomData))
|
||||||
|
@ -283,9 +292,10 @@ mod serde_de {
|
||||||
struct DecomposedVisitor<S: VectorSpace, R>(PhantomData<(S, R)>);
|
struct DecomposedVisitor<S: VectorSpace, R>(PhantomData<(S, R)>);
|
||||||
|
|
||||||
impl<'a, S: VectorSpace, R> serde::de::Visitor<'a> for DecomposedVisitor<S, R>
|
impl<'a, S: VectorSpace, R> serde::de::Visitor<'a> for DecomposedVisitor<S, R>
|
||||||
where S: Deserialize<'a>,
|
where
|
||||||
S::Scalar: Deserialize<'a>,
|
S: Deserialize<'a>,
|
||||||
R: Deserialize<'a>
|
S::Scalar: Deserialize<'a>,
|
||||||
|
R: Deserialize<'a>,
|
||||||
{
|
{
|
||||||
type Value = Decomposed<S, R>;
|
type Value = Decomposed<S, R>;
|
||||||
|
|
||||||
|
@ -294,7 +304,8 @@ mod serde_de {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_map<V>(self, mut visitor: V) -> Result<Decomposed<S, R>, V::Error>
|
fn visit_map<V>(self, mut visitor: V) -> Result<Decomposed<S, R>, V::Error>
|
||||||
where V: serde::de::MapAccess<'a>
|
where
|
||||||
|
V: serde::de::MapAccess<'a>,
|
||||||
{
|
{
|
||||||
let mut scale = None;
|
let mut scale = None;
|
||||||
let mut rot = None;
|
let mut rot = None;
|
||||||
|
@ -330,10 +341,10 @@ mod serde_de {
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Decomposed {
|
Ok(Decomposed {
|
||||||
scale: scale,
|
scale: scale,
|
||||||
rot: rot,
|
rot: rot,
|
||||||
disp: disp,
|
disp: disp,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use rand::{Rand, Rng};
|
use rand::{Rand, Rng};
|
||||||
use num_traits::{NumCast, Bounded};
|
use num_traits::{Bounded, NumCast};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -24,7 +24,7 @@ use structure::*;
|
||||||
|
|
||||||
use angle::Rad;
|
use angle::Rad;
|
||||||
use approx::ApproxEq;
|
use approx::ApproxEq;
|
||||||
use num::{BaseNum, BaseFloat};
|
use num::{BaseFloat, BaseNum};
|
||||||
|
|
||||||
#[cfg(feature = "simd")]
|
#[cfg(feature = "simd")]
|
||||||
use simd::f32x4 as Simdf32x4;
|
use simd::f32x4 as Simdf32x4;
|
||||||
|
@ -659,11 +659,12 @@ impl<S: BaseNum> Vector3<S> {
|
||||||
|
|
||||||
/// Returns the cross product of the vector and `other`.
|
/// Returns the cross product of the vector and `other`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn cross(self, other: Vector3<S>) -> Vector3<S> {
|
pub fn cross(self, other: Vector3<S>) -> Vector3<S> {
|
||||||
Vector3::new((self.y * other.z) - (self.z * other.y),
|
Vector3::new(
|
||||||
(self.z * other.x) - (self.x * other.z),
|
(self.y * other.z) - (self.z * other.y),
|
||||||
(self.x * other.y) - (self.y * other.x))
|
(self.z * other.x) - (self.x * other.z),
|
||||||
|
(self.x * other.y) - (self.y * other.x),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `Vector4`, using the `x`, `y` and `z` values from this vector, and the
|
/// Create a `Vector4`, using the `x`, `y` and `z` values from this vector, and the
|
||||||
|
@ -731,7 +732,8 @@ impl<S: BaseNum> Vector4<S> {
|
||||||
/// Dot product of two vectors.
|
/// Dot product of two vectors.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn dot<V: InnerSpace>(a: V, b: V) -> V::Scalar
|
pub fn dot<V: InnerSpace>(a: V, b: V) -> V::Scalar
|
||||||
where V::Scalar: BaseFloat
|
where
|
||||||
|
V::Scalar: BaseFloat,
|
||||||
{
|
{
|
||||||
V::dot(a, b)
|
V::dot(a, b)
|
||||||
}
|
}
|
||||||
|
@ -841,8 +843,6 @@ impl Vector4<f32> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "simd")]
|
#[cfg(feature = "simd")]
|
||||||
impl Into<Simdf32x4> for Vector4<f32> {
|
impl Into<Simdf32x4> for Vector4<f32> {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -888,8 +888,6 @@ impl_operator_simd!{@rs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "simd")]
|
#[cfg(feature = "simd")]
|
||||||
impl_operator_simd!{
|
impl_operator_simd!{
|
||||||
[Simdf32x4]; Neg for Vector4<f32> {
|
[Simdf32x4]; Neg for Vector4<f32> {
|
||||||
|
@ -939,27 +937,46 @@ impl DivAssign<f32> for Vector4<f32> {
|
||||||
|
|
||||||
#[cfg(feature = "simd")]
|
#[cfg(feature = "simd")]
|
||||||
impl ElementWise for Vector4<f32> {
|
impl ElementWise for Vector4<f32> {
|
||||||
#[inline] fn add_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> { self + rhs }
|
#[inline]
|
||||||
#[inline] fn sub_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> { self - rhs }
|
fn add_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
|
||||||
#[inline] fn mul_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
|
self + rhs
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn sub_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
|
||||||
|
self - rhs
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn mul_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
|
||||||
let s: Simdf32x4 = self.into();
|
let s: Simdf32x4 = self.into();
|
||||||
let rhs: Simdf32x4 = rhs.into();
|
let rhs: Simdf32x4 = rhs.into();
|
||||||
(s * rhs).into()
|
(s * rhs).into()
|
||||||
}
|
}
|
||||||
#[inline] fn div_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
|
#[inline]
|
||||||
|
fn div_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
|
||||||
let s: Simdf32x4 = self.into();
|
let s: Simdf32x4 = self.into();
|
||||||
let rhs: Simdf32x4 = rhs.into();
|
let rhs: Simdf32x4 = rhs.into();
|
||||||
(s / rhs).into()
|
(s / rhs).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline] fn add_assign_element_wise(&mut self, rhs: Vector4<f32>) { (*self) += rhs; }
|
#[inline]
|
||||||
#[inline] fn sub_assign_element_wise(&mut self, rhs: Vector4<f32>) { (*self) -= rhs; }
|
fn add_assign_element_wise(&mut self, rhs: Vector4<f32>) {
|
||||||
#[inline] fn mul_assign_element_wise(&mut self, rhs: Vector4<f32>) {
|
(*self) += rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub_assign_element_wise(&mut self, rhs: Vector4<f32>) {
|
||||||
|
(*self) -= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mul_assign_element_wise(&mut self, rhs: Vector4<f32>) {
|
||||||
let s: Simdf32x4 = (*self).into();
|
let s: Simdf32x4 = (*self).into();
|
||||||
let rhs: Simdf32x4 = rhs.into();
|
let rhs: Simdf32x4 = rhs.into();
|
||||||
*self = (s * rhs).into();
|
*self = (s * rhs).into();
|
||||||
}
|
}
|
||||||
#[inline] fn div_assign_element_wise(&mut self, rhs: Vector4<f32>) {
|
|
||||||
|
#[inline]
|
||||||
|
fn div_assign_element_wise(&mut self, rhs: Vector4<f32>) {
|
||||||
let s: Simdf32x4 = (*self).into();
|
let s: Simdf32x4 = (*self).into();
|
||||||
let rhs: Simdf32x4 = rhs.into();
|
let rhs: Simdf32x4 = rhs.into();
|
||||||
*self = (s * rhs).into();
|
*self = (s * rhs).into();
|
||||||
|
@ -968,31 +985,53 @@ impl ElementWise for Vector4<f32> {
|
||||||
|
|
||||||
#[cfg(feature = "simd")]
|
#[cfg(feature = "simd")]
|
||||||
impl ElementWise<f32> for Vector4<f32> {
|
impl ElementWise<f32> for Vector4<f32> {
|
||||||
#[inline] fn add_element_wise(self, rhs: f32) -> Vector4<f32> {
|
#[inline]
|
||||||
|
fn add_element_wise(self, rhs: f32) -> Vector4<f32> {
|
||||||
let s: Simdf32x4 = self.into();
|
let s: Simdf32x4 = self.into();
|
||||||
let rhs = Simdf32x4::splat(rhs);
|
let rhs = Simdf32x4::splat(rhs);
|
||||||
(s + rhs).into()
|
(s + rhs).into()
|
||||||
}
|
}
|
||||||
#[inline] fn sub_element_wise(self, rhs: f32) -> Vector4<f32> {
|
|
||||||
|
#[inline]
|
||||||
|
fn sub_element_wise(self, rhs: f32) -> Vector4<f32> {
|
||||||
let s: Simdf32x4 = self.into();
|
let s: Simdf32x4 = self.into();
|
||||||
let rhs = Simdf32x4::splat(rhs);
|
let rhs = Simdf32x4::splat(rhs);
|
||||||
(s - rhs).into()
|
(s - rhs).into()
|
||||||
}
|
}
|
||||||
#[inline] fn mul_element_wise(self, rhs: f32) -> Vector4<f32> { self * rhs }
|
|
||||||
#[inline] fn div_element_wise(self, rhs: f32) -> Vector4<f32> { self / rhs }
|
|
||||||
|
|
||||||
#[inline] fn add_assign_element_wise(&mut self, rhs: f32) {
|
#[inline]
|
||||||
|
fn mul_element_wise(self, rhs: f32) -> Vector4<f32> {
|
||||||
|
self * rhs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn div_element_wise(self, rhs: f32) -> Vector4<f32> {
|
||||||
|
self / rhs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn add_assign_element_wise(&mut self, rhs: f32) {
|
||||||
let s: Simdf32x4 = (*self).into();
|
let s: Simdf32x4 = (*self).into();
|
||||||
let rhs = Simdf32x4::splat(rhs);
|
let rhs = Simdf32x4::splat(rhs);
|
||||||
*self = (s + rhs).into();
|
*self = (s + rhs).into();
|
||||||
}
|
}
|
||||||
#[inline] fn sub_assign_element_wise(&mut self, rhs: f32) {
|
|
||||||
|
#[inline]
|
||||||
|
fn sub_assign_element_wise(&mut self, rhs: f32) {
|
||||||
let s: Simdf32x4 = (*self).into();
|
let s: Simdf32x4 = (*self).into();
|
||||||
let rhs = Simdf32x4::splat(rhs);
|
let rhs = Simdf32x4::splat(rhs);
|
||||||
*self = (s - rhs).into();
|
*self = (s - rhs).into();
|
||||||
}
|
}
|
||||||
#[inline] fn mul_assign_element_wise(&mut self, rhs: f32) { (*self) *= rhs; }
|
|
||||||
#[inline] fn div_assign_element_wise(&mut self, rhs: f32) { (*self) /= rhs; }
|
#[inline]
|
||||||
|
fn mul_assign_element_wise(&mut self, rhs: f32) {
|
||||||
|
(*self) *= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn div_assign_element_wise(&mut self, rhs: f32) {
|
||||||
|
(*self) /= rhs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "simd")]
|
#[cfg(feature = "simd")]
|
||||||
|
@ -1171,7 +1210,6 @@ impl_mint_conversions!(Vector3 { x, y, z }, Vector3);
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
impl_mint_conversions!(Vector4 { x, y, z, w }, Vector4);
|
impl_mint_conversions!(Vector4 { x, y, z, w }, Vector4);
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
mod vector2 {
|
mod vector2 {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
extern crate approx;
|
extern crate approx;
|
||||||
extern crate cgmath;
|
extern crate cgmath;
|
||||||
|
|
||||||
use cgmath::{Rad, Deg};
|
use cgmath::{Deg, Rad};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_conv() {
|
fn test_conv() {
|
||||||
|
@ -43,8 +43,14 @@ mod rad {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iter_sum() {
|
fn test_iter_sum() {
|
||||||
assert_eq!(Rad(2.0) + Rad(3.0) + Rad(4.0), [Rad(2.0), Rad(3.0), Rad(4.0)].iter().sum());
|
assert_eq!(
|
||||||
assert_eq!(Rad(2.0) + Rad(3.0) + Rad(4.0), [Rad(2.0), Rad(3.0), Rad(4.0)].iter().cloned().sum());
|
Rad(2.0) + Rad(3.0) + Rad(4.0),
|
||||||
|
[Rad(2.0), Rad(3.0), Rad(4.0)].iter().sum()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Rad(2.0) + Rad(3.0) + Rad(4.0),
|
||||||
|
[Rad(2.0), Rad(3.0), Rad(4.0)].iter().cloned().sum()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +59,13 @@ mod deg {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iter_sum() {
|
fn test_iter_sum() {
|
||||||
assert_eq!(Deg(2.0) + Deg(3.0) + Deg(4.0), [Deg(2.0), Deg(3.0), Deg(4.0)].iter().sum());
|
assert_eq!(
|
||||||
assert_eq!(Deg(2.0) + Deg(3.0) + Deg(4.0), [Deg(2.0), Deg(3.0), Deg(4.0)].iter().cloned().sum());
|
Deg(2.0) + Deg(3.0) + Deg(4.0),
|
||||||
|
[Deg(2.0), Deg(3.0), Deg(4.0)].iter().sum()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Deg(2.0) + Deg(3.0) + Deg(4.0),
|
||||||
|
[Deg(2.0), Deg(3.0), Deg(4.0)].iter().cloned().sum()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ macro_rules! impl_test_rem {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_homogeneous() {
|
fn test_homogeneous() {
|
||||||
let p = Point3::new(1.0f64, 2.0f64, 3.0f64);
|
let p = Point3::new(1.0f64, 2.0f64, 3.0f64);
|
||||||
assert_ulps_eq!(&p, &Point3::from_homogeneous(p.to_homogeneous()));
|
assert_ulps_eq!(&p, &Point3::from_homogeneous(p.to_homogeneous()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +78,12 @@ fn test_rem() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cast() {
|
fn test_cast() {
|
||||||
assert_ulps_eq!(Point1::new(0.9f64).cast().unwrap(), Point1::new(0.9f32));
|
assert_ulps_eq!(Point1::new(0.9f64).cast().unwrap(), Point1::new(0.9f32));
|
||||||
assert_ulps_eq!(Point2::new(0.9f64, 1.5).cast().unwrap(), Point2::new(0.9f32, 1.5));
|
assert_ulps_eq!(
|
||||||
assert_ulps_eq!(Point3::new(1.0f64, 2.4, -3.13).cast().unwrap(), Point3::new(1.0f32, 2.4, -3.13));
|
Point2::new(0.9f64, 1.5).cast().unwrap(),
|
||||||
|
Point2::new(0.9f32, 1.5)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Point3::new(1.0f64, 2.4, -3.13).cast().unwrap(),
|
||||||
|
Point3::new(1.0f32, 2.4, -3.13)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
extern crate cgmath;
|
extern crate cgmath;
|
||||||
|
|
||||||
use cgmath::{Vector4, ortho, Matrix4};
|
use cgmath::{ortho, Matrix4, Vector4};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ortho_scale() {
|
fn test_ortho_scale() {
|
||||||
|
@ -36,7 +36,6 @@ fn test_ortho_scale() {
|
||||||
assert_eq!(orig, Vector4::new(0f32, 0., 0., 1.));
|
assert_eq!(orig, Vector4::new(0f32, 0., 0., 1.));
|
||||||
assert_eq!(far, Vector4::new(1f32, 1., -1., 1.));
|
assert_eq!(far, Vector4::new(1f32, 1., -1., 1.));
|
||||||
|
|
||||||
|
|
||||||
let o: Matrix4<f32> = ortho(-2., 2., -2., 2., -2., 2.);
|
let o: Matrix4<f32> = ortho(-2., 2., -2., 2., -2., 2.);
|
||||||
let near = o * vec_near;
|
let near = o * vec_near;
|
||||||
let orig = o * vec_orig;
|
let orig = o * vec_orig;
|
||||||
|
|
|
@ -44,19 +44,45 @@ mod operators {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mul() {
|
fn test_mul() {
|
||||||
impl_test_mul!(2.0f32, Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(1f32) }));
|
impl_test_mul!(
|
||||||
|
2.0f32,
|
||||||
|
Quaternion::from(Euler {
|
||||||
|
x: Rad(1f32),
|
||||||
|
y: Rad(1f32),
|
||||||
|
z: Rad(1f32),
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_div() {
|
fn test_div() {
|
||||||
impl_test_div!(2.0f32, Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(1f32) }));
|
impl_test_div!(
|
||||||
|
2.0f32,
|
||||||
|
Quaternion::from(Euler {
|
||||||
|
x: Rad(1f32),
|
||||||
|
y: Rad(1f32),
|
||||||
|
z: Rad(1f32),
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iter_sum() {
|
fn test_iter_sum() {
|
||||||
let q1 = Quaternion::from(Euler { x: Rad(2f32), y: Rad(1f32), z: Rad(1f32) });
|
let q1 = Quaternion::from(Euler {
|
||||||
let q2 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(2f32), z: Rad(1f32) });
|
x: Rad(2f32),
|
||||||
let q3 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(2f32) });
|
y: Rad(1f32),
|
||||||
|
z: Rad(1f32),
|
||||||
|
});
|
||||||
|
let q2 = Quaternion::from(Euler {
|
||||||
|
x: Rad(1f32),
|
||||||
|
y: Rad(2f32),
|
||||||
|
z: Rad(1f32),
|
||||||
|
});
|
||||||
|
let q3 = Quaternion::from(Euler {
|
||||||
|
x: Rad(1f32),
|
||||||
|
y: Rad(1f32),
|
||||||
|
z: Rad(2f32),
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().sum());
|
assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().sum());
|
||||||
assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().cloned().sum());
|
assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().cloned().sum());
|
||||||
|
@ -64,9 +90,21 @@ mod operators {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iter_product() {
|
fn test_iter_product() {
|
||||||
let q1 = Quaternion::from(Euler { x: Rad(2f32), y: Rad(1f32), z: Rad(1f32) });
|
let q1 = Quaternion::from(Euler {
|
||||||
let q2 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(2f32), z: Rad(1f32) });
|
x: Rad(2f32),
|
||||||
let q3 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(2f32) });
|
y: Rad(1f32),
|
||||||
|
z: Rad(1f32),
|
||||||
|
});
|
||||||
|
let q2 = Quaternion::from(Euler {
|
||||||
|
x: Rad(1f32),
|
||||||
|
y: Rad(2f32),
|
||||||
|
z: Rad(1f32),
|
||||||
|
});
|
||||||
|
let q3 = Quaternion::from(Euler {
|
||||||
|
x: Rad(1f32),
|
||||||
|
y: Rad(1f32),
|
||||||
|
z: Rad(2f32),
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().product());
|
assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().product());
|
||||||
assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().cloned().product());
|
assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().cloned().product());
|
||||||
|
@ -79,22 +117,103 @@ mod to_from_euler {
|
||||||
use cgmath::*;
|
use cgmath::*;
|
||||||
|
|
||||||
fn check_euler(rotation: Euler<Rad<f32>>) {
|
fn check_euler(rotation: Euler<Rad<f32>>) {
|
||||||
assert_relative_eq!(Euler::from(Quaternion::from(rotation)), rotation, epsilon = 0.001);
|
assert_relative_eq!(
|
||||||
|
Euler::from(Quaternion::from(rotation)),
|
||||||
|
rotation,
|
||||||
|
epsilon = 0.001
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const HPI: f32 = f32::consts::FRAC_PI_2;
|
const HPI: f32 = f32::consts::FRAC_PI_2;
|
||||||
|
|
||||||
#[test] fn test_zero() { check_euler(Euler { x: Rad( 0f32), y: Rad( 0f32), z: Rad( 0f32) }); }
|
#[test]
|
||||||
#[test] fn test_yaw_pos_1() { check_euler(Euler { x: Rad( 0f32), y: Rad( 1f32), z: Rad( 0f32) }); }
|
fn test_zero() {
|
||||||
#[test] fn test_yaw_neg_1() { check_euler(Euler { x: Rad( 0f32), y: Rad(-1f32), z: Rad( 0f32) }); }
|
check_euler(Euler {
|
||||||
#[test] fn test_pitch_pos_1() { check_euler(Euler { x: Rad( 1f32), y: Rad( 0f32), z: Rad( 0f32) }); }
|
x: Rad(0f32),
|
||||||
#[test] fn test_pitch_neg_1() { check_euler(Euler { x: Rad(-1f32), y: Rad( 0f32), z: Rad( 0f32) }); }
|
y: Rad(0f32),
|
||||||
#[test] fn test_roll_pos_1() { check_euler(Euler { x: Rad( 0f32), y: Rad( 0f32), z: Rad( 1f32) }); }
|
z: Rad(0f32),
|
||||||
#[test] fn test_roll_neg_1() { check_euler(Euler { x: Rad( 0f32), y: Rad( 0f32), z: Rad(-1f32) }); }
|
});
|
||||||
#[test] fn test_pitch_yaw_roll_pos_1() { check_euler(Euler { x: Rad( 1f32), y: Rad( 1f32), z: Rad( 1f32) }); }
|
}
|
||||||
#[test] fn test_pitch_yaw_roll_neg_1() { check_euler(Euler { x: Rad(-1f32), y: Rad(-1f32), z: Rad(-1f32) }); }
|
#[test]
|
||||||
#[test] fn test_pitch_yaw_roll_pos_hp() { check_euler(Euler { x: Rad( 0f32), y: Rad( HPI), z: Rad( 1f32) }); }
|
fn test_yaw_pos_1() {
|
||||||
#[test] fn test_pitch_yaw_roll_neg_hp() { check_euler(Euler { x: Rad( 0f32), y: Rad( -HPI), z: Rad( 1f32) }); }
|
check_euler(Euler {
|
||||||
|
x: Rad(0f32),
|
||||||
|
y: Rad(1f32),
|
||||||
|
z: Rad(0f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_yaw_neg_1() {
|
||||||
|
check_euler(Euler {
|
||||||
|
x: Rad(0f32),
|
||||||
|
y: Rad(-1f32),
|
||||||
|
z: Rad(0f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_pitch_pos_1() {
|
||||||
|
check_euler(Euler {
|
||||||
|
x: Rad(1f32),
|
||||||
|
y: Rad(0f32),
|
||||||
|
z: Rad(0f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_pitch_neg_1() {
|
||||||
|
check_euler(Euler {
|
||||||
|
x: Rad(-1f32),
|
||||||
|
y: Rad(0f32),
|
||||||
|
z: Rad(0f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_roll_pos_1() {
|
||||||
|
check_euler(Euler {
|
||||||
|
x: Rad(0f32),
|
||||||
|
y: Rad(0f32),
|
||||||
|
z: Rad(1f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_roll_neg_1() {
|
||||||
|
check_euler(Euler {
|
||||||
|
x: Rad(0f32),
|
||||||
|
y: Rad(0f32),
|
||||||
|
z: Rad(-1f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_pitch_yaw_roll_pos_1() {
|
||||||
|
check_euler(Euler {
|
||||||
|
x: Rad(1f32),
|
||||||
|
y: Rad(1f32),
|
||||||
|
z: Rad(1f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_pitch_yaw_roll_neg_1() {
|
||||||
|
check_euler(Euler {
|
||||||
|
x: Rad(-1f32),
|
||||||
|
y: Rad(-1f32),
|
||||||
|
z: Rad(-1f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_pitch_yaw_roll_pos_hp() {
|
||||||
|
check_euler(Euler {
|
||||||
|
x: Rad(0f32),
|
||||||
|
y: Rad(HPI),
|
||||||
|
z: Rad(1f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_pitch_yaw_roll_neg_hp() {
|
||||||
|
check_euler(Euler {
|
||||||
|
x: Rad(0f32),
|
||||||
|
y: Rad(-HPI),
|
||||||
|
z: Rad(1f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod from {
|
mod from {
|
||||||
|
@ -206,7 +325,6 @@ mod rotate_from_euler {
|
||||||
assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec);
|
assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// tests that the Y rotation is done after the X
|
// tests that the Y rotation is done after the X
|
||||||
#[test]
|
#[test]
|
||||||
fn test_x_then_y() {
|
fn test_x_then_y() {
|
||||||
|
@ -258,7 +376,10 @@ mod rotate_from_axis_angle {
|
||||||
let vec = vec3(0.0, 0.0, 1.0);
|
let vec = vec3(0.0, 0.0, 1.0);
|
||||||
|
|
||||||
let rot = Quaternion::from_axis_angle(vec3(1.0, 1.0, 0.0).normalize(), Deg(90.0));
|
let rot = Quaternion::from_axis_angle(vec3(1.0, 1.0, 0.0).normalize(), Deg(90.0));
|
||||||
assert_ulps_eq!(vec3(2.0f32.sqrt() / 2.0, -2.0f32.sqrt() / 2.0, 0.0), rot * vec);
|
assert_ulps_eq!(
|
||||||
|
vec3(2.0f32.sqrt() / 2.0, -2.0f32.sqrt() / 2.0, 0.0),
|
||||||
|
rot * vec
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -266,7 +387,10 @@ mod rotate_from_axis_angle {
|
||||||
let vec = vec3(1.0, 0.0, 0.0);
|
let vec = vec3(1.0, 0.0, 0.0);
|
||||||
|
|
||||||
let rot = Quaternion::from_axis_angle(vec3(0.0, 1.0, 1.0).normalize(), Deg(-90.0));
|
let rot = Quaternion::from_axis_angle(vec3(0.0, 1.0, 1.0).normalize(), Deg(-90.0));
|
||||||
assert_ulps_eq!(vec3(0.0, -2.0f32.sqrt() / 2.0, 2.0f32.sqrt() / 2.0), rot * vec);
|
assert_ulps_eq!(
|
||||||
|
vec3(0.0, -2.0f32.sqrt() / 2.0, 2.0f32.sqrt() / 2.0),
|
||||||
|
rot * vec
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -274,7 +398,10 @@ mod rotate_from_axis_angle {
|
||||||
let vec = vec3(0.0, 1.0, 0.0);
|
let vec = vec3(0.0, 1.0, 0.0);
|
||||||
|
|
||||||
let rot = Quaternion::from_axis_angle(vec3(1.0, 0.0, 1.0).normalize(), Deg(90.0));
|
let rot = Quaternion::from_axis_angle(vec3(1.0, 0.0, 1.0).normalize(), Deg(90.0));
|
||||||
assert_ulps_eq!(vec3(-2.0f32.sqrt() / 2.0, 0.0, 2.0f32.sqrt() / 2.0), rot * vec);
|
assert_ulps_eq!(
|
||||||
|
vec3(-2.0f32.sqrt() / 2.0, 0.0, 2.0f32.sqrt() / 2.0),
|
||||||
|
rot * vec
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +464,9 @@ mod cast {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cast() {
|
fn test_cast() {
|
||||||
assert_ulps_eq!(Quaternion::new(0.9f64, 1.5, 2.4, 7.6).cast().unwrap(),
|
assert_ulps_eq!(
|
||||||
Quaternion::new(0.9f32, 1.5, 2.4, 7.6));
|
Quaternion::new(0.9f64, 1.5, 2.4, 7.6).cast().unwrap(),
|
||||||
|
Quaternion::new(0.9f32, 1.5, 2.4, 7.6)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ fn test_invert() {
|
||||||
rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5),
|
rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5),
|
||||||
disp: Vector3::new(6.0f64, -7.0, 8.0),
|
disp: Vector3::new(6.0f64, -7.0, 8.0),
|
||||||
};
|
};
|
||||||
let ti = t.inverse_transform().expect("Expected successful inversion");
|
let ti = t.inverse_transform()
|
||||||
|
.expect("Expected successful inversion");
|
||||||
let vt = t.transform_vector(v);
|
let vt = t.transform_vector(v);
|
||||||
assert_ulps_eq!(&v, &ti.transform_vector(vt));
|
assert_ulps_eq!(&v, &ti.transform_vector(vt));
|
||||||
}
|
}
|
||||||
|
@ -43,7 +44,8 @@ fn test_inverse_vector() {
|
||||||
rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5),
|
rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5),
|
||||||
disp: Vector3::new(6.0f64, -7.0, 8.0),
|
disp: Vector3::new(6.0f64, -7.0, 8.0),
|
||||||
};
|
};
|
||||||
let vt = t.inverse_transform_vector(v).expect("Expected successful inversion");
|
let vt = t.inverse_transform_vector(v)
|
||||||
|
.expect("Expected successful inversion");
|
||||||
assert_ulps_eq!(v, t.transform_vector(vt));
|
assert_ulps_eq!(v, t.transform_vector(vt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +70,8 @@ fn test_serialize() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let serialized = serde_json::to_string(&t).unwrap();
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
let deserialized: Decomposed<Vector3<f64>, Quaternion<f64>> = serde_json::from_str(&serialized).unwrap();
|
let deserialized: Decomposed<Vector3<f64>, Quaternion<f64>> =
|
||||||
|
serde_json::from_str(&serialized).unwrap();
|
||||||
|
|
||||||
assert_ulps_eq!(&t, &deserialized);
|
assert_ulps_eq!(&t, &deserialized);
|
||||||
}
|
}
|
||||||
|
|
171
tests/vector.rs
171
tests/vector.rs
|
@ -25,14 +25,26 @@ use std::iter;
|
||||||
fn test_constructor() {
|
fn test_constructor() {
|
||||||
assert_eq!(vec2(1f32, 2f32), Vector2::new(1f32, 2f32));
|
assert_eq!(vec2(1f32, 2f32), Vector2::new(1f32, 2f32));
|
||||||
assert_eq!(vec3(1f64, 2f64, 3f64), Vector3::new(1f64, 2f64, 3f64));
|
assert_eq!(vec3(1f64, 2f64, 3f64), Vector3::new(1f64, 2f64, 3f64));
|
||||||
assert_eq!(vec4(1isize, 2isize, 3isize, 4isize), Vector4::new(1isize, 2isize, 3isize, 4isize));
|
assert_eq!(
|
||||||
|
vec4(1isize, 2isize, 3isize, 4isize),
|
||||||
|
Vector4::new(1isize, 2isize, 3isize, 4isize)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_value() {
|
fn test_from_value() {
|
||||||
assert_eq!(Vector2::from_value(102isize), Vector2::new(102isize, 102isize));
|
assert_eq!(
|
||||||
assert_eq!(Vector3::from_value(22isize), Vector3::new(22isize, 22isize, 22isize));
|
Vector2::from_value(102isize),
|
||||||
assert_eq!(Vector4::from_value(76.5f64), Vector4::new(76.5f64, 76.5f64, 76.5f64, 76.5f64));
|
Vector2::new(102isize, 102isize)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Vector3::from_value(22isize),
|
||||||
|
Vector3::new(22isize, 22isize, 22isize)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Vector4::from_value(76.5f64),
|
||||||
|
Vector4::new(76.5f64, 76.5f64, 76.5f64, 76.5f64)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_test_add {
|
macro_rules! impl_test_add {
|
||||||
|
@ -132,8 +144,14 @@ fn test_rem() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dot() {
|
fn test_dot() {
|
||||||
assert_eq!(Vector2::new(1.0, 2.0).dot(Vector2::new(3.0, 4.0)), 11.0);
|
assert_eq!(Vector2::new(1.0, 2.0).dot(Vector2::new(3.0, 4.0)), 11.0);
|
||||||
assert_eq!(Vector3::new(1.0, 2.0, 3.0).dot(Vector3::new(4.0, 5.0, 6.0)), 32.0);
|
assert_eq!(
|
||||||
assert_eq!(Vector4::new(1.0, 2.0, 3.0, 4.0).dot(Vector4::new(5.0, 6.0, 7.0, 8.0)), 70.0);
|
Vector3::new(1.0, 2.0, 3.0).dot(Vector3::new(4.0, 5.0, 6.0)),
|
||||||
|
32.0
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Vector4::new(1.0, 2.0, 3.0, 4.0).dot(Vector4::new(5.0, 6.0, 7.0, 8.0)),
|
||||||
|
70.0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -149,7 +167,12 @@ fn test_sum() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iter_sum() {
|
fn test_iter_sum() {
|
||||||
impl_test_iter_sum!(Vector4 { x, y, z, w }, f32, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0));
|
impl_test_iter_sum!(
|
||||||
|
Vector4 { x, y, z, w },
|
||||||
|
f32,
|
||||||
|
2.0f32,
|
||||||
|
vec4(2.0f32, 4.0, 6.0, 8.0)
|
||||||
|
);
|
||||||
impl_test_iter_sum!(Vector3 { x, y, z }, f32, 2.0f32, vec3(2.0f32, 4.0, 6.0));
|
impl_test_iter_sum!(Vector3 { x, y, z }, f32, 2.0f32, vec3(2.0f32, 4.0, 6.0));
|
||||||
impl_test_iter_sum!(Vector2 { x, y }, f32, 2.0f32, vec2(2.0f32, 4.0));
|
impl_test_iter_sum!(Vector2 { x, y }, f32, 2.0f32, vec2(2.0f32, 4.0));
|
||||||
|
|
||||||
|
@ -162,11 +185,17 @@ fn test_iter_sum() {
|
||||||
fn test_product() {
|
fn test_product() {
|
||||||
assert_eq!(Vector2::new(1isize, 2isize).product(), 2isize);
|
assert_eq!(Vector2::new(1isize, 2isize).product(), 2isize);
|
||||||
assert_eq!(Vector3::new(1isize, 2isize, 3isize).product(), 6isize);
|
assert_eq!(Vector3::new(1isize, 2isize, 3isize).product(), 6isize);
|
||||||
assert_eq!(Vector4::new(1isize, 2isize, 3isize, 4isize).product(), 24isize);
|
assert_eq!(
|
||||||
|
Vector4::new(1isize, 2isize, 3isize, 4isize).product(),
|
||||||
|
24isize
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(Vector2::new(3.0f64, 4.0f64).product(), 12.0f64);
|
assert_eq!(Vector2::new(3.0f64, 4.0f64).product(), 12.0f64);
|
||||||
assert_eq!(Vector3::new(4.0f64, 5.0f64, 6.0f64).product(), 120.0f64);
|
assert_eq!(Vector3::new(4.0f64, 5.0f64, 6.0f64).product(), 120.0f64);
|
||||||
assert_eq!(Vector4::new(5.0f64, 6.0f64, 7.0f64, 8.0f64).product(), 1680.0f64);
|
assert_eq!(
|
||||||
|
Vector4::new(5.0f64, 6.0f64, 7.0f64, 8.0f64).product(),
|
||||||
|
1680.0f64
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -180,8 +209,17 @@ fn test_cross() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_perpendicular() {
|
fn test_is_perpendicular() {
|
||||||
assert!(Vector2::new(1.0f64, 0.0f64).is_perpendicular(Vector2::new(0.0f64, 1.0f64)));
|
assert!(Vector2::new(1.0f64, 0.0f64).is_perpendicular(Vector2::new(0.0f64, 1.0f64)));
|
||||||
assert!(Vector3::new(0.0f64, 1.0f64, 0.0f64).is_perpendicular(Vector3::new(0.0f64, 0.0f64, 1.0f64)));
|
assert!(
|
||||||
assert!(Vector4::new(1.0f64, 0.0f64, 0.0f64, 0.0f64).is_perpendicular(Vector4::new(0.0f64, 0.0f64, 0.0f64, 1.0f64)));
|
Vector3::new(0.0f64, 1.0f64, 0.0f64).is_perpendicular(Vector3::new(0.0f64, 0.0f64, 1.0f64))
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
Vector4::new(1.0f64, 0.0f64, 0.0f64, 0.0f64).is_perpendicular(Vector4::new(
|
||||||
|
0.0f64,
|
||||||
|
0.0f64,
|
||||||
|
0.0f64,
|
||||||
|
1.0f64
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -189,7 +227,7 @@ mod test_magnitude {
|
||||||
use cgmath::*;
|
use cgmath::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vector2(){
|
fn test_vector2() {
|
||||||
let (a, a_res) = (Vector2::new(3.0f64, 4.0f64), 5.0f64); // (3, 4, 5) Pythagorean triple
|
let (a, a_res) = (Vector2::new(3.0f64, 4.0f64), 5.0f64); // (3, 4, 5) Pythagorean triple
|
||||||
let (b, b_res) = (Vector2::new(5.0f64, 12.0f64), 13.0f64); // (5, 12, 13) Pythagorean triple
|
let (b, b_res) = (Vector2::new(5.0f64, 12.0f64), 13.0f64); // (5, 12, 13) Pythagorean triple
|
||||||
|
|
||||||
|
@ -201,7 +239,7 @@ mod test_magnitude {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vector3(){
|
fn test_vector3() {
|
||||||
let (a, a_res) = (Vector3::new(2.0f64, 3.0f64, 6.0f64), 7.0f64); // (2, 3, 6, 7) Pythagorean quadruple
|
let (a, a_res) = (Vector3::new(2.0f64, 3.0f64, 6.0f64), 7.0f64); // (2, 3, 6, 7) Pythagorean quadruple
|
||||||
let (b, b_res) = (Vector3::new(1.0f64, 4.0f64, 8.0f64), 9.0f64); // (1, 4, 8, 9) Pythagorean quadruple
|
let (b, b_res) = (Vector3::new(1.0f64, 4.0f64, 8.0f64), 9.0f64); // (1, 4, 8, 9) Pythagorean quadruple
|
||||||
|
|
||||||
|
@ -213,7 +251,7 @@ mod test_magnitude {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vector4(){
|
fn test_vector4() {
|
||||||
let (a, a_res) = (Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64), 11.0f64); // (1, 2, 4, 10, 11) Pythagorean quintuple
|
let (a, a_res) = (Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64), 11.0f64); // (1, 2, 4, 10, 11) Pythagorean quintuple
|
||||||
let (b, b_res) = (Vector4::new(1.0f64, 2.0f64, 8.0f64, 10.0f64), 13.0f64); // (1, 2, 8, 10, 13) Pythagorean quintuple
|
let (b, b_res) = (Vector4::new(1.0f64, 2.0f64, 8.0f64, 10.0f64), 13.0f64); // (1, 2, 8, 10, 13) Pythagorean quintuple
|
||||||
|
|
||||||
|
@ -227,37 +265,106 @@ mod test_magnitude {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_angle() {
|
fn test_angle() {
|
||||||
assert_ulps_eq!(Vector2::new(1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
assert_ulps_eq!(
|
||||||
assert_ulps_eq!(Vector2::new(10.0f64, 0.0f64).angle(Vector2::new(0.0f64, 5.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
Vector2::new(1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)),
|
||||||
assert_ulps_eq!(Vector2::new(-1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)), &-Rad(f64::consts::FRAC_PI_2));
|
&Rad(f64::consts::FRAC_PI_2)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector2::new(10.0f64, 0.0f64).angle(Vector2::new(0.0f64, 5.0f64)),
|
||||||
|
&Rad(f64::consts::FRAC_PI_2)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector2::new(-1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)),
|
||||||
|
&-Rad(f64::consts::FRAC_PI_2)
|
||||||
|
);
|
||||||
|
|
||||||
assert_ulps_eq!(Vector3::new(1.0f64, 0.0f64, 1.0f64).angle(Vector3::new(1.0f64, 1.0f64, 0.0f64)), &Rad(f64::consts::FRAC_PI_3));
|
assert_ulps_eq!(
|
||||||
assert_ulps_eq!(Vector3::new(10.0f64, 0.0f64, 10.0f64).angle(Vector3::new(5.0f64, 5.0f64, 0.0f64)), &Rad(f64::consts::FRAC_PI_3));
|
Vector3::new(1.0f64, 0.0f64, 1.0f64).angle(Vector3::new(1.0f64, 1.0f64, 0.0f64)),
|
||||||
assert_ulps_eq!(Vector3::new(-1.0f64, 0.0f64, -1.0f64).angle(Vector3::new(1.0f64, -1.0f64, 0.0f64)), &Rad(2.0f64 * f64::consts::FRAC_PI_3));
|
&Rad(f64::consts::FRAC_PI_3)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector3::new(10.0f64, 0.0f64, 10.0f64).angle(Vector3::new(5.0f64, 5.0f64, 0.0f64)),
|
||||||
|
&Rad(f64::consts::FRAC_PI_3)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector3::new(-1.0f64, 0.0f64, -1.0f64).angle(Vector3::new(1.0f64, -1.0f64, 0.0f64)),
|
||||||
|
&Rad(2.0f64 * f64::consts::FRAC_PI_3)
|
||||||
|
);
|
||||||
|
|
||||||
assert_ulps_eq!(Vector4::new(1.0f64, 0.0f64, 1.0f64, 0.0f64).angle(Vector4::new(0.0f64, 1.0f64, 0.0f64, 1.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
assert_ulps_eq!(
|
||||||
assert_ulps_eq!(Vector4::new(10.0f64, 0.0f64, 10.0f64, 0.0f64).angle(Vector4::new(0.0f64, 5.0f64, 0.0f64, 5.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
Vector4::new(1.0f64, 0.0f64, 1.0f64, 0.0f64).angle(Vector4::new(
|
||||||
assert_ulps_eq!(Vector4::new(-1.0f64, 0.0f64, -1.0f64, 0.0f64).angle(Vector4::new(0.0f64, 1.0f64, 0.0f64, 1.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
0.0f64,
|
||||||
|
1.0f64,
|
||||||
|
0.0f64,
|
||||||
|
1.0f64
|
||||||
|
)),
|
||||||
|
&Rad(f64::consts::FRAC_PI_2)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector4::new(10.0f64, 0.0f64, 10.0f64, 0.0f64).angle(Vector4::new(
|
||||||
|
0.0f64,
|
||||||
|
5.0f64,
|
||||||
|
0.0f64,
|
||||||
|
5.0f64
|
||||||
|
)),
|
||||||
|
&Rad(f64::consts::FRAC_PI_2)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector4::new(-1.0f64, 0.0f64, -1.0f64, 0.0f64).angle(Vector4::new(
|
||||||
|
0.0f64,
|
||||||
|
1.0f64,
|
||||||
|
0.0f64,
|
||||||
|
1.0f64
|
||||||
|
)),
|
||||||
|
&Rad(f64::consts::FRAC_PI_2)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_normalize() {
|
fn test_normalize() {
|
||||||
// TODO: test normalize_to, normalize_sel.0, and normalize_self_to
|
// TODO: test normalize_to, normalize_sel.0, and normalize_self_to
|
||||||
assert_ulps_eq!(Vector2::new(3.0f64, 4.0f64).normalize(), &Vector2::new(3.0/5.0, 4.0/5.0));
|
assert_ulps_eq!(
|
||||||
assert_ulps_eq!(Vector3::new(2.0f64, 3.0f64, 6.0f64).normalize(), &Vector3::new(2.0/7.0, 3.0/7.0, 6.0/7.0));
|
Vector2::new(3.0f64, 4.0f64).normalize(),
|
||||||
assert_ulps_eq!(Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64).normalize(), &Vector4::new(1.0/11.0, 2.0/11.0, 4.0/11.0, 10.0/11.0));
|
&Vector2::new(3.0 / 5.0, 4.0 / 5.0)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector3::new(2.0f64, 3.0f64, 6.0f64).normalize(),
|
||||||
|
&Vector3::new(2.0 / 7.0, 3.0 / 7.0, 6.0 / 7.0)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64).normalize(),
|
||||||
|
&Vector4::new(1.0 / 11.0, 2.0 / 11.0, 4.0 / 11.0, 10.0 / 11.0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_project_on() {
|
fn test_project_on() {
|
||||||
assert_ulps_eq!(Vector2::new(-1.0f64, 5.0).project_on(Vector2::new(2.0, 4.0)), &Vector2::new(9.0/5.0, 18.0/5.0));
|
assert_ulps_eq!(
|
||||||
assert_ulps_eq!(Vector3::new(5.0f64, 6.0, 7.0).project_on(Vector3::new(1.0, 1.0, 1.0)), &Vector3::new(6.0, 6.0, 6.0));
|
Vector2::new(-1.0f64, 5.0).project_on(Vector2::new(2.0, 4.0)),
|
||||||
assert_ulps_eq!(Vector4::new(0.0f64, -5.0, 5.0, 5.0).project_on(Vector4::new(0.0, 1.0, 0.0, 0.5)), &Vector4::new(0.0, -2.0, 0.0, -1.0));
|
&Vector2::new(9.0 / 5.0, 18.0 / 5.0)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector3::new(5.0f64, 6.0, 7.0).project_on(Vector3::new(1.0, 1.0, 1.0)),
|
||||||
|
&Vector3::new(6.0, 6.0, 6.0)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector4::new(0.0f64, -5.0, 5.0, 5.0).project_on(Vector4::new(0.0, 1.0, 0.0, 0.5)),
|
||||||
|
&Vector4::new(0.0, -2.0, 0.0, -1.0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cast() {
|
fn test_cast() {
|
||||||
assert_ulps_eq!(Vector2::new(0.9f64, 1.5).cast().unwrap(), Vector2::new(0.9f32, 1.5));
|
assert_ulps_eq!(
|
||||||
assert_ulps_eq!(Vector3::new(1.0f64, 2.4, -3.13).cast().unwrap(), Vector3::new(1.0f32, 2.4, -3.13));
|
Vector2::new(0.9f64, 1.5).cast().unwrap(),
|
||||||
assert_ulps_eq!(Vector4::new(13.5f64, -4.6, -8.3, 2.41).cast().unwrap(), Vector4::new(13.5f32, -4.6, -8.3, 2.41));
|
Vector2::new(0.9f32, 1.5)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector3::new(1.0f64, 2.4, -3.13).cast().unwrap(),
|
||||||
|
Vector3::new(1.0f32, 2.4, -3.13)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector4::new(13.5f64, -4.6, -8.3, 2.41).cast().unwrap(),
|
||||||
|
Vector4::new(13.5f32, -4.6, -8.3, 2.41)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,18 @@ use std::f32;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_constructor() {
|
fn test_constructor() {
|
||||||
assert_eq!(vec4(1f32, 2f32, 3f32, 4f32), Vector4::new(1f32, 2f32, 3f32, 4f32));
|
assert_eq!(
|
||||||
|
vec4(1f32, 2f32, 3f32, 4f32),
|
||||||
|
Vector4::new(1f32, 2f32, 3f32, 4f32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_value() {
|
fn test_from_value() {
|
||||||
assert_eq!(Vector4::from_value(76.5f32), Vector4::new(76.5f32, 76.5f32, 76.5f32, 76.5f32));
|
assert_eq!(
|
||||||
|
Vector4::from_value(76.5f32),
|
||||||
|
Vector4::new(76.5f32, 76.5f32, 76.5f32, 76.5f32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_test_add {
|
macro_rules! impl_test_add {
|
||||||
|
@ -84,32 +90,60 @@ macro_rules! impl_test_rem {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add() {
|
fn test_add() {
|
||||||
impl_test_add!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32));
|
impl_test_add!(
|
||||||
|
Vector4 { x, y, z, w },
|
||||||
|
2.0f32,
|
||||||
|
vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sub() {
|
fn test_sub() {
|
||||||
impl_test_sub!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32));
|
impl_test_sub!(
|
||||||
|
Vector4 { x, y, z, w },
|
||||||
|
2.0f32,
|
||||||
|
vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mul() {
|
fn test_mul() {
|
||||||
impl_test_mul!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32));
|
impl_test_mul!(
|
||||||
|
Vector4 { x, y, z, w },
|
||||||
|
2.0f32,
|
||||||
|
vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_div() {
|
fn test_div() {
|
||||||
impl_test_div!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32));
|
impl_test_div!(
|
||||||
|
Vector4 { x, y, z, w },
|
||||||
|
2.0f32,
|
||||||
|
vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rem() {
|
fn test_rem() {
|
||||||
impl_test_rem!(Vector4 { x, y, z, w }, 2.0f32, vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32));
|
impl_test_rem!(
|
||||||
|
Vector4 { x, y, z, w },
|
||||||
|
2.0f32,
|
||||||
|
vec4(2.0f32, 4.0f32, 6.0f32, 8.0f32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dot() {
|
fn test_dot() {
|
||||||
assert_eq!(Vector4::new(1.0f32, 2.0f32, 3.0f32, 4.0f32).dot(Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32)), 70.0f32);
|
assert_eq!(
|
||||||
|
Vector4::new(1.0f32, 2.0f32, 3.0f32, 4.0f32).dot(Vector4::new(
|
||||||
|
5.0f32,
|
||||||
|
6.0f32,
|
||||||
|
7.0f32,
|
||||||
|
8.0f32
|
||||||
|
)),
|
||||||
|
70.0f32
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -123,12 +157,22 @@ fn test_sum() {
|
||||||
fn test_product() {
|
fn test_product() {
|
||||||
assert_eq!(Vector4::new(1f32, 2f32, 3f32, 4f32).product(), 24f32);
|
assert_eq!(Vector4::new(1f32, 2f32, 3f32, 4f32).product(), 24f32);
|
||||||
|
|
||||||
assert_eq!(Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32).product(), 1680.0f32);
|
assert_eq!(
|
||||||
|
Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32).product(),
|
||||||
|
1680.0f32
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_perpendicular() {
|
fn test_is_perpendicular() {
|
||||||
assert!(Vector4::new(1.0f32, 0.0f32, 0.0f32, 0.0f32).is_perpendicular(Vector4::new(0.0f32, 0.0f32, 0.0f32, 1.0f32)));
|
assert!(
|
||||||
|
Vector4::new(1.0f32, 0.0f32, 0.0f32, 0.0f32).is_perpendicular(Vector4::new(
|
||||||
|
0.0f32,
|
||||||
|
0.0f32,
|
||||||
|
0.0f32,
|
||||||
|
1.0f32
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -136,7 +180,7 @@ mod test_magnitude {
|
||||||
use cgmath::*;
|
use cgmath::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vector4(){
|
fn test_vector4() {
|
||||||
let (a, a_res) = (Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32), 11.0f32); // (1, 2, 4, 10, 11) Pythagorean quintuple
|
let (a, a_res) = (Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32), 11.0f32); // (1, 2, 4, 10, 11) Pythagorean quintuple
|
||||||
let (b, b_res) = (Vector4::new(1.0f32, 2.0f32, 8.0f32, 10.0f32), 13.0f32); // (1, 2, 8, 10, 13) Pythagorean quintuple
|
let (b, b_res) = (Vector4::new(1.0f32, 2.0f32, 8.0f32, 10.0f32), 13.0f32); // (1, 2, 8, 10, 13) Pythagorean quintuple
|
||||||
|
|
||||||
|
@ -150,26 +194,69 @@ mod test_magnitude {
|
||||||
{
|
{
|
||||||
let a = Vector4::new(1f32, 4f32, 9f32, 16f32);
|
let a = Vector4::new(1f32, 4f32, 9f32, 16f32);
|
||||||
assert_ulps_eq!(a.sqrt_element_wide(), Vector4::new(1f32, 2f32, 3f32, 4f32));
|
assert_ulps_eq!(a.sqrt_element_wide(), Vector4::new(1f32, 2f32, 3f32, 4f32));
|
||||||
assert_relative_eq!(a.sqrt_element_wide().recip_element_wide(), Vector4::new(1f32, 1f32/2f32, 1f32/3f32, 1f32/4f32), max_relative = 0.005f32);
|
assert_relative_eq!(
|
||||||
assert_relative_eq!(a.rsqrt_element_wide(), Vector4::new(1f32, 1f32/2f32, 1f32/3f32, 1f32/4f32), max_relative= 0.005f32);
|
a.sqrt_element_wide().recip_element_wide(),
|
||||||
|
Vector4::new(1f32, 1f32 / 2f32, 1f32 / 3f32, 1f32 / 4f32),
|
||||||
|
max_relative = 0.005f32
|
||||||
|
);
|
||||||
|
assert_relative_eq!(
|
||||||
|
a.rsqrt_element_wide(),
|
||||||
|
Vector4::new(1f32, 1f32 / 2f32, 1f32 / 3f32, 1f32 / 4f32),
|
||||||
|
max_relative = 0.005f32
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_angle() {
|
fn test_angle() {
|
||||||
assert_ulps_eq!(Vector4::new(1.0f32, 0.0f32, 1.0f32, 0.0f32).angle(Vector4::new(0.0f32, 1.0f32, 0.0f32, 1.0f32)), &Rad(f32::consts::FRAC_PI_2));
|
assert_ulps_eq!(
|
||||||
assert_ulps_eq!(Vector4::new(10.0f32, 0.0f32, 10.0f32, 0.0f32).angle(Vector4::new(0.0f32, 5.0f32, 0.0f32, 5.0f32)), &Rad(f32::consts::FRAC_PI_2));
|
Vector4::new(1.0f32, 0.0f32, 1.0f32, 0.0f32).angle(Vector4::new(
|
||||||
assert_ulps_eq!(Vector4::new(-1.0f32, 0.0f32, -1.0f32, 0.0f32).angle(Vector4::new(0.0f32, 1.0f32, 0.0f32, 1.0f32)), &Rad(f32::consts::FRAC_PI_2));
|
0.0f32,
|
||||||
|
1.0f32,
|
||||||
|
0.0f32,
|
||||||
|
1.0f32
|
||||||
|
)),
|
||||||
|
&Rad(f32::consts::FRAC_PI_2)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector4::new(10.0f32, 0.0f32, 10.0f32, 0.0f32).angle(Vector4::new(
|
||||||
|
0.0f32,
|
||||||
|
5.0f32,
|
||||||
|
0.0f32,
|
||||||
|
5.0f32
|
||||||
|
)),
|
||||||
|
&Rad(f32::consts::FRAC_PI_2)
|
||||||
|
);
|
||||||
|
assert_ulps_eq!(
|
||||||
|
Vector4::new(-1.0f32, 0.0f32, -1.0f32, 0.0f32).angle(Vector4::new(
|
||||||
|
0.0f32,
|
||||||
|
1.0f32,
|
||||||
|
0.0f32,
|
||||||
|
1.0f32
|
||||||
|
)),
|
||||||
|
&Rad(f32::consts::FRAC_PI_2)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_normalize() {
|
fn test_normalize() {
|
||||||
// TODO: test normalize_to, normalize_sel.0f32, and normalize_self_to
|
// TODO: test normalize_to, normalize_sel.0f32, and normalize_self_to
|
||||||
assert_ulps_eq!(Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32).normalize(), &Vector4::new(1.0f32/11.0f32, 2.0f32/11.0f32, 4.0f32/11.0f32, 10.0f32/11.0f32));
|
assert_ulps_eq!(
|
||||||
|
Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32).normalize(),
|
||||||
|
&Vector4::new(
|
||||||
|
1.0f32 / 11.0f32,
|
||||||
|
2.0f32 / 11.0f32,
|
||||||
|
4.0f32 / 11.0f32,
|
||||||
|
10.0f32 / 11.0f32
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cast() {
|
fn test_cast() {
|
||||||
assert_ulps_eq!(Vector4::new(13.5f32, -4.6, -8.3, 2.41).cast().unwrap(), Vector4::new(13.5f32, -4.6, -8.3, 2.41));
|
assert_ulps_eq!(
|
||||||
|
Vector4::new(13.5f32, -4.6, -8.3, 2.41).cast().unwrap(),
|
||||||
|
Vector4::new(13.5f32, -4.6, -8.3, 2.41)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue