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]
|
||||
|
||||
## [v0.16.0] - 2018-01-03
|
||||
|
||||
### Added
|
||||
|
||||
- Add `InnerSpace::project_on`
|
||||
- Add `Array::len`
|
||||
- Re-export `Bounded` and implement for vectors, points, and angles
|
||||
- Add vector subtraction to `EuclideanSpace`
|
||||
- Add swizzle functions behinde that `"swizzle"` feature
|
||||
- Add `Matrix4::look_at_dir`
|
||||
|
||||
### Changed
|
||||
|
||||
- Return `Option` from cast functions
|
||||
|
||||
## [v0.15.0] - 2017-07-30
|
||||
|
||||
### Added
|
||||
|
@ -274,7 +289,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||
|
||||
## 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.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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
|
||||
name = "cgmath"
|
||||
version = "0.15.0"
|
||||
version = "0.16.0"
|
||||
authors = ["Brendan Zabarauskas <bjzaba@yahoo.com.au>"]
|
||||
license = "Apache-2.0"
|
||||
description = "A linear algebra and mathematics library for computer graphics."
|
||||
|
@ -22,12 +22,12 @@ swizzle = []
|
|||
|
||||
[dependencies]
|
||||
approx = "0.1"
|
||||
mint = { version = "0.4.1", optional = true }
|
||||
mint = { version = "0.5", optional = true }
|
||||
num-traits = "0.1"
|
||||
rand = "0.4"
|
||||
serde = { version = "1.0", features = ["serde_derive"], optional = true }
|
||||
simd = { version = "0.2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
glium = "0.17"
|
||||
glium = "0.19"
|
||||
serde_json = "1.0"
|
||||
|
|
|
@ -15,16 +15,17 @@
|
|||
|
||||
#![feature(test)]
|
||||
|
||||
extern crate cgmath;
|
||||
extern crate rand;
|
||||
extern crate test;
|
||||
extern crate cgmath;
|
||||
|
||||
use rand::{IsaacRng, Rng};
|
||||
use test::Bencher;
|
||||
use cgmath::*;
|
||||
|
||||
#[path="common/macros.rs"]
|
||||
#[macro_use] mod macros;
|
||||
#[path = "common/macros.rs"]
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
fn bench_from_axis_angle<T: Rotation3<f32>>(bh: &mut Bencher) {
|
||||
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);
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
@ -55,7 +57,19 @@ fn _bench_rot3_from_axisangle(bh: &mut Bencher) {
|
|||
bench_from_axis_angle::<Basis3<f32>>(bh)
|
||||
}
|
||||
|
||||
bench_construction!(_bench_rot2_from_axisangle, Basis2<f32>, Basis2::from_angle [ angle: Rad<f32> ]);
|
||||
bench_construction!(
|
||||
_bench_rot2_from_axisangle,
|
||||
Basis2<f32>,
|
||||
Basis2::from_angle[angle: Rad<f32>]
|
||||
);
|
||||
|
||||
bench_construction!(_bench_quat_from_euler_angles, Quaternion<f32>, Quaternion::from [src: Euler<Rad<f32>>]);
|
||||
bench_construction!(_bench_rot3_from_euler_angles, Basis3<f32>, Basis3::from [src: Euler<Rad<f32>>]);
|
||||
bench_construction!(
|
||||
_bench_quat_from_euler_angles,
|
||||
Quaternion<f32>,
|
||||
Quaternion::from[src: Euler<Rad<f32>>]
|
||||
);
|
||||
bench_construction!(
|
||||
_bench_rot3_from_euler_angles,
|
||||
Basis3<f32>,
|
||||
Basis3::from[src: Euler<Rad<f32>>]
|
||||
);
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
|
||||
#![feature(test)]
|
||||
|
||||
extern crate cgmath;
|
||||
extern crate rand;
|
||||
extern crate test;
|
||||
extern crate cgmath;
|
||||
|
||||
use rand::{IsaacRng, Rng};
|
||||
use std::ops::*;
|
||||
|
@ -25,8 +25,9 @@ use test::Bencher;
|
|||
|
||||
use cgmath::*;
|
||||
|
||||
#[path="common/macros.rs"]
|
||||
#[macro_use] mod macros;
|
||||
#[path = "common/macros.rs"]
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
bench_binop!(_bench_matrix2_mul_m, Matrix2<f32>, Matrix2<f32>, mul);
|
||||
bench_binop!(_bench_matrix3_mul_m, Matrix3<f32>, Matrix3<f32>, mul);
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
|
||||
#![feature(test)]
|
||||
|
||||
extern crate cgmath;
|
||||
extern crate rand;
|
||||
extern crate test;
|
||||
extern crate cgmath;
|
||||
|
||||
use rand::{IsaacRng, Rng};
|
||||
use std::ops::*;
|
||||
|
@ -25,8 +25,9 @@ use test::Bencher;
|
|||
|
||||
use cgmath::*;
|
||||
|
||||
#[path="common/macros.rs"]
|
||||
#[macro_use] mod macros;
|
||||
#[path = "common/macros.rs"]
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
bench_binop!(_bench_quat_add_q, Quaternion<f32>, Quaternion<f32>, add);
|
||||
bench_binop!(_bench_quat_sub_q, Quaternion<f32>, Quaternion<f32>, sub);
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
|
||||
#![feature(test)]
|
||||
|
||||
extern crate cgmath;
|
||||
extern crate rand;
|
||||
extern crate test;
|
||||
extern crate cgmath;
|
||||
|
||||
use rand::{IsaacRng, Rng};
|
||||
use std::ops::*;
|
||||
|
@ -25,8 +25,9 @@ use test::Bencher;
|
|||
|
||||
use cgmath::*;
|
||||
|
||||
#[path="common/macros.rs"]
|
||||
#[macro_use] mod macros;
|
||||
#[path = "common/macros.rs"]
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
bench_binop!(_bench_vector2_add_v, Vector2<f32>, Vector2<f32>, add);
|
||||
bench_binop!(_bench_vector3_add_v, Vector3<f32>, Vector3<f32>, add);
|
||||
|
|
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))]
|
||||
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]
|
||||
fn from(rad: Rad<S>) -> Deg<S> {
|
||||
Deg(rad.0 * cast(180.0 / f64::consts::PI).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<Deg<S>> for Rad<S> where S: BaseFloat {
|
||||
impl<S> From<Deg<S>> for Rad<S>
|
||||
where
|
||||
S: BaseFloat,
|
||||
{
|
||||
#[inline]
|
||||
fn from(deg: Deg<S>) -> Rad<S> {
|
||||
Rad(deg.0 * cast(f64::consts::PI / 180.0).unwrap())
|
||||
|
|
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
|
||||
/// [convert]: #defining-rotations-using-euler-angles
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Euler<A: Angle> {
|
||||
/// 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]
|
||||
fn relative_eq(&self, other: &Self, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool {
|
||||
A::relative_eq(&self.x, &other.x, epsilon, max_relative) &&
|
||||
A::relative_eq(&self.y, &other.y, epsilon, max_relative) &&
|
||||
A::relative_eq(&self.z, &other.z, epsilon, max_relative)
|
||||
A::relative_eq(&self.x, &other.x, epsilon, max_relative)
|
||||
&& A::relative_eq(&self.y, &other.y, epsilon, max_relative)
|
||||
&& A::relative_eq(&self.z, &other.z, epsilon, max_relative)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ulps_eq(&self, other: &Self, epsilon: A::Epsilon, max_ulps: u32) -> bool {
|
||||
A::ulps_eq(&self.x, &other.x, epsilon, max_ulps) &&
|
||||
A::ulps_eq(&self.y, &other.y, epsilon, max_ulps) &&
|
||||
A::ulps_eq(&self.z, &other.z, epsilon, max_ulps)
|
||||
A::ulps_eq(&self.x, &other.x, epsilon, max_ulps)
|
||||
&& A::ulps_eq(&self.y, &other.y, epsilon, max_ulps)
|
||||
&& A::ulps_eq(&self.z, &other.z, epsilon, max_ulps)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Angle + Rand> Rand for Euler<A> {
|
||||
#[inline]
|
||||
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")]
|
||||
pub extern crate mint;
|
||||
|
||||
pub extern crate num_traits;
|
||||
extern crate rand;
|
||||
pub extern crate num_traits;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[macro_use]
|
||||
|
@ -76,7 +76,7 @@ pub use structure::*;
|
|||
|
||||
pub use matrix::{Matrix2, Matrix3, Matrix4};
|
||||
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 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};
|
||||
|
||||
/// 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
|
||||
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
|
||||
//! not have a fixed position.
|
||||
|
||||
use num_traits::{NumCast, Bounded};
|
||||
use num_traits::{Bounded, NumCast};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ops::*;
|
||||
|
@ -25,7 +25,7 @@ use std::ops::*;
|
|||
use structure::*;
|
||||
|
||||
use approx::ApproxEq;
|
||||
use num::{BaseNum, BaseFloat};
|
||||
use num::{BaseFloat, BaseNum};
|
||||
use vector::{Vector1, Vector2, Vector3, Vector4};
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
|
@ -95,7 +95,7 @@ impl<S: BaseNum> Point3<S> {
|
|||
#[inline]
|
||||
pub fn from_homogeneous(v: Vector4<S>) -> Point3<S> {
|
||||
let e = v.truncate() * (S::one() / v.w);
|
||||
Point3::new(e.x, e.y, e.z) //FIXME
|
||||
Point3::new(e.x, e.y, e.z) //FIXME
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use num_traits::{Zero};
|
||||
use num_traits::Zero;
|
||||
use num_traits::cast;
|
||||
|
||||
use structure::Angle;
|
||||
|
@ -26,12 +26,17 @@ use num::BaseFloat;
|
|||
///
|
||||
/// This is the equivalent to the [gluPerspective]
|
||||
/// (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 {
|
||||
fovy: fovy.into(),
|
||||
fovy: fovy.into(),
|
||||
aspect: aspect,
|
||||
near: near,
|
||||
far: far,
|
||||
near: near,
|
||||
far: far,
|
||||
}.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.
|
||||
pub fn frustum<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
||||
Perspective {
|
||||
left: left,
|
||||
right: right,
|
||||
left: left,
|
||||
right: right,
|
||||
bottom: bottom,
|
||||
top: top,
|
||||
near: near,
|
||||
far: far,
|
||||
top: top,
|
||||
near: near,
|
||||
far: far,
|
||||
}.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.
|
||||
pub fn ortho<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
||||
Ortho {
|
||||
left: left,
|
||||
right: right,
|
||||
left: left,
|
||||
right: right,
|
||||
bottom: bottom,
|
||||
top: top,
|
||||
near: near,
|
||||
far: far,
|
||||
top: top,
|
||||
near: near,
|
||||
far: far,
|
||||
}.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 = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct PerspectiveFov<S> {
|
||||
pub fovy: Rad<S>,
|
||||
pub fovy: Rad<S>,
|
||||
pub aspect: S,
|
||||
pub near: S,
|
||||
pub far: S,
|
||||
pub near: S,
|
||||
pub far: S,
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> PerspectiveFov<S> {
|
||||
|
@ -84,24 +89,49 @@ impl<S: BaseFloat> PerspectiveFov<S> {
|
|||
let xmax = ymax * self.aspect;
|
||||
|
||||
Perspective {
|
||||
left: -xmax,
|
||||
right: xmax,
|
||||
left: -xmax,
|
||||
right: xmax,
|
||||
bottom: -ymax,
|
||||
top: ymax,
|
||||
near: self.near.clone(),
|
||||
far: self.far.clone(),
|
||||
top: ymax,
|
||||
near: self.near.clone(),
|
||||
far: self.far.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<PerspectiveFov<S>> for Matrix4<S> {
|
||||
fn from(persp: PerspectiveFov<S>) -> Matrix4<S> {
|
||||
assert!(persp.fovy > Rad::zero(), "The vertical field of view cannot be below zero, found: {:?}", persp.fovy);
|
||||
assert!(persp.fovy < Rad::turn_div_2(), "The vertical field of view cannot be greater than a half turn, found: {:?}", persp.fovy);
|
||||
assert!(persp.aspect > S::zero(), "The aspect ratio cannot be below zero, found: {:?}", persp.aspect);
|
||||
assert!(persp.near > S::zero(), "The near plane distance cannot be below zero, found: {:?}", persp.near);
|
||||
assert!(persp.far > S::zero(), "The far plane distance cannot be below zero, found: {:?}", persp.far);
|
||||
assert!(persp.far > persp.near, "The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}", persp.far, persp.near);
|
||||
assert!(
|
||||
persp.fovy > Rad::zero(),
|
||||
"The vertical field of view cannot be below zero, found: {:?}",
|
||||
persp.fovy
|
||||
);
|
||||
assert!(
|
||||
persp.fovy < Rad::turn_div_2(),
|
||||
"The vertical field of view cannot be greater than a half turn, found: {:?}",
|
||||
persp.fovy
|
||||
);
|
||||
assert!(
|
||||
persp.aspect > S::zero(),
|
||||
"The aspect ratio cannot be below zero, found: {:?}",
|
||||
persp.aspect
|
||||
);
|
||||
assert!(
|
||||
persp.near > S::zero(),
|
||||
"The near plane distance cannot be below zero, found: {:?}",
|
||||
persp.near
|
||||
);
|
||||
assert!(
|
||||
persp.far > S::zero(),
|
||||
"The far plane distance cannot be below zero, found: {:?}",
|
||||
persp.far
|
||||
);
|
||||
assert!(
|
||||
persp.far > persp.near,
|
||||
"The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}",
|
||||
persp.far,
|
||||
persp.near
|
||||
);
|
||||
|
||||
let two: S = cast(2).unwrap();
|
||||
let f = Rad::cot(persp.fovy / two);
|
||||
|
@ -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 c3r3 = S::zero();
|
||||
|
||||
Matrix4::new(c0r0, c0r1, c0r2, c0r3,
|
||||
c1r0, c1r1, c1r2, c1r3,
|
||||
c2r0, c2r1, c2r2, c2r3,
|
||||
c3r0, c3r1, c3r2, c3r3)
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
Matrix4::new(
|
||||
c0r0, c0r1, c0r2, c0r3,
|
||||
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)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Perspective<S> {
|
||||
pub left: S,
|
||||
pub right: S,
|
||||
pub left: S,
|
||||
pub right: S,
|
||||
pub bottom: S,
|
||||
pub top: S,
|
||||
pub near: S,
|
||||
pub far: S,
|
||||
pub top: S,
|
||||
pub near: S,
|
||||
pub far: S,
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> From<Perspective<S>> for Matrix4<S> {
|
||||
fn from(persp: Perspective<S>) -> Matrix4<S> {
|
||||
assert!(persp.left <= persp.right, "`left` cannot be greater than `right`, found: left: {:?} right: {:?}", persp.left, persp.right);
|
||||
assert!(persp.bottom <= persp.top, "`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}", persp.bottom, persp.top);
|
||||
assert!(persp.near <= persp.far, "`near` cannot be greater than `far`, found: near: {:?} far: {:?}", persp.near, persp.far);
|
||||
assert!(
|
||||
persp.left <= persp.right,
|
||||
"`left` cannot be greater than `right`, found: left: {:?} right: {:?}",
|
||||
persp.left,
|
||||
persp.right
|
||||
);
|
||||
assert!(
|
||||
persp.bottom <= persp.top,
|
||||
"`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}",
|
||||
persp.bottom,
|
||||
persp.top
|
||||
);
|
||||
assert!(
|
||||
persp.near <= persp.far,
|
||||
"`near` cannot be greater than `far`, found: near: {:?} far: {:?}",
|
||||
persp.near,
|
||||
persp.far
|
||||
);
|
||||
|
||||
let two: S = cast(2i8).unwrap();
|
||||
|
||||
|
@ -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 c3r3 = S::zero();
|
||||
|
||||
Matrix4::new(c0r0, c0r1, c0r2, c0r3,
|
||||
c1r0, c1r1, c1r2, c1r3,
|
||||
c2r0, c2r1, c2r2, c2r3,
|
||||
c3r0, c3r1, c3r2, c3r3)
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
Matrix4::new(
|
||||
c0r0, c0r1, c0r2, c0r3,
|
||||
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)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Ortho<S> {
|
||||
pub left: S,
|
||||
pub right: S,
|
||||
pub left: S,
|
||||
pub right: S,
|
||||
pub bottom: S,
|
||||
pub top: S,
|
||||
pub near: S,
|
||||
pub far: S,
|
||||
pub top: S,
|
||||
pub near: S,
|
||||
pub far: 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 c3r3 = S::one();
|
||||
|
||||
Matrix4::new(c0r0, c0r1, c0r2, c0r3,
|
||||
c1r0, c1r1, c1r2, c1r3,
|
||||
c2r0, c2r1, c2r2, c2r3,
|
||||
c3r0, c3r1, c3r2, c3r3)
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
Matrix4::new(
|
||||
c0r0, c0r1, c0r2, c0r3,
|
||||
c1r0, c1r1, c1r2, c1r3,
|
||||
c2r0, c2r1, c2r2, c2r3,
|
||||
c3r0, c3r1, c3r2, c3r3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use std::mem;
|
|||
use std::ops::*;
|
||||
|
||||
use rand::{Rand, Rng};
|
||||
use num_traits::{NumCast, cast};
|
||||
use num_traits::{cast, NumCast};
|
||||
|
||||
use structure::*;
|
||||
|
||||
|
@ -28,7 +28,7 @@ use euler::Euler;
|
|||
use matrix::{Matrix3, Matrix4};
|
||||
use num::BaseFloat;
|
||||
use point::Point3;
|
||||
use rotation::{Rotation, Rotation3, Basis3};
|
||||
use rotation::{Basis3, Rotation, Rotation3};
|
||||
use vector::Vector3;
|
||||
|
||||
#[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)
|
||||
/// - [Ogre implementation for normalized vectors]
|
||||
/// (https://bitbucket.org/sinbad/ogre/src/9db75e3ba05c/OgreMain/include/OgreVector3.h?fileviewer=file-view-default#cl-651)
|
||||
pub fn from_arc(src: Vector3<S>, dst: Vector3<S>, fallback: Option<Vector3<S>>)
|
||||
-> Quaternion<S> {
|
||||
pub fn from_arc(
|
||||
src: Vector3<S>,
|
||||
dst: Vector3<S>,
|
||||
fallback: Option<Vector3<S>>,
|
||||
) -> Quaternion<S> {
|
||||
let mag_avg = (src.magnitude2() * dst.magnitude2()).sqrt();
|
||||
let dot = src.dot(dst);
|
||||
if ulps_eq!(dot, &mag_avg) {
|
||||
|
@ -193,28 +196,28 @@ impl<S: BaseFloat> One for Quaternion<S> {
|
|||
|
||||
impl<S: BaseFloat> iter::Sum<Quaternion<S>> for Quaternion<S> {
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + BaseFloat> iter::Sum<&'a Quaternion<S>> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn sum<I: Iterator<Item=&'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
fn sum<I: Iterator<Item = &'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
iter.fold(Quaternion::<S>::zero(), Add::add)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> iter::Product<Quaternion<S>> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item=Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
fn product<I: Iterator<Item = Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
iter.fold(Quaternion::<S>::one(), Mul::mul)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Quaternion<S>> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item=&'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
fn product<I: Iterator<Item = &'a Quaternion<S>>>(iter: I) -> Quaternion<S> {
|
||||
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>> {
|
||||
let s = match NumCast::from(self.s) {
|
||||
Some(s) => s,
|
||||
None => return None
|
||||
None => return None,
|
||||
};
|
||||
let v = match self.v.cast() {
|
||||
Some(v) => v,
|
||||
None => return None
|
||||
None => return None,
|
||||
};
|
||||
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>>,
|
||||
{
|
||||
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_z, c_z) = Rad::sin_cos(src.z.into() * half);
|
||||
|
||||
Quaternion::new(-s_x * s_y * s_z + c_x * c_y * c_z,
|
||||
s_x * c_y * c_z + s_y * s_z * c_x,
|
||||
-s_x * s_z * c_y + s_y * c_x * c_z,
|
||||
s_x * s_y * c_z + s_z * c_x * c_y)
|
||||
Quaternion::new(
|
||||
-s_x * s_y * s_z + c_x * c_y * c_z,
|
||||
s_x * c_y * c_z + s_y * s_z * c_x,
|
||||
-s_x * s_z * c_y + s_y * c_x * c_z,
|
||||
s_x * s_y * c_z + s_z * c_x * c_y,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -510,20 +516,24 @@ impl SubAssign for Quaternion<f32> {
|
|||
#[cfg(not(feature = "simd"))]
|
||||
impl_operator!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
|
||||
fn mul(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::new(lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z,
|
||||
lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y,
|
||||
lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z,
|
||||
lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x)
|
||||
Quaternion::new(
|
||||
lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z,
|
||||
lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y,
|
||||
lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z,
|
||||
lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x,
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_default!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
|
||||
fn mul(lhs, rhs) -> Quaternion<S> {
|
||||
Quaternion::new(lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z,
|
||||
lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y,
|
||||
lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z,
|
||||
lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x)
|
||||
Quaternion::new(
|
||||
lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z,
|
||||
lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y,
|
||||
lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z,
|
||||
lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x,
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -593,14 +603,14 @@ impl<S: BaseFloat> ApproxEq for Quaternion<S> {
|
|||
|
||||
#[inline]
|
||||
fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool {
|
||||
S::relative_eq(&self.s, &other.s, epsilon, max_relative) &&
|
||||
Vector3::relative_eq(&self.v, &other.v, epsilon, max_relative)
|
||||
S::relative_eq(&self.s, &other.s, epsilon, max_relative)
|
||||
&& Vector3::relative_eq(&self.v, &other.v, epsilon, max_relative)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool {
|
||||
S::ulps_eq(&self.s, &other.s, epsilon, max_ulps) &&
|
||||
Vector3::ulps_eq(&self.v, &other.v, epsilon, max_ulps)
|
||||
S::ulps_eq(&self.s, &other.s, 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 sx2 = x2 * quat.s;
|
||||
|
||||
Matrix3::new(S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2,
|
||||
xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2,
|
||||
xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2)
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
Matrix3::new(
|
||||
S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2,
|
||||
xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2,
|
||||
xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -648,10 +661,13 @@ impl<S: BaseFloat> From<Quaternion<S>> for Matrix4<S> {
|
|||
let sz2 = z2 * quat.s;
|
||||
let sx2 = x2 * quat.s;
|
||||
|
||||
Matrix4::new(S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2, S::zero(),
|
||||
xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2, S::zero(),
|
||||
xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2, S::zero(),
|
||||
S::zero(), S::zero(), S::zero(), S::one())
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
Matrix4::new(
|
||||
S::one() - yy2 - zz2, xy2 + sz2, xz2 - sy2, S::zero(),
|
||||
xy2 - sz2, S::one() - xx2 - zz2, yz2 + sx2, S::zero(),
|
||||
xz2 + sy2, yz2 - sx2, S::one() - xx2 - yy2, S::zero(),
|
||||
S::zero(), S::zero(), S::zero(), S::one(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -659,7 +675,9 @@ impl<S: BaseFloat> From<Quaternion<S>> for Matrix4<S> {
|
|||
|
||||
impl<S: BaseFloat> From<Quaternion<S>> for Basis3<S> {
|
||||
#[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> {
|
||||
|
@ -695,10 +713,14 @@ impl<S: BaseFloat> Rotation<Point3<S>> for Quaternion<S> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn rotate_vector(&self, vec: Vector3<S>) -> Vector3<S> { self * vec }
|
||||
fn rotate_vector(&self, vec: Vector3<S>) -> Vector3<S> {
|
||||
self * vec
|
||||
}
|
||||
|
||||
#[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> {
|
||||
|
@ -712,7 +734,9 @@ impl<S: BaseFloat> Rotation3<S> for Quaternion<S> {
|
|||
impl<S: BaseFloat> Into<[S; 4]> for Quaternion<S> {
|
||||
#[inline]
|
||||
fn into(self) -> [S; 4] {
|
||||
match self.into() { (w, xi, yj, zk) => [w, xi, yj, zk] }
|
||||
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> {
|
||||
#[inline]
|
||||
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> {
|
||||
#[inline]
|
||||
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> {
|
||||
#[inline]
|
||||
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)]
|
||||
mod tests {
|
||||
use quaternion::*;
|
||||
|
@ -854,7 +884,11 @@ mod tests {
|
|||
|
||||
const QUATERNION: Quaternion<f32> = Quaternion {
|
||||
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]
|
||||
|
@ -887,11 +921,11 @@ mod tests {
|
|||
fn test_as_mut() {
|
||||
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]);
|
||||
}
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
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
|
||||
/// 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
|
||||
Self: ApproxEq<Epsilon = P::Scalar>,
|
||||
P::Scalar: BaseFloat,
|
||||
|
@ -59,20 +60,17 @@ pub trait Rotation<P: EuclideanSpace>: Sized + Copy + One where
|
|||
}
|
||||
|
||||
/// A two-dimensional rotation.
|
||||
pub trait Rotation2<S: BaseFloat>: Rotation<Point2<S>>
|
||||
+ Into<Matrix2<S>>
|
||||
+ Into<Basis2<S>> {
|
||||
pub trait Rotation2<S: BaseFloat>
|
||||
: Rotation<Point2<S>> + Into<Matrix2<S>> + Into<Basis2<S>> {
|
||||
/// Create a rotation by a given angle. Thus is a redundant case of both
|
||||
/// from_axis_angle() and from_euler() for 2D space.
|
||||
fn from_angle<A: Into<Rad<S>>>(theta: A) -> Self;
|
||||
}
|
||||
|
||||
/// A three-dimensional rotation.
|
||||
pub trait Rotation3<S: BaseFloat>: Rotation<Point3<S>>
|
||||
+ Into<Matrix3<S>>
|
||||
+ Into<Basis3<S>>
|
||||
+ Into<Quaternion<S>>
|
||||
+ From<Euler<Rad<S>>> {
|
||||
pub trait Rotation3<S: BaseFloat>
|
||||
: Rotation<Point3<S>> + Into<Matrix3<S>> + Into<Basis3<S>> + Into<Quaternion<S>> + From<Euler<Rad<S>>>
|
||||
{
|
||||
/// Create a rotation using an angle around a given axis.
|
||||
///
|
||||
/// The specified axis **must be normalized**, or it represents an invalid rotation.
|
||||
|
@ -97,7 +95,6 @@ pub trait Rotation3<S: BaseFloat>: Rotation<Point3<S>>
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// A two-dimensional rotation matrix.
|
||||
///
|
||||
/// 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)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Basis2<S> {
|
||||
mat: Matrix2<S>
|
||||
mat: Matrix2<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> {
|
||||
#[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> {
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis2<S>> for Basis2<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item=&'a Basis2<S>>>(iter: I) -> Basis2<S> {
|
||||
fn product<I: Iterator<Item = &'a Basis2<S>>>(iter: I) -> Basis2<S> {
|
||||
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> {
|
||||
#[inline]
|
||||
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]
|
||||
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]
|
||||
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
|
||||
// to be faster
|
||||
#[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> {
|
||||
#[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> {
|
||||
|
@ -232,7 +243,11 @@ impl<S: BaseFloat> ApproxEq 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> {
|
||||
|
@ -251,14 +266,16 @@ impl<S: fmt::Debug> fmt::Debug for Basis2<S> {
|
|||
#[derive(PartialEq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Basis3<S> {
|
||||
mat: Matrix3<S>
|
||||
mat: Matrix3<S>,
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> Basis3<S> {
|
||||
/// Create a new rotation matrix from a quaternion.
|
||||
#[inline]
|
||||
pub fn from_quaternion(quaternion: &Quaternion<S>) -> Basis3<S> {
|
||||
Basis3 { mat: quaternion.clone().into() }
|
||||
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> {
|
||||
#[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> {
|
||||
#[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> {
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3<S>> for Basis3<S> {
|
||||
#[inline]
|
||||
fn product<I: Iterator<Item=&'a Basis3<S>>>(iter: I) -> Basis3<S> {
|
||||
fn product<I: Iterator<Item = &'a Basis3<S>>>(iter: I) -> Basis3<S> {
|
||||
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> {
|
||||
#[inline]
|
||||
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]
|
||||
|
@ -306,17 +329,27 @@ impl<S: BaseFloat> Rotation<Point3<S>> for Basis3<S> {
|
|||
}
|
||||
|
||||
#[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
|
||||
// to be faster
|
||||
#[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> {
|
||||
#[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> {
|
||||
|
@ -354,23 +387,32 @@ impl<S: BaseFloat> ApproxEq 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> {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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>>,
|
||||
{
|
||||
/// Create a three-dimensional rotation matrix from a set of euler angles.
|
||||
|
|
|
@ -23,12 +23,13 @@ use std::ops::*;
|
|||
use approx::ApproxEq;
|
||||
|
||||
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`
|
||||
pub trait Array where
|
||||
pub trait Array
|
||||
where
|
||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||
Self: Index<usize, Output = <Self as Array>::Element>,
|
||||
Self: IndexMut<usize, Output = <Self as Array>::Element>,
|
||||
|
@ -78,10 +79,14 @@ pub trait Array where
|
|||
}
|
||||
|
||||
/// 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.
|
||||
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
|
||||
|
@ -156,7 +161,8 @@ pub trait ElementWise<Rhs = Self> {
|
|||
/// let upscaled_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: Add<Self, Output = Self>,
|
||||
|
@ -199,7 +205,8 @@ pub trait MetricSpace: Sized {
|
|||
/// finding the magnitude of a vector or normalizing it.
|
||||
///
|
||||
/// 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
|
||||
<Self as VectorSpace>::Scalar: BaseFloat,
|
||||
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`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn normalize(self) -> Self {
|
||||
self.normalize_to(Self::Scalar::one())
|
||||
}
|
||||
|
||||
/// Returns a vector with the same direction and a given magnitude.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn normalize_to(self, magnitude: Self::Scalar) -> Self {
|
||||
self * (magnitude / self.magnitude())
|
||||
}
|
||||
|
@ -252,7 +257,6 @@ pub trait InnerSpace: VectorSpace where
|
|||
/// Returns the result of linearly interpolating the magnitude of the vector
|
||||
/// towards the magnitude of `other` by the specified amount.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn lerp(self, other: Self, amount: Self::Scalar) -> Self {
|
||||
self + ((other - self) * amount)
|
||||
}
|
||||
|
@ -261,7 +265,6 @@ pub trait InnerSpace: VectorSpace where
|
|||
/// [vector projection](https://en.wikipedia.org/wiki/Vector_projection)
|
||||
/// of the current inner space projected onto the supplied argument.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn project_on(self, other: Self) -> Self {
|
||||
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)
|
||||
/// - [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
|
||||
Self: Array<Element = <Self as EuclideanSpace>::Scalar>,
|
||||
|
||||
|
@ -382,10 +386,9 @@ pub trait EuclideanSpace: Copy + Clone where
|
|||
/// ```
|
||||
#[inline]
|
||||
fn centroid(points: &[Self]) -> Self {
|
||||
let total_displacement =
|
||||
points.iter().fold(Self::Diff::zero(), |acc, p| {
|
||||
acc + p.to_vec()
|
||||
});
|
||||
let total_displacement = points
|
||||
.iter()
|
||||
.fold(Self::Diff::zero(), |acc, p| acc + p.to_vec());
|
||||
|
||||
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
|
||||
/// Rust's current type system. For the multiplication of square matrices,
|
||||
/// see `SquareMatrix`.
|
||||
pub trait Matrix: VectorSpace where
|
||||
pub trait Matrix: VectorSpace
|
||||
where
|
||||
Self::Scalar: BaseFloat,
|
||||
|
||||
// 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.
|
||||
pub trait SquareMatrix where
|
||||
pub trait SquareMatrix
|
||||
where
|
||||
Self::Scalar: BaseFloat,
|
||||
|
||||
Self: One,
|
||||
|
@ -518,22 +523,27 @@ pub trait SquareMatrix where
|
|||
|
||||
/// Return the trace of this matrix. That is, the sum of the diagonal.
|
||||
#[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
|
||||
/// the identity matrix. Returns `None` if this matrix is not invertible
|
||||
/// (has a determinant of zero).
|
||||
#[must_use]
|
||||
fn invert(&self) -> Option<Self>;
|
||||
|
||||
/// Test if this matrix is invertible.
|
||||
#[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
|
||||
/// and every element in the diagonal is one.
|
||||
#[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
|
||||
/// the diagonal is 0.
|
||||
|
@ -550,7 +560,8 @@ pub trait SquareMatrix where
|
|||
/// clear when semantic violations have occured - for example, adding degrees to
|
||||
/// radians, or adding a number to an angle.
|
||||
///
|
||||
pub trait Angle where
|
||||
pub trait Angle
|
||||
where
|
||||
Self: Copy + Clone,
|
||||
Self: PartialEq + cmp::PartialOrd,
|
||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||
|
@ -574,7 +585,11 @@ pub trait Angle where
|
|||
#[inline]
|
||||
fn normalize(self) -> Self {
|
||||
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.
|
||||
|
|
|
@ -39,7 +39,8 @@ pub trait Transform<P: EuclideanSpace>: Sized {
|
|||
|
||||
/// Inverse transform a vector using this transform
|
||||
fn inverse_transform_vector(&self, vec: P::Diff) -> Option<P::Diff> {
|
||||
self.inverse_transform().and_then(|inverse| Some(inverse.transform_vector(vec)))
|
||||
self.inverse_transform()
|
||||
.and_then(|inverse| Some(inverse.transform_vector(vec)))
|
||||
}
|
||||
|
||||
/// 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>
|
||||
where P::Scalar: BaseFloat,
|
||||
// FIXME: Investigate why this is needed!
|
||||
P::Diff: VectorSpace
|
||||
where
|
||||
P::Scalar: BaseFloat,
|
||||
// FIXME: Investigate why this is needed!
|
||||
P::Diff: VectorSpace,
|
||||
{
|
||||
#[inline]
|
||||
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 d = r.rotate_vector(self.disp.clone()) * -s;
|
||||
Some(Decomposed {
|
||||
scale: s,
|
||||
rot: r,
|
||||
disp: d,
|
||||
})
|
||||
scale: s,
|
||||
rot: r,
|
||||
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: VectorSpace, R, E: BaseFloat> ApproxEq for Decomposed<S, R>
|
||||
where S: ApproxEq<Epsilon = E>,
|
||||
S::Scalar: ApproxEq<Epsilon = E>,
|
||||
R: ApproxEq<Epsilon = E>
|
||||
where
|
||||
S: ApproxEq<Epsilon = E>,
|
||||
S::Scalar: ApproxEq<Epsilon = E>,
|
||||
R: ApproxEq<Epsilon = E>,
|
||||
{
|
||||
type Epsilon = E;
|
||||
|
||||
|
@ -185,16 +188,16 @@ impl<S: VectorSpace, R, E: BaseFloat> ApproxEq for Decomposed<S, R>
|
|||
|
||||
#[inline]
|
||||
fn relative_eq(&self, other: &Self, epsilon: E, max_relative: E) -> bool {
|
||||
S::Scalar::relative_eq(&self.scale, &other.scale, epsilon, max_relative) &&
|
||||
R::relative_eq(&self.rot, &other.rot, epsilon, max_relative) &&
|
||||
S::relative_eq(&self.disp, &other.disp, epsilon, max_relative)
|
||||
S::Scalar::relative_eq(&self.scale, &other.scale, epsilon, max_relative)
|
||||
&& R::relative_eq(&self.rot, &other.rot, epsilon, max_relative)
|
||||
&& S::relative_eq(&self.disp, &other.disp, epsilon, max_relative)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ulps_eq(&self, other: &Self, epsilon: E, max_ulps: u32) -> bool {
|
||||
S::Scalar::ulps_eq(&self.scale, &other.scale, epsilon, max_ulps) &&
|
||||
R::ulps_eq(&self.rot, &other.rot, epsilon, max_ulps) &&
|
||||
S::ulps_eq(&self.disp, &other.disp, epsilon, max_ulps)
|
||||
S::Scalar::ulps_eq(&self.scale, &other.scale, epsilon, max_ulps)
|
||||
&& R::ulps_eq(&self.rot, &other.rot, epsilon, max_ulps)
|
||||
&& S::ulps_eq(&self.disp, &other.disp, epsilon, max_ulps)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,12 +210,14 @@ mod serde_ser {
|
|||
use serde::ser::SerializeStruct;
|
||||
|
||||
impl<V, R> Serialize for Decomposed<V, R>
|
||||
where V: Serialize + VectorSpace,
|
||||
V::Scalar: Serialize,
|
||||
R: Serialize
|
||||
where
|
||||
V: Serialize + VectorSpace,
|
||||
V::Scalar: Serialize,
|
||||
R: Serialize,
|
||||
{
|
||||
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)?;
|
||||
struc.serialize_field("scale", &self.scale)?;
|
||||
|
@ -240,7 +245,8 @@ mod serde_de {
|
|||
|
||||
impl<'a> Deserialize<'a> for DecomposedField {
|
||||
fn deserialize<D>(deserializer: D) -> Result<DecomposedField, D::Error>
|
||||
where D: serde::Deserializer<'a>
|
||||
where
|
||||
D: serde::Deserializer<'a>,
|
||||
{
|
||||
struct DecomposedFieldVisitor;
|
||||
|
||||
|
@ -252,7 +258,8 @@ mod serde_de {
|
|||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<DecomposedField, E>
|
||||
where E: serde::de::Error
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match value {
|
||||
"scale" => Ok(DecomposedField::Scale),
|
||||
|
@ -268,12 +275,14 @@ mod serde_de {
|
|||
}
|
||||
|
||||
impl<'a, S: VectorSpace, R> Deserialize<'a> for Decomposed<S, R>
|
||||
where S: Deserialize<'a>,
|
||||
S::Scalar: Deserialize<'a>,
|
||||
R: Deserialize<'a>
|
||||
where
|
||||
S: Deserialize<'a>,
|
||||
S::Scalar: Deserialize<'a>,
|
||||
R: Deserialize<'a>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Decomposed<S, R>, D::Error>
|
||||
where D: serde::de::Deserializer<'a>
|
||||
where
|
||||
D: serde::de::Deserializer<'a>,
|
||||
{
|
||||
const FIELDS: &'static [&'static str] = &["scale", "rot", "disp"];
|
||||
deserializer.deserialize_struct("Decomposed", FIELDS, DecomposedVisitor(PhantomData))
|
||||
|
@ -283,9 +292,10 @@ mod serde_de {
|
|||
struct DecomposedVisitor<S: VectorSpace, R>(PhantomData<(S, R)>);
|
||||
|
||||
impl<'a, S: VectorSpace, R> serde::de::Visitor<'a> for DecomposedVisitor<S, R>
|
||||
where S: Deserialize<'a>,
|
||||
S::Scalar: Deserialize<'a>,
|
||||
R: Deserialize<'a>
|
||||
where
|
||||
S: Deserialize<'a>,
|
||||
S::Scalar: Deserialize<'a>,
|
||||
R: Deserialize<'a>,
|
||||
{
|
||||
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>
|
||||
where V: serde::de::MapAccess<'a>
|
||||
where
|
||||
V: serde::de::MapAccess<'a>,
|
||||
{
|
||||
let mut scale = None;
|
||||
let mut rot = None;
|
||||
|
@ -330,10 +341,10 @@ mod serde_de {
|
|||
};
|
||||
|
||||
Ok(Decomposed {
|
||||
scale: scale,
|
||||
rot: rot,
|
||||
disp: disp,
|
||||
})
|
||||
scale: scale,
|
||||
rot: rot,
|
||||
disp: disp,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
use rand::{Rand, Rng};
|
||||
use num_traits::{NumCast, Bounded};
|
||||
use num_traits::{Bounded, NumCast};
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
|
@ -24,7 +24,7 @@ use structure::*;
|
|||
|
||||
use angle::Rad;
|
||||
use approx::ApproxEq;
|
||||
use num::{BaseNum, BaseFloat};
|
||||
use num::{BaseFloat, BaseNum};
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
use simd::f32x4 as Simdf32x4;
|
||||
|
@ -659,11 +659,12 @@ impl<S: BaseNum> Vector3<S> {
|
|||
|
||||
/// Returns the cross product of the vector and `other`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cross(self, other: Vector3<S>) -> Vector3<S> {
|
||||
Vector3::new((self.y * other.z) - (self.z * other.y),
|
||||
(self.z * other.x) - (self.x * other.z),
|
||||
(self.x * other.y) - (self.y * other.x))
|
||||
Vector3::new(
|
||||
(self.y * other.z) - (self.z * other.y),
|
||||
(self.z * other.x) - (self.x * other.z),
|
||||
(self.x * other.y) - (self.y * other.x),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a `Vector4`, using the `x`, `y` and `z` values from this vector, and the
|
||||
|
@ -731,7 +732,8 @@ impl<S: BaseNum> Vector4<S> {
|
|||
/// Dot product of two vectors.
|
||||
#[inline]
|
||||
pub fn dot<V: InnerSpace>(a: V, b: V) -> V::Scalar
|
||||
where V::Scalar: BaseFloat
|
||||
where
|
||||
V::Scalar: BaseFloat,
|
||||
{
|
||||
V::dot(a, b)
|
||||
}
|
||||
|
@ -841,8 +843,6 @@ impl Vector4<f32> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl Into<Simdf32x4> for Vector4<f32> {
|
||||
#[inline]
|
||||
|
@ -888,8 +888,6 @@ impl_operator_simd!{@rs
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(feature = "simd")]
|
||||
impl_operator_simd!{
|
||||
[Simdf32x4]; Neg for Vector4<f32> {
|
||||
|
@ -939,27 +937,46 @@ impl DivAssign<f32> for Vector4<f32> {
|
|||
|
||||
#[cfg(feature = "simd")]
|
||||
impl ElementWise for Vector4<f32> {
|
||||
#[inline] fn add_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> {
|
||||
#[inline]
|
||||
fn add_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 rhs: Simdf32x4 = 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 rhs: Simdf32x4 = rhs.into();
|
||||
(s / rhs).into()
|
||||
}
|
||||
|
||||
#[inline] fn add_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>) {
|
||||
#[inline]
|
||||
fn add_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 rhs: Simdf32x4 = 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 rhs: Simdf32x4 = rhs.into();
|
||||
*self = (s * rhs).into();
|
||||
|
@ -968,31 +985,53 @@ impl ElementWise for Vector4<f32> {
|
|||
|
||||
#[cfg(feature = "simd")]
|
||||
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 rhs = Simdf32x4::splat(rhs);
|
||||
(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 rhs = Simdf32x4::splat(rhs);
|
||||
(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 rhs = Simdf32x4::splat(rhs);
|
||||
*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 rhs = Simdf32x4::splat(rhs);
|
||||
*self = (s - rhs).into();
|
||||
}
|
||||
#[inline] fn mul_assign_element_wise(&mut self, rhs: f32) { (*self) *= rhs; }
|
||||
#[inline] fn div_assign_element_wise(&mut self, rhs: f32) { (*self) /= rhs; }
|
||||
|
||||
#[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")]
|
||||
|
@ -1171,7 +1210,6 @@ impl_mint_conversions!(Vector3 { x, y, z }, Vector3);
|
|||
#[cfg(feature = "mint")]
|
||||
impl_mint_conversions!(Vector4 { x, y, z, w }, Vector4);
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod vector2 {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
extern crate approx;
|
||||
extern crate cgmath;
|
||||
|
||||
use cgmath::{Rad, Deg};
|
||||
use cgmath::{Deg, Rad};
|
||||
|
||||
#[test]
|
||||
fn test_conv() {
|
||||
|
@ -43,8 +43,14 @@ mod rad {
|
|||
|
||||
#[test]
|
||||
fn test_iter_sum() {
|
||||
assert_eq!(Rad(2.0) + Rad(3.0) + Rad(4.0), [Rad(2.0), Rad(3.0), Rad(4.0)].iter().sum());
|
||||
assert_eq!(Rad(2.0) + Rad(3.0) + Rad(4.0), [Rad(2.0), Rad(3.0), Rad(4.0)].iter().cloned().sum());
|
||||
assert_eq!(
|
||||
Rad(2.0) + Rad(3.0) + Rad(4.0),
|
||||
[Rad(2.0), Rad(3.0), Rad(4.0)].iter().sum()
|
||||
);
|
||||
assert_eq!(
|
||||
Rad(2.0) + Rad(3.0) + Rad(4.0),
|
||||
[Rad(2.0), Rad(3.0), Rad(4.0)].iter().cloned().sum()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +59,13 @@ mod deg {
|
|||
|
||||
#[test]
|
||||
fn test_iter_sum() {
|
||||
assert_eq!(Deg(2.0) + Deg(3.0) + Deg(4.0), [Deg(2.0), Deg(3.0), Deg(4.0)].iter().sum());
|
||||
assert_eq!(Deg(2.0) + Deg(3.0) + Deg(4.0), [Deg(2.0), Deg(3.0), Deg(4.0)].iter().cloned().sum());
|
||||
assert_eq!(
|
||||
Deg(2.0) + Deg(3.0) + Deg(4.0),
|
||||
[Deg(2.0), Deg(3.0), Deg(4.0)].iter().sum()
|
||||
);
|
||||
assert_eq!(
|
||||
Deg(2.0) + Deg(3.0) + Deg(4.0),
|
||||
[Deg(2.0), Deg(3.0), Deg(4.0)].iter().cloned().sum()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ macro_rules! impl_test_rem {
|
|||
|
||||
#[test]
|
||||
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()));
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,12 @@ fn test_rem() {
|
|||
#[test]
|
||||
fn test_cast() {
|
||||
assert_ulps_eq!(Point1::new(0.9f64).cast().unwrap(), Point1::new(0.9f32));
|
||||
assert_ulps_eq!(Point2::new(0.9f64, 1.5).cast().unwrap(), Point2::new(0.9f32, 1.5));
|
||||
assert_ulps_eq!(Point3::new(1.0f64, 2.4, -3.13).cast().unwrap(), Point3::new(1.0f32, 2.4, -3.13));
|
||||
assert_ulps_eq!(
|
||||
Point2::new(0.9f64, 1.5).cast().unwrap(),
|
||||
Point2::new(0.9f32, 1.5)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Point3::new(1.0f64, 2.4, -3.13).cast().unwrap(),
|
||||
Point3::new(1.0f32, 2.4, -3.13)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
extern crate cgmath;
|
||||
|
||||
use cgmath::{Vector4, ortho, Matrix4};
|
||||
use cgmath::{ortho, Matrix4, Vector4};
|
||||
|
||||
#[test]
|
||||
fn test_ortho_scale() {
|
||||
|
@ -36,7 +36,6 @@ fn test_ortho_scale() {
|
|||
assert_eq!(orig, Vector4::new(0f32, 0., 0., 1.));
|
||||
assert_eq!(far, Vector4::new(1f32, 1., -1., 1.));
|
||||
|
||||
|
||||
let o: Matrix4<f32> = ortho(-2., 2., -2., 2., -2., 2.);
|
||||
let near = o * vec_near;
|
||||
let orig = o * vec_orig;
|
||||
|
|
|
@ -44,19 +44,45 @@ mod operators {
|
|||
|
||||
#[test]
|
||||
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]
|
||||
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]
|
||||
fn test_iter_sum() {
|
||||
let q1 = Quaternion::from(Euler { x: Rad(2f32), y: Rad(1f32), z: Rad(1f32) });
|
||||
let q2 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(2f32), z: Rad(1f32) });
|
||||
let q3 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(2f32) });
|
||||
let q1 = Quaternion::from(Euler {
|
||||
x: Rad(2f32),
|
||||
y: Rad(1f32),
|
||||
z: Rad(1f32),
|
||||
});
|
||||
let q2 = Quaternion::from(Euler {
|
||||
x: Rad(1f32),
|
||||
y: Rad(2f32),
|
||||
z: Rad(1f32),
|
||||
});
|
||||
let q3 = Quaternion::from(Euler {
|
||||
x: Rad(1f32),
|
||||
y: Rad(1f32),
|
||||
z: Rad(2f32),
|
||||
});
|
||||
|
||||
assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().sum());
|
||||
assert_eq!(q1 + q2 + q3, [q1, q2, q3].iter().cloned().sum());
|
||||
|
@ -64,9 +90,21 @@ mod operators {
|
|||
|
||||
#[test]
|
||||
fn test_iter_product() {
|
||||
let q1 = Quaternion::from(Euler { x: Rad(2f32), y: Rad(1f32), z: Rad(1f32) });
|
||||
let q2 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(2f32), z: Rad(1f32) });
|
||||
let q3 = Quaternion::from(Euler { x: Rad(1f32), y: Rad(1f32), z: Rad(2f32) });
|
||||
let q1 = Quaternion::from(Euler {
|
||||
x: Rad(2f32),
|
||||
y: Rad(1f32),
|
||||
z: Rad(1f32),
|
||||
});
|
||||
let q2 = Quaternion::from(Euler {
|
||||
x: Rad(1f32),
|
||||
y: Rad(2f32),
|
||||
z: Rad(1f32),
|
||||
});
|
||||
let q3 = Quaternion::from(Euler {
|
||||
x: Rad(1f32),
|
||||
y: Rad(1f32),
|
||||
z: Rad(2f32),
|
||||
});
|
||||
|
||||
assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().product());
|
||||
assert_eq!(q1 * q2 * q3, [q1, q2, q3].iter().cloned().product());
|
||||
|
@ -79,22 +117,103 @@ mod to_from_euler {
|
|||
use cgmath::*;
|
||||
|
||||
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;
|
||||
|
||||
#[test] fn test_zero() { check_euler(Euler { x: Rad( 0f32), y: Rad( 0f32), z: Rad( 0f32) }); }
|
||||
#[test] fn test_yaw_pos_1() { check_euler(Euler { x: Rad( 0f32), y: Rad( 1f32), z: Rad( 0f32) }); }
|
||||
#[test] fn test_yaw_neg_1() { check_euler(Euler { x: Rad( 0f32), y: Rad(-1f32), z: Rad( 0f32) }); }
|
||||
#[test] fn test_pitch_pos_1() { check_euler(Euler { x: Rad( 1f32), y: Rad( 0f32), z: Rad( 0f32) }); }
|
||||
#[test] fn test_pitch_neg_1() { check_euler(Euler { x: Rad(-1f32), y: Rad( 0f32), z: Rad( 0f32) }); }
|
||||
#[test] fn test_roll_pos_1() { check_euler(Euler { x: Rad( 0f32), y: Rad( 0f32), z: Rad( 1f32) }); }
|
||||
#[test] fn test_roll_neg_1() { check_euler(Euler { x: Rad( 0f32), y: Rad( 0f32), z: Rad(-1f32) }); }
|
||||
#[test] fn test_pitch_yaw_roll_pos_1() { check_euler(Euler { x: Rad( 1f32), y: Rad( 1f32), z: Rad( 1f32) }); }
|
||||
#[test] fn test_pitch_yaw_roll_neg_1() { check_euler(Euler { x: Rad(-1f32), y: Rad(-1f32), z: Rad(-1f32) }); }
|
||||
#[test] fn test_pitch_yaw_roll_pos_hp() { check_euler(Euler { x: Rad( 0f32), y: Rad( HPI), z: Rad( 1f32) }); }
|
||||
#[test] fn test_pitch_yaw_roll_neg_hp() { check_euler(Euler { x: Rad( 0f32), y: Rad( -HPI), z: Rad( 1f32) }); }
|
||||
#[test]
|
||||
fn test_zero() {
|
||||
check_euler(Euler {
|
||||
x: Rad(0f32),
|
||||
y: Rad(0f32),
|
||||
z: Rad(0f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_yaw_pos_1() {
|
||||
check_euler(Euler {
|
||||
x: Rad(0f32),
|
||||
y: Rad(1f32),
|
||||
z: Rad(0f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_yaw_neg_1() {
|
||||
check_euler(Euler {
|
||||
x: Rad(0f32),
|
||||
y: Rad(-1f32),
|
||||
z: Rad(0f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_pitch_pos_1() {
|
||||
check_euler(Euler {
|
||||
x: Rad(1f32),
|
||||
y: Rad(0f32),
|
||||
z: Rad(0f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_pitch_neg_1() {
|
||||
check_euler(Euler {
|
||||
x: Rad(-1f32),
|
||||
y: Rad(0f32),
|
||||
z: Rad(0f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_roll_pos_1() {
|
||||
check_euler(Euler {
|
||||
x: Rad(0f32),
|
||||
y: Rad(0f32),
|
||||
z: Rad(1f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_roll_neg_1() {
|
||||
check_euler(Euler {
|
||||
x: Rad(0f32),
|
||||
y: Rad(0f32),
|
||||
z: Rad(-1f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_pitch_yaw_roll_pos_1() {
|
||||
check_euler(Euler {
|
||||
x: Rad(1f32),
|
||||
y: Rad(1f32),
|
||||
z: Rad(1f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_pitch_yaw_roll_neg_1() {
|
||||
check_euler(Euler {
|
||||
x: Rad(-1f32),
|
||||
y: Rad(-1f32),
|
||||
z: Rad(-1f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_pitch_yaw_roll_pos_hp() {
|
||||
check_euler(Euler {
|
||||
x: Rad(0f32),
|
||||
y: Rad(HPI),
|
||||
z: Rad(1f32),
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_pitch_yaw_roll_neg_hp() {
|
||||
check_euler(Euler {
|
||||
x: Rad(0f32),
|
||||
y: Rad(-HPI),
|
||||
z: Rad(1f32),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mod from {
|
||||
|
@ -206,7 +325,6 @@ mod rotate_from_euler {
|
|||
assert_ulps_eq!(vec3(0.0, -1.0, 0.0), rot * vec);
|
||||
}
|
||||
|
||||
|
||||
// tests that the Y rotation is done after the X
|
||||
#[test]
|
||||
fn test_x_then_y() {
|
||||
|
@ -258,7 +376,10 @@ mod rotate_from_axis_angle {
|
|||
let vec = vec3(0.0, 0.0, 1.0);
|
||||
|
||||
let rot = Quaternion::from_axis_angle(vec3(1.0, 1.0, 0.0).normalize(), Deg(90.0));
|
||||
assert_ulps_eq!(vec3(2.0f32.sqrt() / 2.0, -2.0f32.sqrt() / 2.0, 0.0), rot * vec);
|
||||
assert_ulps_eq!(
|
||||
vec3(2.0f32.sqrt() / 2.0, -2.0f32.sqrt() / 2.0, 0.0),
|
||||
rot * vec
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -266,7 +387,10 @@ mod rotate_from_axis_angle {
|
|||
let vec = vec3(1.0, 0.0, 0.0);
|
||||
|
||||
let rot = Quaternion::from_axis_angle(vec3(0.0, 1.0, 1.0).normalize(), Deg(-90.0));
|
||||
assert_ulps_eq!(vec3(0.0, -2.0f32.sqrt() / 2.0, 2.0f32.sqrt() / 2.0), rot * vec);
|
||||
assert_ulps_eq!(
|
||||
vec3(0.0, -2.0f32.sqrt() / 2.0, 2.0f32.sqrt() / 2.0),
|
||||
rot * vec
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -274,7 +398,10 @@ mod rotate_from_axis_angle {
|
|||
let vec = vec3(0.0, 1.0, 0.0);
|
||||
|
||||
let rot = Quaternion::from_axis_angle(vec3(1.0, 0.0, 1.0).normalize(), Deg(90.0));
|
||||
assert_ulps_eq!(vec3(-2.0f32.sqrt() / 2.0, 0.0, 2.0f32.sqrt() / 2.0), rot * vec);
|
||||
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]
|
||||
fn test_cast() {
|
||||
assert_ulps_eq!(Quaternion::new(0.9f64, 1.5, 2.4, 7.6).cast().unwrap(),
|
||||
Quaternion::new(0.9f32, 1.5, 2.4, 7.6));
|
||||
assert_ulps_eq!(
|
||||
Quaternion::new(0.9f64, 1.5, 2.4, 7.6).cast().unwrap(),
|
||||
Quaternion::new(0.9f32, 1.5, 2.4, 7.6)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ fn test_invert() {
|
|||
rot: Quaternion::new(0.5f64, 0.5, 0.5, 0.5),
|
||||
disp: Vector3::new(6.0f64, -7.0, 8.0),
|
||||
};
|
||||
let ti = t.inverse_transform().expect("Expected successful inversion");
|
||||
let ti = t.inverse_transform()
|
||||
.expect("Expected successful inversion");
|
||||
let vt = t.transform_vector(v);
|
||||
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),
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -68,7 +70,8 @@ fn test_serialize() {
|
|||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
171
tests/vector.rs
171
tests/vector.rs
|
@ -25,14 +25,26 @@ use std::iter;
|
|||
fn test_constructor() {
|
||||
assert_eq!(vec2(1f32, 2f32), Vector2::new(1f32, 2f32));
|
||||
assert_eq!(vec3(1f64, 2f64, 3f64), Vector3::new(1f64, 2f64, 3f64));
|
||||
assert_eq!(vec4(1isize, 2isize, 3isize, 4isize), Vector4::new(1isize, 2isize, 3isize, 4isize));
|
||||
assert_eq!(
|
||||
vec4(1isize, 2isize, 3isize, 4isize),
|
||||
Vector4::new(1isize, 2isize, 3isize, 4isize)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value() {
|
||||
assert_eq!(Vector2::from_value(102isize), Vector2::new(102isize, 102isize));
|
||||
assert_eq!(Vector3::from_value(22isize), Vector3::new(22isize, 22isize, 22isize));
|
||||
assert_eq!(Vector4::from_value(76.5f64), Vector4::new(76.5f64, 76.5f64, 76.5f64, 76.5f64));
|
||||
assert_eq!(
|
||||
Vector2::from_value(102isize),
|
||||
Vector2::new(102isize, 102isize)
|
||||
);
|
||||
assert_eq!(
|
||||
Vector3::from_value(22isize),
|
||||
Vector3::new(22isize, 22isize, 22isize)
|
||||
);
|
||||
assert_eq!(
|
||||
Vector4::from_value(76.5f64),
|
||||
Vector4::new(76.5f64, 76.5f64, 76.5f64, 76.5f64)
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! impl_test_add {
|
||||
|
@ -132,8 +144,14 @@ fn test_rem() {
|
|||
#[test]
|
||||
fn test_dot() {
|
||||
assert_eq!(Vector2::new(1.0, 2.0).dot(Vector2::new(3.0, 4.0)), 11.0);
|
||||
assert_eq!(Vector3::new(1.0, 2.0, 3.0).dot(Vector3::new(4.0, 5.0, 6.0)), 32.0);
|
||||
assert_eq!(Vector4::new(1.0, 2.0, 3.0, 4.0).dot(Vector4::new(5.0, 6.0, 7.0, 8.0)), 70.0);
|
||||
assert_eq!(
|
||||
Vector3::new(1.0, 2.0, 3.0).dot(Vector3::new(4.0, 5.0, 6.0)),
|
||||
32.0
|
||||
);
|
||||
assert_eq!(
|
||||
Vector4::new(1.0, 2.0, 3.0, 4.0).dot(Vector4::new(5.0, 6.0, 7.0, 8.0)),
|
||||
70.0
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -149,7 +167,12 @@ fn test_sum() {
|
|||
|
||||
#[test]
|
||||
fn test_iter_sum() {
|
||||
impl_test_iter_sum!(Vector4 { x, y, z, w }, f32, 2.0f32, vec4(2.0f32, 4.0, 6.0, 8.0));
|
||||
impl_test_iter_sum!(
|
||||
Vector4 { x, y, z, w },
|
||||
f32,
|
||||
2.0f32,
|
||||
vec4(2.0f32, 4.0, 6.0, 8.0)
|
||||
);
|
||||
impl_test_iter_sum!(Vector3 { x, y, z }, f32, 2.0f32, vec3(2.0f32, 4.0, 6.0));
|
||||
impl_test_iter_sum!(Vector2 { x, y }, f32, 2.0f32, vec2(2.0f32, 4.0));
|
||||
|
||||
|
@ -162,11 +185,17 @@ fn test_iter_sum() {
|
|||
fn test_product() {
|
||||
assert_eq!(Vector2::new(1isize, 2isize).product(), 2isize);
|
||||
assert_eq!(Vector3::new(1isize, 2isize, 3isize).product(), 6isize);
|
||||
assert_eq!(Vector4::new(1isize, 2isize, 3isize, 4isize).product(), 24isize);
|
||||
assert_eq!(
|
||||
Vector4::new(1isize, 2isize, 3isize, 4isize).product(),
|
||||
24isize
|
||||
);
|
||||
|
||||
assert_eq!(Vector2::new(3.0f64, 4.0f64).product(), 12.0f64);
|
||||
assert_eq!(Vector3::new(4.0f64, 5.0f64, 6.0f64).product(), 120.0f64);
|
||||
assert_eq!(Vector4::new(5.0f64, 6.0f64, 7.0f64, 8.0f64).product(), 1680.0f64);
|
||||
assert_eq!(
|
||||
Vector4::new(5.0f64, 6.0f64, 7.0f64, 8.0f64).product(),
|
||||
1680.0f64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -180,8 +209,17 @@ fn test_cross() {
|
|||
#[test]
|
||||
fn test_is_perpendicular() {
|
||||
assert!(Vector2::new(1.0f64, 0.0f64).is_perpendicular(Vector2::new(0.0f64, 1.0f64)));
|
||||
assert!(Vector3::new(0.0f64, 1.0f64, 0.0f64).is_perpendicular(Vector3::new(0.0f64, 0.0f64, 1.0f64)));
|
||||
assert!(Vector4::new(1.0f64, 0.0f64, 0.0f64, 0.0f64).is_perpendicular(Vector4::new(0.0f64, 0.0f64, 0.0f64, 1.0f64)));
|
||||
assert!(
|
||||
Vector3::new(0.0f64, 1.0f64, 0.0f64).is_perpendicular(Vector3::new(0.0f64, 0.0f64, 1.0f64))
|
||||
);
|
||||
assert!(
|
||||
Vector4::new(1.0f64, 0.0f64, 0.0f64, 0.0f64).is_perpendicular(Vector4::new(
|
||||
0.0f64,
|
||||
0.0f64,
|
||||
0.0f64,
|
||||
1.0f64
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -189,7 +227,7 @@ mod test_magnitude {
|
|||
use cgmath::*;
|
||||
|
||||
#[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 (b, b_res) = (Vector2::new(5.0f64, 12.0f64), 13.0f64); // (5, 12, 13) Pythagorean triple
|
||||
|
||||
|
@ -201,7 +239,7 @@ mod test_magnitude {
|
|||
}
|
||||
|
||||
#[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 (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]
|
||||
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 (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]
|
||||
fn test_angle() {
|
||||
assert_ulps_eq!(Vector2::new(1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
||||
assert_ulps_eq!(Vector2::new(10.0f64, 0.0f64).angle(Vector2::new(0.0f64, 5.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
||||
assert_ulps_eq!(Vector2::new(-1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)), &-Rad(f64::consts::FRAC_PI_2));
|
||||
assert_ulps_eq!(
|
||||
Vector2::new(1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)),
|
||||
&Rad(f64::consts::FRAC_PI_2)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector2::new(10.0f64, 0.0f64).angle(Vector2::new(0.0f64, 5.0f64)),
|
||||
&Rad(f64::consts::FRAC_PI_2)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector2::new(-1.0f64, 0.0f64).angle(Vector2::new(0.0f64, 1.0f64)),
|
||||
&-Rad(f64::consts::FRAC_PI_2)
|
||||
);
|
||||
|
||||
assert_ulps_eq!(Vector3::new(1.0f64, 0.0f64, 1.0f64).angle(Vector3::new(1.0f64, 1.0f64, 0.0f64)), &Rad(f64::consts::FRAC_PI_3));
|
||||
assert_ulps_eq!(Vector3::new(10.0f64, 0.0f64, 10.0f64).angle(Vector3::new(5.0f64, 5.0f64, 0.0f64)), &Rad(f64::consts::FRAC_PI_3));
|
||||
assert_ulps_eq!(Vector3::new(-1.0f64, 0.0f64, -1.0f64).angle(Vector3::new(1.0f64, -1.0f64, 0.0f64)), &Rad(2.0f64 * f64::consts::FRAC_PI_3));
|
||||
assert_ulps_eq!(
|
||||
Vector3::new(1.0f64, 0.0f64, 1.0f64).angle(Vector3::new(1.0f64, 1.0f64, 0.0f64)),
|
||||
&Rad(f64::consts::FRAC_PI_3)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector3::new(10.0f64, 0.0f64, 10.0f64).angle(Vector3::new(5.0f64, 5.0f64, 0.0f64)),
|
||||
&Rad(f64::consts::FRAC_PI_3)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector3::new(-1.0f64, 0.0f64, -1.0f64).angle(Vector3::new(1.0f64, -1.0f64, 0.0f64)),
|
||||
&Rad(2.0f64 * f64::consts::FRAC_PI_3)
|
||||
);
|
||||
|
||||
assert_ulps_eq!(Vector4::new(1.0f64, 0.0f64, 1.0f64, 0.0f64).angle(Vector4::new(0.0f64, 1.0f64, 0.0f64, 1.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
||||
assert_ulps_eq!(Vector4::new(10.0f64, 0.0f64, 10.0f64, 0.0f64).angle(Vector4::new(0.0f64, 5.0f64, 0.0f64, 5.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
||||
assert_ulps_eq!(Vector4::new(-1.0f64, 0.0f64, -1.0f64, 0.0f64).angle(Vector4::new(0.0f64, 1.0f64, 0.0f64, 1.0f64)), &Rad(f64::consts::FRAC_PI_2));
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(1.0f64, 0.0f64, 1.0f64, 0.0f64).angle(Vector4::new(
|
||||
0.0f64,
|
||||
1.0f64,
|
||||
0.0f64,
|
||||
1.0f64
|
||||
)),
|
||||
&Rad(f64::consts::FRAC_PI_2)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(10.0f64, 0.0f64, 10.0f64, 0.0f64).angle(Vector4::new(
|
||||
0.0f64,
|
||||
5.0f64,
|
||||
0.0f64,
|
||||
5.0f64
|
||||
)),
|
||||
&Rad(f64::consts::FRAC_PI_2)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(-1.0f64, 0.0f64, -1.0f64, 0.0f64).angle(Vector4::new(
|
||||
0.0f64,
|
||||
1.0f64,
|
||||
0.0f64,
|
||||
1.0f64
|
||||
)),
|
||||
&Rad(f64::consts::FRAC_PI_2)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize() {
|
||||
// TODO: test normalize_to, normalize_sel.0, and normalize_self_to
|
||||
assert_ulps_eq!(Vector2::new(3.0f64, 4.0f64).normalize(), &Vector2::new(3.0/5.0, 4.0/5.0));
|
||||
assert_ulps_eq!(Vector3::new(2.0f64, 3.0f64, 6.0f64).normalize(), &Vector3::new(2.0/7.0, 3.0/7.0, 6.0/7.0));
|
||||
assert_ulps_eq!(Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64).normalize(), &Vector4::new(1.0/11.0, 2.0/11.0, 4.0/11.0, 10.0/11.0));
|
||||
assert_ulps_eq!(
|
||||
Vector2::new(3.0f64, 4.0f64).normalize(),
|
||||
&Vector2::new(3.0 / 5.0, 4.0 / 5.0)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector3::new(2.0f64, 3.0f64, 6.0f64).normalize(),
|
||||
&Vector3::new(2.0 / 7.0, 3.0 / 7.0, 6.0 / 7.0)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(1.0f64, 2.0f64, 4.0f64, 10.0f64).normalize(),
|
||||
&Vector4::new(1.0 / 11.0, 2.0 / 11.0, 4.0 / 11.0, 10.0 / 11.0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_project_on() {
|
||||
assert_ulps_eq!(Vector2::new(-1.0f64, 5.0).project_on(Vector2::new(2.0, 4.0)), &Vector2::new(9.0/5.0, 18.0/5.0));
|
||||
assert_ulps_eq!(Vector3::new(5.0f64, 6.0, 7.0).project_on(Vector3::new(1.0, 1.0, 1.0)), &Vector3::new(6.0, 6.0, 6.0));
|
||||
assert_ulps_eq!(Vector4::new(0.0f64, -5.0, 5.0, 5.0).project_on(Vector4::new(0.0, 1.0, 0.0, 0.5)), &Vector4::new(0.0, -2.0, 0.0, -1.0));
|
||||
assert_ulps_eq!(
|
||||
Vector2::new(-1.0f64, 5.0).project_on(Vector2::new(2.0, 4.0)),
|
||||
&Vector2::new(9.0 / 5.0, 18.0 / 5.0)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector3::new(5.0f64, 6.0, 7.0).project_on(Vector3::new(1.0, 1.0, 1.0)),
|
||||
&Vector3::new(6.0, 6.0, 6.0)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(0.0f64, -5.0, 5.0, 5.0).project_on(Vector4::new(0.0, 1.0, 0.0, 0.5)),
|
||||
&Vector4::new(0.0, -2.0, 0.0, -1.0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cast() {
|
||||
assert_ulps_eq!(Vector2::new(0.9f64, 1.5).cast().unwrap(), Vector2::new(0.9f32, 1.5));
|
||||
assert_ulps_eq!(Vector3::new(1.0f64, 2.4, -3.13).cast().unwrap(), Vector3::new(1.0f32, 2.4, -3.13));
|
||||
assert_ulps_eq!(Vector4::new(13.5f64, -4.6, -8.3, 2.41).cast().unwrap(), Vector4::new(13.5f32, -4.6, -8.3, 2.41));
|
||||
assert_ulps_eq!(
|
||||
Vector2::new(0.9f64, 1.5).cast().unwrap(),
|
||||
Vector2::new(0.9f32, 1.5)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector3::new(1.0f64, 2.4, -3.13).cast().unwrap(),
|
||||
Vector3::new(1.0f32, 2.4, -3.13)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(13.5f64, -4.6, -8.3, 2.41).cast().unwrap(),
|
||||
Vector4::new(13.5f32, -4.6, -8.3, 2.41)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,12 +22,18 @@ use std::f32;
|
|||
|
||||
#[test]
|
||||
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]
|
||||
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 {
|
||||
|
@ -84,32 +90,60 @@ macro_rules! impl_test_rem {
|
|||
|
||||
#[test]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
|
@ -123,12 +157,22 @@ fn test_sum() {
|
|||
fn test_product() {
|
||||
assert_eq!(Vector4::new(1f32, 2f32, 3f32, 4f32).product(), 24f32);
|
||||
|
||||
assert_eq!(Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32).product(), 1680.0f32);
|
||||
assert_eq!(
|
||||
Vector4::new(5.0f32, 6.0f32, 7.0f32, 8.0f32).product(),
|
||||
1680.0f32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_perpendicular() {
|
||||
assert!(Vector4::new(1.0f32, 0.0f32, 0.0f32, 0.0f32).is_perpendicular(Vector4::new(0.0f32, 0.0f32, 0.0f32, 1.0f32)));
|
||||
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)]
|
||||
|
@ -136,7 +180,7 @@ mod test_magnitude {
|
|||
use cgmath::*;
|
||||
|
||||
#[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 (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);
|
||||
assert_ulps_eq!(a.sqrt_element_wide(), Vector4::new(1f32, 2f32, 3f32, 4f32));
|
||||
assert_relative_eq!(a.sqrt_element_wide().recip_element_wide(), Vector4::new(1f32, 1f32/2f32, 1f32/3f32, 1f32/4f32), max_relative = 0.005f32);
|
||||
assert_relative_eq!(a.rsqrt_element_wide(), Vector4::new(1f32, 1f32/2f32, 1f32/3f32, 1f32/4f32), max_relative= 0.005f32);
|
||||
assert_relative_eq!(
|
||||
a.sqrt_element_wide().recip_element_wide(),
|
||||
Vector4::new(1f32, 1f32 / 2f32, 1f32 / 3f32, 1f32 / 4f32),
|
||||
max_relative = 0.005f32
|
||||
);
|
||||
assert_relative_eq!(
|
||||
a.rsqrt_element_wide(),
|
||||
Vector4::new(1f32, 1f32 / 2f32, 1f32 / 3f32, 1f32 / 4f32),
|
||||
max_relative = 0.005f32
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_angle() {
|
||||
assert_ulps_eq!(Vector4::new(1.0f32, 0.0f32, 1.0f32, 0.0f32).angle(Vector4::new(0.0f32, 1.0f32, 0.0f32, 1.0f32)), &Rad(f32::consts::FRAC_PI_2));
|
||||
assert_ulps_eq!(Vector4::new(10.0f32, 0.0f32, 10.0f32, 0.0f32).angle(Vector4::new(0.0f32, 5.0f32, 0.0f32, 5.0f32)), &Rad(f32::consts::FRAC_PI_2));
|
||||
assert_ulps_eq!(Vector4::new(-1.0f32, 0.0f32, -1.0f32, 0.0f32).angle(Vector4::new(0.0f32, 1.0f32, 0.0f32, 1.0f32)), &Rad(f32::consts::FRAC_PI_2));
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(1.0f32, 0.0f32, 1.0f32, 0.0f32).angle(Vector4::new(
|
||||
0.0f32,
|
||||
1.0f32,
|
||||
0.0f32,
|
||||
1.0f32
|
||||
)),
|
||||
&Rad(f32::consts::FRAC_PI_2)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(10.0f32, 0.0f32, 10.0f32, 0.0f32).angle(Vector4::new(
|
||||
0.0f32,
|
||||
5.0f32,
|
||||
0.0f32,
|
||||
5.0f32
|
||||
)),
|
||||
&Rad(f32::consts::FRAC_PI_2)
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(-1.0f32, 0.0f32, -1.0f32, 0.0f32).angle(Vector4::new(
|
||||
0.0f32,
|
||||
1.0f32,
|
||||
0.0f32,
|
||||
1.0f32
|
||||
)),
|
||||
&Rad(f32::consts::FRAC_PI_2)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize() {
|
||||
// TODO: test normalize_to, normalize_sel.0f32, and normalize_self_to
|
||||
assert_ulps_eq!(Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32).normalize(), &Vector4::new(1.0f32/11.0f32, 2.0f32/11.0f32, 4.0f32/11.0f32, 10.0f32/11.0f32));
|
||||
assert_ulps_eq!(
|
||||
Vector4::new(1.0f32, 2.0f32, 4.0f32, 10.0f32).normalize(),
|
||||
&Vector4::new(
|
||||
1.0f32 / 11.0f32,
|
||||
2.0f32 / 11.0f32,
|
||||
4.0f32 / 11.0f32,
|
||||
10.0f32 / 11.0f32
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cast() {
|
||||
assert_ulps_eq!(Vector4::new(13.5f32, -4.6, -8.3, 2.41).cast().unwrap(), Vector4::new(13.5f32, -4.6, -8.3, 2.41));
|
||||
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