Add MetricSpace trait for calculating distances

This commit is contained in:
Brendan Zabarauskas 2016-04-23 19:52:37 +10:00
parent 04073c724d
commit 8795b179d7
5 changed files with 63 additions and 17 deletions

View file

@ -23,6 +23,7 @@
//! //!
//! - `VectorSpace`: Specifies the main operators for vectors, quaternions, and //! - `VectorSpace`: Specifies the main operators for vectors, quaternions, and
//! matrices. //! matrices.
//! - `MetricSpace`: For types that have a distance function implemented.
//! - `InnerSpace`: For types that have a dot (or inner) product - ie. vectors or //! - `InnerSpace`: For types that have a dot (or inner) product - ie. vectors or
//! quaternions. This also allows for the definition of operations that are //! quaternions. This also allows for the definition of operations that are
//! based on the dot product, like finding the magnitude or normalizing. //! based on the dot product, like finding the magnitude or normalizing.

View file

@ -109,6 +109,15 @@ macro_rules! impl_point {
} }
} }
impl<S: BaseFloat> MetricSpace for $PointN<S> {
type Metric = S;
#[inline]
fn distance2(self, other: Self) -> S {
(other - self).magnitude2()
}
}
impl<S: BaseNum> EuclideanSpace for $PointN<S> { impl<S: BaseNum> EuclideanSpace for $PointN<S> {
type Scalar = S; type Scalar = S;
type Diff = $VectorN<S>; type Diff = $VectorN<S>;

View file

@ -129,6 +129,15 @@ impl<S: BaseFloat> VectorSpace for Quaternion<S> {
} }
} }
impl<S: BaseFloat> MetricSpace for Quaternion<S> {
type Metric = S;
#[inline]
fn distance2(self, other: Self) -> S {
(other - self).magnitude2()
}
}
impl<S: BaseFloat> InnerSpace for Quaternion<S> { impl<S: BaseFloat> InnerSpace for Quaternion<S> {
#[inline] #[inline]
fn dot(self, other: Quaternion<S>) -> S { fn dot(self, other: Quaternion<S>) -> S {

View file

@ -164,6 +164,26 @@ pub trait VectorSpace: Copy + Clone where
fn zero() -> Self; fn zero() -> Self;
} }
/// A type with a distance function between values.
///
/// Examples are vectors, points, and quaternions.
pub trait MetricSpace: Sized {
/// The metric to be returned by the `distance` function.
type Metric: BaseFloat;
/// Returns the squared distance.
///
/// This does not perform an expensive square root operation like in
/// `MetricSpace::distance` method, and so can be used to compare distances
/// more efficiently.
fn distance2(self, other: Self) -> Self::Metric;
/// The distance between two values.
fn distance(self, other: Self) -> Self::Metric {
Float::sqrt(Self::distance2(self, other))
}
}
/// Vectors that also have a [dot](https://en.wikipedia.org/wiki/Dot_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. /// (or [inner](https://en.wikipedia.org/wiki/Inner_product_space)) product.
/// ///
@ -171,9 +191,10 @@ pub trait VectorSpace: Copy + Clone where
/// finding the magnitude of a vector or normalizing it. /// finding the magnitude of a vector or normalizing it.
/// ///
/// Examples include vectors and quaternions. /// Examples include vectors and quaternions.
pub trait InnerSpace: VectorSpace + Sized where pub trait InnerSpace: VectorSpace where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
<Self as VectorSpace>::Scalar: BaseFloat, <Self as VectorSpace>::Scalar: BaseFloat,
Self: MetricSpace<Metric = <Self as VectorSpace>::Scalar>,
Self: ApproxEq<Epsilon = <Self as VectorSpace>::Scalar>, Self: ApproxEq<Epsilon = <Self as VectorSpace>::Scalar>,
{ {
/// Vector dot (or inner) product. /// Vector dot (or inner) product.
@ -185,11 +206,11 @@ pub trait InnerSpace: VectorSpace + Sized where
Self::dot(self, other).approx_eq(&Self::Scalar::zero()) Self::dot(self, other).approx_eq(&Self::Scalar::zero())
} }
/// Returns the squared magnitude of the vector. /// Returns the squared magnitude.
/// ///
/// This does not perform an expensive square root operation like in /// This does not perform an expensive square root operation like in
/// `Vector::magnitude` method, and so can be used to compare vectors more /// `InnerSpace::magnitude` method, and so can be used to compare magnitudes
/// efficiently. /// more efficiently.
#[inline] #[inline]
fn magnitude2(self) -> Self::Scalar { fn magnitude2(self) -> Self::Scalar {
Self::dot(self, self) Self::dot(self, self)
@ -198,9 +219,6 @@ pub trait InnerSpace: VectorSpace + Sized where
/// The distance from the tail to the tip of the vector. /// The distance from the tail to the tip of the vector.
#[inline] #[inline]
fn magnitude(self) -> Self::Scalar { fn magnitude(self) -> Self::Scalar {
use num_traits::Float;
// FIXME: Not sure why we can't use method syntax for `sqrt` here...
Float::sqrt(self.magnitude2()) Float::sqrt(self.magnitude2())
} }

View file

@ -69,11 +69,11 @@ pub struct Vector4<S> {
// Utility macro for generating associated functions for the vectors // Utility macro for generating associated functions for the vectors
macro_rules! impl_vector { macro_rules! impl_vector {
($VectorN:ident <$S:ident> { $($field:ident),+ }, $n:expr, $constructor:ident) => { ($VectorN:ident { $($field:ident),+ }, $n:expr, $constructor:ident) => {
impl<$S> $VectorN<$S> { impl<S> $VectorN<S> {
/// Construct a new vector, using the provided values. /// Construct a new vector, using the provided values.
#[inline] #[inline]
pub fn new($($field: $S),+) -> $VectorN<$S> { pub fn new($($field: S),+) -> $VectorN<S> {
$VectorN { $($field: $field),+ } $VectorN { $($field: $field),+ }
} }
} }
@ -84,7 +84,7 @@ macro_rules! impl_vector {
$VectorN::new($($field),+) $VectorN::new($($field),+)
} }
impl<$S: NumCast + Copy> $VectorN<$S> { impl<S: NumCast + Copy> $VectorN<S> {
/// Component-wise casting to another type /// Component-wise casting to another type
#[inline] #[inline]
pub fn cast<T: NumCast>(&self) -> $VectorN<T> { pub fn cast<T: NumCast>(&self) -> $VectorN<T> {
@ -92,6 +92,15 @@ macro_rules! impl_vector {
} }
} }
impl<S: BaseFloat> MetricSpace for $VectorN<S> {
type Metric = S;
#[inline]
fn distance2(self, other: Self) -> S {
(other - self).magnitude2()
}
}
impl<S: Copy> Array for $VectorN<S> { impl<S: Copy> Array for $VectorN<S> {
type Element = S; type Element = S;
@ -251,9 +260,9 @@ macro_rules! impl_scalar_ops {
}; };
} }
impl_vector!(Vector2<S> { x, y }, 2, vec2); impl_vector!(Vector2 { x, y }, 2, vec2);
impl_vector!(Vector3<S> { x, y, z }, 3, vec3); impl_vector!(Vector3 { x, y, z }, 3, vec3);
impl_vector!(Vector4<S> { x, y, z, w }, 4, vec4); impl_vector!(Vector4 { x, y, z, w }, 4, vec4);
impl_fixed_array_conversions!(Vector2<S> { x: 0, y: 1 }, 2); impl_fixed_array_conversions!(Vector2<S> { x: 0, y: 1 }, 2);
impl_fixed_array_conversions!(Vector3<S> { x: 0, y: 1, z: 2 }, 3); impl_fixed_array_conversions!(Vector3<S> { x: 0, y: 1, z: 2 }, 3);