diff --git a/CHANGELOG.md b/CHANGELOG.md index f7e3606..cce4b47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Constrained conversion functions for assisting in situations where type inference is difficult. - An `ElementWise` trait for non-mathematical element-wise operations. +- A default implementation for `EuclideanVector::angle`. ### Changed @@ -21,13 +22,16 @@ This project adheres to [Semantic Versioning](http://semver.org/). formatting. - Marks vectors, points, matrices, and angles as `#[repr(C, packed)]`. - Renames the `Vector::{length, length2}` functions to `Vector::{magnitude, magnitude2}`. -- Moved `Angle::new` to be directly implemented on the `Rad` and `Deg` types. +- Move `Angle::new` to be directly implemented on the `Rad` and `Deg` types. +- Move `Vector::dot` to `EuclideanVector` trait. +- Move `Vector::from_value` to `Array` trait. ### Removed - The non-mathematical operator trait implementations have been removed from the `Vector` trait, in favor of the `ElementWise` trait. - `Angle::equiv`. +- Remove `neg_self` method on vectors and matrices. ## [v0.7.0] - 2015-12-23 diff --git a/src/array.rs b/src/array.rs index f71c37f..df068de 100644 --- a/src/array.rs +++ b/src/array.rs @@ -26,6 +26,17 @@ pub trait Array where { type Element: Copy; + /// Construct a vector from a single value, replicating it. + /// + /// ```rust + /// use cgmath::prelude::*; + /// use cgmath::Vector3; + /// + /// assert_eq!(Vector3::from_value(1), + /// Vector3::new(1, 1, 1)); + /// ``` + fn from_value(value: Self::Element) -> Self; + /// Get the pointer to the first element of the array. #[inline] fn as_ptr(&self) -> *const Self::Element { diff --git a/src/matrix.rs b/src/matrix.rs index f364072..aa1aaf2 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -100,15 +100,6 @@ impl Matrix2 { } } -impl> Matrix2 { - /// Negate this `Matrix2` in-place. - #[inline] - pub fn neg_self(&mut self) { - self[0].neg_self(); - self[1].neg_self(); - } -} - impl Matrix3 { /// Create a new matrix, providing values for each index. #[inline] @@ -200,16 +191,6 @@ impl Matrix3 { } } -impl> Matrix3 { - /// Negate this `Matrix3` in-place. - #[inline] - pub fn neg_self(&mut self) { - self[0].neg_self(); - self[1].neg_self(); - self[2].neg_self(); - } -} - impl Matrix4 { /// Create a new matrix, providing values for each index. #[inline] @@ -267,17 +248,6 @@ impl Matrix4 { } } -impl> Matrix4 { - /// Negate this `Matrix4` in-place. - #[inline] - pub fn neg_self(&mut self) { - self[0].neg_self(); - self[1].neg_self(); - self[2].neg_self(); - self[3].neg_self(); - } -} - /// A column-major matrix of arbitrary dimensions. pub trait Matrix where // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 diff --git a/src/point.rs b/src/point.rs index fbf21fd..e35877c 100644 --- a/src/point.rs +++ b/src/point.rs @@ -169,10 +169,30 @@ macro_rules! impl_point { impl Array for $PointN { type Element = S; - #[inline] fn sum(self) -> S { fold_array!(add, { $(self.$field),+ }) } - #[inline] fn product(self) -> S { fold_array!(mul, { $(self.$field),+ }) } - #[inline] fn min(self) -> S { fold_array!(partial_min, { $(self.$field),+ }) } - #[inline] fn max(self) -> S { fold_array!(partial_max, { $(self.$field),+ }) } + #[inline] + fn from_value(scalar: S) -> $PointN { + $PointN { $($field: scalar),+ } + } + + #[inline] + fn sum(self) -> S where S: Add { + fold_array!(add, { $(self.$field),+ }) + } + + #[inline] + fn product(self) -> S where S: Mul { + fold_array!(mul, { $(self.$field),+ }) + } + + #[inline] + fn min(self) -> S where S: PartialOrd { + fold_array!(partial_min, { $(self.$field),+ }) + } + + #[inline] + fn max(self) -> S where S: PartialOrd { + fold_array!(partial_max, { $(self.$field),+ }) + } } impl Point for $PointN { diff --git a/src/rotation.rs b/src/rotation.rs index 993188e..614a5ac 100644 --- a/src/rotation.rs +++ b/src/rotation.rs @@ -22,7 +22,7 @@ use matrix::{Matrix2, Matrix3}; use num::BaseFloat; use point::{Point, Point2, Point3}; use quaternion::Quaternion; -use vector::{Vector, Vector2, Vector3}; +use vector::{EuclideanVector, 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. diff --git a/src/vector.rs b/src/vector.rs index 47decd2..659527f 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -22,11 +22,10 @@ //! vector are also provided: //! //! ```rust -//! use cgmath::{Vector, Vector2, Vector3, Vector4, vec2, vec3}; +//! use cgmath::{Vector, Vector2, Vector3, Vector4, vec3}; //! //! assert_eq!(Vector2::new(1.0f64, 0.0f64), Vector2::unit_x()); //! assert_eq!(vec3(0.0f64, 0.0f64, 0.0f64), Vector3::zero()); -//! assert_eq!(Vector2::from_value(1.0f64), vec2(1.0, 1.0)); //! ``` //! //! Vectors can be manipulated with typical mathematical operations (addition, @@ -62,7 +61,8 @@ //! and [cross products](http://en.wikipedia.org/wiki/Cross_product). //! //! ```rust -//! use cgmath::{Vector, Vector2, Vector3, Vector4, dot}; +//! use cgmath::{Vector, EuclideanVector}; +//! use cgmath::{Vector2, Vector3, Vector4}; //! //! // All vectors implement the dot product as a method: //! let a: Vector2 = Vector2::new(3.0, 6.0); @@ -70,7 +70,7 @@ //! assert_eq!(a.dot(b), 0.0); //! //! // But there is also a top-level function: -//! assert_eq!(a.dot(b), dot(a, b)); +//! assert_eq!(a.dot(b), cgmath::dot(a, b)); //! //! // Cross products are defined for 3-dimensional vectors: //! let e: Vector3 = Vector3::unit_x(); @@ -100,7 +100,49 @@ use approx::ApproxEq; use array::{Array, ElementWise}; use num::{BaseNum, BaseFloat, PartialOrd}; -/// A trait that specifies a range of numeric operations for vectors. +/// Vectors that can be [added](http://mathworld.wolfram.com/VectorAddition.html) +/// together and [multiplied](https://en.wikipedia.org/wiki/Scalar_multiplication) +/// by scalars. +/// +/// # Required operators +/// +/// ## Vector addition +/// +/// Vectors can be added, subtracted, or negated via the following traits: +/// +/// - `Add` +/// - `Sub` +/// - `Neg` +/// +/// ```rust +/// use cgmath::Vector3; +/// +/// let velocity0 = Vector3::new(1, 2, 0); +/// let velocity1 = Vector3::new(1, 1, 0); +/// +/// let total_velocity = velocity0 + velocity1; +/// let velocity_diff = velocity1 - velocity0; +/// let reversed_velocity0 = -velocity0; +/// ``` +/// +/// ## Scalar multiplication +/// +/// Vectors can be multiplied or divided by their associated scalars via the +/// following traits: +/// +/// - `Mul` +/// - `Div` +/// - `Rem` +/// +/// ```rust +/// use cgmath::Vector2; +/// +/// let translation = Vector2::new(3.0, 4.0); +/// let scale_factor = 2.0; +/// +/// let upscaled_translation = translation * scale_factor; +/// let downscaled_translation = translation / scale_factor; +/// ``` pub trait Vector: Copy + Clone where // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: Array::Scalar>, @@ -115,27 +157,29 @@ pub trait Vector: Copy + Clone where /// The associated scalar. type Scalar: BaseNum; - /// Construct a vector from a single value, replicating it. - fn from_value(scalar: Self::Scalar) -> Self; - - /// The additive identity vector. Adding this vector with another has no effect. - #[inline] - fn zero() -> Self { Self::from_value(Self::Scalar::zero()) } - - /// Vector dot product - fn dot(self, other: Self) -> Self::Scalar; + /// The additive identity. + /// + /// Adding this to another `Self` value has no effect. + /// + /// ```rust + /// use cgmath::prelude::*; + /// use cgmath::Vector2; + /// + /// let v = Vector2::new(1, 2); + /// assert_eq!(v + Vector2::zero(), v); + /// ``` + fn zero() -> Self; } -/// Dot product of two vectors. -#[inline] pub fn dot(a: V, b: V) -> V::Scalar { V::dot(a, b) } - /// A 2-dimensional vector. /// /// This type is marked as `#[repr(C, packed)]`. #[repr(C, packed)] #[derive(PartialEq, Eq, Copy, Clone, Hash, RustcEncodable, RustcDecodable)] pub struct Vector2 { + /// The x component of the vector. pub x: S, + /// The y component of the vector. pub y: S, } @@ -145,8 +189,11 @@ pub struct Vector2 { #[repr(C, packed)] #[derive(PartialEq, Eq, Copy, Clone, Hash, RustcEncodable, RustcDecodable)] pub struct Vector3 { + /// The x component of the vector. pub x: S, + /// The y component of the vector. pub y: S, + /// The z component of the vector. pub z: S, } @@ -156,9 +203,13 @@ pub struct Vector3 { #[repr(C, packed)] #[derive(PartialEq, Eq, Copy, Clone, Hash, RustcEncodable, RustcDecodable)] pub struct Vector4 { + /// The x component of the vector. pub x: S, + /// The y component of the vector. pub y: S, + /// The z component of the vector. pub z: S, + /// The w component of the vector. pub w: S, } @@ -173,14 +224,6 @@ macro_rules! impl_vector { } } - impl<$S: Copy + Neg> $VectorN<$S> { - /// Negate this vector in-place (multiply by -1). - #[inline] - pub fn neg_self(&mut self) { - $(self.$field = -self.$field);+ - } - } - /// The short constructor. #[inline] pub fn $constructor($($field: S),+) -> $VectorN { @@ -198,18 +241,39 @@ macro_rules! impl_vector { impl Array for $VectorN { type Element = S; - #[inline] fn sum(self) -> S where S: Add { fold_array!(add, { $(self.$field),+ }) } - #[inline] fn product(self) -> S where S: Mul { fold_array!(mul, { $(self.$field),+ }) } - #[inline] fn min(self) -> S where S: PartialOrd { fold_array!(partial_min, { $(self.$field),+ }) } - #[inline] fn max(self) -> S where S: PartialOrd { fold_array!(partial_max, { $(self.$field),+ }) } + #[inline] + fn from_value(scalar: S) -> $VectorN { + $VectorN { $($field: scalar),+ } + } + + #[inline] + fn sum(self) -> S where S: Add { + fold_array!(add, { $(self.$field),+ }) + } + + #[inline] + fn product(self) -> S where S: Mul { + fold_array!(mul, { $(self.$field),+ }) + } + + #[inline] + fn min(self) -> S where S: PartialOrd { + fold_array!(partial_min, { $(self.$field),+ }) + } + + #[inline] + fn max(self) -> S where S: PartialOrd { + fold_array!(partial_max, { $(self.$field),+ }) + } } impl Vector for $VectorN { type Scalar = S; - #[inline] fn from_value(scalar: S) -> $VectorN { $VectorN { $($field: scalar),+ } } - - #[inline] fn dot(self, other: $VectorN) -> S { $VectorN::mul_element_wise(self, other).sum() } + #[inline] + fn zero() -> Self { + Self::from_value(Self::Scalar::zero()) + } } impl> Neg for $VectorN { @@ -461,13 +525,19 @@ impl Vector4 { } } -/// Specifies geometric operations for vectors. This is only implemented for -/// vectors of float types. +/// Vectors that also have a [dot](https://en.wikipedia.org/wiki/Dot_product) +/// (or [inner](https://en.wikipedia.org/wiki/Inner_product_space)) product. +/// +/// The dot product allows for the definition of other useful operations, like +/// finding the magnitude of a vector or normalizing it. pub trait EuclideanVector: Vector + Sized where // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 ::Scalar: BaseFloat, Self: ApproxEq::Scalar>, { + /// Vector dot (or inner) product. + fn dot(self, other: Self) -> Self::Scalar; + /// Returns `true` if the vector is perpendicular (at right angles) to the /// other vector. fn is_perpendicular(self, other: Self) -> bool { @@ -493,8 +563,10 @@ pub trait EuclideanVector: Vector + Sized where Float::sqrt(self.magnitude2()) } - /// The angle between the vector and `other`, in radians. - fn angle(self, other: Self) -> Rad; + /// Returns the angle between two vectors in radians. + fn angle(self, other: Self) -> Rad { + Rad::acos(Self::dot(self, other) / (self.magnitude() * other.magnitude())) + } /// Returns a vector with the same direction, but with a magnitude of `1`. #[inline] @@ -519,7 +591,20 @@ pub trait EuclideanVector: Vector + Sized where } } +/// Dot product of two vectors. +#[inline] +pub fn dot(a: V, b: V) -> V::Scalar where + V::Scalar: BaseFloat, +{ + V::dot(a, b) +} + impl EuclideanVector for Vector2 { + #[inline] + fn dot(self, other: Vector2) -> S { + Vector2::mul_element_wise(self, other).sum() + } + #[inline] fn angle(self, other: Vector2) -> Rad { Rad::atan2(Self::perp_dot(self, other), Self::dot(self, other)) @@ -527,6 +612,11 @@ impl EuclideanVector for Vector2 { } impl EuclideanVector for Vector3 { + #[inline] + fn dot(self, other: Vector3) -> S { + Vector3::mul_element_wise(self, other).sum() + } + #[inline] fn angle(self, other: Vector3) -> Rad { Rad::atan2(self.cross(other).magnitude(), Self::dot(self, other)) @@ -535,8 +625,8 @@ impl EuclideanVector for Vector3 { impl EuclideanVector for Vector4 { #[inline] - fn angle(self, other: Vector4) -> Rad { - Rad::acos(Self::dot(self, other) / (self.magnitude() * other.magnitude())) + fn dot(self, other: Vector4) -> S { + Vector4::mul_element_wise(self, other).sum() } } diff --git a/tests/vector.rs b/tests/vector.rs index c8d84f7..d7ed20b 100644 --- a/tests/vector.rs +++ b/tests/vector.rs @@ -122,9 +122,9 @@ fn test_rem() { #[test] fn test_dot() { - assert_eq!(Vector2::new(1isize, 2isize).dot(Vector2::new(3isize, 4isize)), 11isize); - assert_eq!(Vector3::new(1isize, 2isize, 3isize).dot(Vector3::new(4isize, 5isize, 6isize)), 32isize); - assert_eq!(Vector4::new(1isize, 2isize, 3isize, 4isize).dot(Vector4::new(5isize, 6isize, 7isize, 8isize)), 70isize); + assert_eq!(Vector2::new(1.0, 2.0).dot(Vector2::new(3.0, 4.0)), 11.0); + assert_eq!(Vector3::new(1.0, 2.0, 3.0).dot(Vector3::new(4.0, 5.0, 6.0)), 32.0); + assert_eq!(Vector4::new(1.0, 2.0, 3.0, 4.0).dot(Vector4::new(5.0, 6.0, 7.0, 8.0)), 70.0); } #[test]