From 2cd6f402df8fbaa3d1cbc4632322c26cbc521d11 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 4 Apr 2016 19:45:54 +1000 Subject: [PATCH 1/9] Move the dot method onto EuclideanVector The Vector and EuclideanVector traits roughly line up with the concept of vector spaces and inner spaces respectively. It makes more sense to group `dot` with the other methods that depend on it. --- src/rotation.rs | 2 +- src/vector.rs | 44 +++++++++++++++++++++++++++++++++----------- tests/vector.rs | 6 +++--- 3 files changed, 37 insertions(+), 15 deletions(-) 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..b443c32 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -62,7 +62,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 +71,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(); @@ -121,14 +122,8 @@ pub trait Vector: Copy + Clone where /// 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; } -/// 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)]`. @@ -207,9 +202,10 @@ macro_rules! impl_vector { 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 from_value(scalar: S) -> $VectorN { + $VectorN { $($field: scalar),+ } + } } impl> Neg for $VectorN { @@ -468,6 +464,9 @@ pub trait EuclideanVector: Vector + Sized where ::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 { @@ -519,7 +518,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 +539,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)) @@ -534,6 +551,11 @@ impl EuclideanVector for Vector3 { } impl EuclideanVector for Vector4 { + #[inline] + fn dot(self, other: Vector4) -> S { + Vector4::mul_element_wise(self, other).sum() + } + #[inline] fn angle(self, other: Vector4) -> Rad { Rad::acos(Self::dot(self, other) / (self.magnitude() * other.magnitude())) 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] From e0a516496712f673c490899dbdb6224cecb2785b Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 4 Apr 2016 19:46:48 +1000 Subject: [PATCH 2/9] Improve documentation for vector traits --- src/vector.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/src/vector.rs b/src/vector.rs index b443c32..641d6cd 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -101,7 +101,48 @@ 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 together and multiplied by scalars. +/// +/// # Required operators +/// +/// ## Vector addition +/// +/// Vectors are required to be able to 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 are required to be able to 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>, @@ -117,9 +158,27 @@ pub trait Vector: Copy + Clone where type Scalar: BaseNum; /// 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(scalar: Self::Scalar) -> Self; - /// The additive identity vector. Adding this vector with another has no effect. + /// 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); + /// ``` #[inline] fn zero() -> Self { Self::from_value(Self::Scalar::zero()) } } @@ -457,8 +516,10 @@ impl Vector4 { } } -/// Specifies geometric operations for vectors. This is only implemented for -/// vectors of float types. +/// Vectors that also have a dot (or inner) 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, From 3b55ad5f70953fdb9702699dff8334bb2e539284 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 4 Apr 2016 19:53:55 +1000 Subject: [PATCH 3/9] Move Vector::from_value function to Array trait --- src/array.rs | 11 +++++++++++ src/point.rs | 28 ++++++++++++++++++++++++---- src/vector.rs | 49 ++++++++++++++++++++++++++++--------------------- 3 files changed, 63 insertions(+), 25 deletions(-) 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/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/vector.rs b/src/vector.rs index 641d6cd..606f5ed 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, @@ -157,17 +156,6 @@ pub trait Vector: Copy + Clone where /// The associated scalar. type Scalar: BaseNum; - /// 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(scalar: Self::Scalar) -> Self; - /// The additive identity. /// /// Adding this to another `Self` value has no effect. @@ -179,8 +167,7 @@ pub trait Vector: Copy + Clone where /// let v = Vector2::new(1, 2); /// assert_eq!(v + Vector2::zero(), v); /// ``` - #[inline] - fn zero() -> Self { Self::from_value(Self::Scalar::zero()) } + fn zero() -> Self; } /// A 2-dimensional vector. @@ -252,18 +239,38 @@ 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),+ } + fn zero() -> Self { + Self::from_value(Self::Scalar::zero()) } } From 40a3ad31852f8d065d2a0baa77254dbcaaaf7d0e Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 4 Apr 2016 20:20:31 +1000 Subject: [PATCH 4/9] Add a default implementation for EuclideanVector::angle --- src/vector.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vector.rs b/src/vector.rs index 606f5ed..a6ef745 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -560,8 +560,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] @@ -623,11 +625,6 @@ impl EuclideanVector for Vector4 { fn dot(self, other: Vector4) -> S { Vector4::mul_element_wise(self, other).sum() } - - #[inline] - fn angle(self, other: Vector4) -> Rad { - Rad::acos(Self::dot(self, other) / (self.magnitude() * other.magnitude())) - } } impl fmt::Debug for Vector2 { From 019cac1f74973642e5c96c5a7ce4390355045636 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 4 Apr 2016 20:25:51 +1000 Subject: [PATCH 5/9] Add some links to wikipedia pages --- src/vector.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vector.rs b/src/vector.rs index a6ef745..6b8a80e 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -100,7 +100,9 @@ use approx::ApproxEq; use array::{Array, ElementWise}; use num::{BaseNum, BaseFloat, PartialOrd}; -/// Vectors that can be added together and multiplied by scalars. +/// 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 /// @@ -523,7 +525,8 @@ impl Vector4 { } } -/// Vectors that also have a dot (or inner) product. +/// 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. From f766973a74bdaba4dbec03042e3597358990e047 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 4 Apr 2016 20:34:07 +1000 Subject: [PATCH 6/9] Remove neg_self methods These are a weird methods... they aren't associated with any traits. I think they were left-over from before we moved to operator impls. --- src/matrix.rs | 30 ------------------------------ src/vector.rs | 8 -------- 2 files changed, 38 deletions(-) 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/vector.rs b/src/vector.rs index 6b8a80e..8d5682c 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -216,14 +216,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 { From 90dbd29f6dc8ddb6e4bef0b3bb2a9433bec91f0c Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 4 Apr 2016 20:39:18 +1000 Subject: [PATCH 7/9] Make some documentation more succinct --- src/vector.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vector.rs b/src/vector.rs index 8d5682c..64bbcfc 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -108,8 +108,7 @@ use num::{BaseNum, BaseFloat, PartialOrd}; /// /// ## Vector addition /// -/// Vectors are required to be able to be added, subtracted, or negated via the -/// following traits: +/// Vectors can be added, subtracted, or negated via the following traits: /// /// - `Add` /// - `Sub` @@ -128,8 +127,8 @@ use num::{BaseNum, BaseFloat, PartialOrd}; /// /// ## Scalar multiplication /// -/// Vectors are required to be able to be multiplied or divided by their -/// associated scalars via the following traits: +/// Vectors can be multiplied or divided by their associated scalars via the +/// following traits: /// /// - `Mul` /// - `Div` From 41a7d1a22d32e616fcf8098f1b0df6a51a8ce34a Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 4 Apr 2016 20:43:51 +1000 Subject: [PATCH 8/9] Update changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 From f82c8826a2fc1f7ba0b0c5fad063bc0e8486b51a Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Tue, 5 Apr 2016 08:52:27 +1000 Subject: [PATCH 9/9] Add doc comments for vector axes --- src/vector.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vector.rs b/src/vector.rs index 64bbcfc..659527f 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -177,7 +177,9 @@ pub trait Vector: Copy + Clone where #[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, } @@ -187,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, } @@ -198,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, }