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

@ -22,10 +22,11 @@
//! concern ourselves with are listed below:
//!
//! - `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
//! quaternions. This also allows for the definition of operations that are
//! based on the dot product, like finding the magnitude or normalizing.
//! quaternions. This also allows for the definition of operations that are
//! based on the dot product, like finding the magnitude or normalizing.
//! - `EuclideanSpace`: Points in euclidean space, with an associated space of
//! displacement vectors.
//! - `Matrix`: Common operations for matrices of arbitrary dimensions.

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> {
type Scalar = 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> {
#[inline]
fn dot(self, other: Quaternion<S>) -> S {

View file

@ -164,6 +164,26 @@ pub trait VectorSpace: Copy + Clone where
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)
/// (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.
///
/// 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
<Self as VectorSpace>::Scalar: BaseFloat,
Self: MetricSpace<Metric = <Self as VectorSpace>::Scalar>,
Self: ApproxEq<Epsilon = <Self as VectorSpace>::Scalar>,
{
/// Vector dot (or inner) product.
@ -185,11 +206,11 @@ pub trait InnerSpace: VectorSpace + Sized where
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
/// `Vector::magnitude` method, and so can be used to compare vectors more
/// efficiently.
/// `InnerSpace::magnitude` method, and so can be used to compare magnitudes
/// more efficiently.
#[inline]
fn magnitude2(self) -> Self::Scalar {
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.
#[inline]
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())
}

View file

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