Implement traits from the algebra crate.

This commit is contained in:
Sébastien Crozet 2016-08-20 23:13:53 +02:00
parent fc08caabbe
commit d29ff53329
24 changed files with 436 additions and 31 deletions

View File

@ -43,6 +43,10 @@ longer unsafe. Instead, their name end with `_unchecked`. In particular:
well. It is clear that performing computations with floats requires
approximate equality.
Still WIP: add implementations of abstract algebra traits from the `algebra`
crate for vectors, rotations and points. To enable them, activate the
`abstract_algebra` feature.
## [0.8.0]
### Modified
* Almost everything (types, methods, and traits) now use full names instead

View File

@ -19,12 +19,12 @@ path = "src/lib.rs"
# Generate arbitrary instances of nalgebra types for testing with quickcheck
arbitrary = [ "quickcheck" ]
generic_sizes = [ "generic-array", "typenum" ]
abstract_algebra = [ "algebra" ]
[dependencies]
rustc-serialize = "0.3.*"
rand = "0.3.*"
num = "0.1.*"
# algebra = "*"
[dependencies.generic-array]
optional = true
@ -37,3 +37,7 @@ version = "1.3.*"
[dependencies.quickcheck]
optional = true
version = "0.2.*"
[dependencies.algebra]
optional = true
version = "0.2.*"

View File

@ -1,18 +1,18 @@
tmp=_git_distcheck
all:
cargo build --release --features "arbitrary generic_sizes"
cargo build --release --features "arbitrary generic_sizes abstract_algebra"
test:
cargo test --features "arbitrary generic_sizes"
cargo test --features "arbitrary generic_sizes abstract_algebra"
bench:
cargo bench --features "arbitrary generic_sizes"
cargo bench --features "arbitrary generic_sizes abstract_algebra"
doc:
cargo doc --no-deps --features "arbitrary generic_sizes"
cargo doc --no-deps --features "arbitrary generic_sizes abstract_algebra"
clean:

View File

@ -59,7 +59,8 @@ an optimized set of tools for computer graphics and physics. Those features incl
* Points with static sizes: `Point1`, `Point2`, `Point3`, `Point4`, `Point5`, `Point6`.
* Square matrices with static sizes: `Matrix1`, `Matrix2`, `Matrix3`, `Matrix4`, `Matrix5`, `Matrix6 `.
* Rotation matrices: `Rotation2`, `Rotation3`
* Quaternions: `Quaternion`, `UnitQuaternion`.
* Quaternions: `Quaternion`, `Unit<Quaternion>`.
* Unit-sized values (unit vectors, unit quaternions, etc.): `Unit<T>`, e.g., `Unit<Vector3<f32>>`.
* Isometries (translation rotation): `Isometry2`, `Isometry3`
* Similarity transformations (translation rotation uniform scale): `Similarity2`, `Similarity3`.
* 3D projections for computer graphics: `Persp3`, `PerspMatrix3`, `Ortho3`, `OrthoMatrix3`.

View File

@ -47,6 +47,7 @@ an optimized set of tools for computer graphics and physics. Those features incl
* Square matrices with static sizes: `Matrix1`, `Matrix2`, `Matrix3`, `Matrix4`, `Matrix5`, `Matrix6 `.
* Rotation matrices: `Rotation2`, `Rotation3`
* Quaternions: `Quaternion`, `UnitQuaternion`.
* Unit-sized values (unit vectors, unit quaternions, etc.): `Unit<T>`, e.g., `Unit<Vector3<f32>>`.
* Isometries (translation rotation): `Isometry2`, `Isometry3`
* Similarity transformations (translation rotation uniform scale): `Similarity2`, `Similarity3`.
* 3D projections for computer graphics: `Perspective3`, `PerspectiveMatrix3`, `Orthographic3`, `OrthographicMatrix3`.
@ -85,6 +86,9 @@ extern crate generic_array;
#[cfg(feature="arbitrary")]
extern crate quickcheck;
#[cfg(feature="abstract_algebra")]
extern crate algebra;
use std::cmp;
use std::ops::{Neg, Mul};
use num::{Zero, One};

View File

@ -0,0 +1,17 @@
#![macro_use]
macro_rules! vector_space_impl(
($t: ident, $dimension: expr, $($compN: ident),+) => { }
);
macro_rules! special_orthogonal_group_impl(
($t: ident, $point: ident, $vector: ident) => { }
);
macro_rules! euclidean_space_impl(
($t: ident, $vector: ident) => { }
);
macro_rules! matrix_group_approx_impl(
($t: ident, $($compN: ident),+) => { }
);

View File

@ -0,0 +1,25 @@
#![macro_use]
macro_rules! use_matrix_group_modules(
() => {
use algebra::cmp::ApproxEq as AlgebraApproxEq;
}
);
macro_rules! matrix_group_approx_impl(
($t: ident, $($compN: ident),+) => {
impl<N: AlgebraApproxEq> AlgebraApproxEq for $t<N> {
type Eps = N::Eps;
#[inline]
fn default_epsilon() -> N::Eps {
N::default_epsilon()
}
#[inline]
fn approx_eq_eps(&self, other: &$t<N>, epsilon: &N::Eps) -> bool {
$(AlgebraApproxEq::approx_eq_eps(&self.$compN, &other.$compN, &epsilon))&&+
}
}
}
);

View File

@ -0,0 +1,13 @@
#![macro_use]
#[cfg(not(feature="abstract_algebra"))]
mod dummy;
#[cfg(feature="abstract_algebra")]
mod vector;
#[cfg(feature="abstract_algebra")]
mod rotation;
#[cfg(feature="abstract_algebra")]
mod point;
#[cfg(feature="abstract_algebra")]
mod matrix;

View File

@ -0,0 +1,34 @@
#![macro_use]
macro_rules! use_euclidean_space_modules(
() => {
use algebra::structure::{AffineSpaceApprox, EuclideanSpaceApprox,
FieldApprox, RealApprox};
use algebra::cmp::ApproxEq as AlgebraApproxEq;
}
);
macro_rules! euclidean_space_impl(
($t: ident, $vector: ident) => {
impl<N> AffineSpaceApprox<N> for $t<N>
where N: Copy + Neg<Output = N> + Add<N, Output = N> +
Sub<N, Output = N> + AlgebraApproxEq + FieldApprox {
type Translation = $vector<N>;
#[inline]
fn translate_by(&self, vector: &Self::Translation) -> Self {
*self + *vector
}
#[inline]
fn subtract(&self, other: &Self) -> Self::Translation {
*self - *other
}
}
impl<N: RealApprox> EuclideanSpaceApprox<N> for $t<N> {
type Vector = $vector<N>;
}
}
);

View File

@ -0,0 +1,92 @@
#![macro_use]
macro_rules! use_special_orthogonal_group_modules(
() => {
use algebra::structure::{EuclideanGroupApprox, SpecialEuclideanGroupApprox,
OrthogonalGroupApprox, SpecialOrthogonalGroupApprox,
GroupApprox, LoopApprox, MonoidApprox,
QuasigroupApprox, SemigroupApprox,
RealApprox};
use algebra::cmp::ApproxEq as AlgebraApproxEq;
use algebra::ident::Identity;
use algebra::ops::{Recip, Multiplicative};
}
);
macro_rules! special_orthogonal_group_impl(
($t: ident, $point: ident, $vector: ident) => {
/*
* Operations.
*/
impl<N: BaseNum> Identity<Multiplicative> for $t<N> {
#[inline]
fn id() -> Self {
::one()
}
}
impl<N: Copy + AlgebraApproxEq<Eps = N>> AlgebraApproxEq for $t<N> {
type Eps = N;
#[inline]
fn default_epsilon() -> N {
<N as AlgebraApproxEq>::default_epsilon()
}
#[inline]
fn approx_eq_eps(&self, other: &$t<N>, epsilon: &N) -> bool {
AlgebraApproxEq::approx_eq_eps(&self.submatrix, &other.submatrix, &epsilon)
}
}
impl<N: Copy> Recip for $t<N> {
type Result = $t<N>;
#[inline]
fn recip(mut self) -> $t<N> {
self.inverse_mut();
self
}
}
/*
*
* Algebraic structures.
*
*/
// FIXME: in the end, we will keep only RealApprox.
impl<N: BaseNum + RealApprox> GroupApprox<Multiplicative> for $t<N> { }
impl<N: BaseNum + RealApprox> LoopApprox<Multiplicative> for $t<N> { }
impl<N: BaseNum + RealApprox> MonoidApprox<Multiplicative> for $t<N> { }
impl<N: BaseNum + RealApprox> QuasigroupApprox<Multiplicative> for $t<N> { }
impl<N: BaseNum + RealApprox> SemigroupApprox<Multiplicative> for $t<N> { }
/*
*
* Matrix groups.
*
*/
impl<N: BaseNum + RealApprox> EuclideanGroupApprox<N, $point<N>> for $t<N> {
#[inline]
fn transform_point(&self, pt: &$point<N>) -> $point<N> {
*self * *pt
}
#[inline]
fn transform_vector(&self, v: &$vector<N>) -> $vector<N> {
*self * *v
}
}
impl<N: BaseNum + RealApprox> SpecialEuclideanGroupApprox<N, $point<N>> for $t<N> {
}
impl<N: BaseNum + RealApprox> OrthogonalGroupApprox<N, $point<N>> for $t<N> {
}
impl<N: BaseNum + RealApprox> SpecialOrthogonalGroupApprox<N, $point<N>> for $t<N> {
}
}
);

View File

@ -0,0 +1,186 @@
#![macro_use]
macro_rules! use_vector_space_modules(
() => {
use algebra::structure::{FieldApprox, RingCommutativeApprox, GroupAbelianApprox,
GroupApprox, LoopApprox, MonoidApprox, QuasigroupApprox,
SemigroupApprox, VectorSpaceApprox, ModuleApprox,
NormedSpaceApprox, InnerSpaceApprox,
FiniteDimVectorSpaceApprox,
Field, RingCommutative, GroupAbelian,
Group, Loop, Monoid, Quasigroup,
Semigroup, VectorSpace, Module, RealApprox};
use algebra::cmp::ApproxEq as AlgebraApproxEq;
use algebra::ident::Identity;
use algebra::ops::Additive;
}
);
macro_rules! vector_space_impl(
($t: ident, $dimension: expr, $($compN: ident),+) => {
/*
* Identity & ApproxEq
*/
impl<N: Copy + Identity<Additive>> Identity<Additive> for $t<N> {
#[inline]
fn id() -> Self {
Repeat::repeat(Identity::id())
}
}
impl<N: AlgebraApproxEq> AlgebraApproxEq for $t<N> {
type Eps = N::Eps;
#[inline]
fn default_epsilon() -> N::Eps {
N::default_epsilon()
}
#[inline]
fn approx_eq_eps(&self, other: &$t<N>, epsilon: &N::Eps) -> bool {
$(AlgebraApproxEq::approx_eq_eps(&self.$compN, &other.$compN, &epsilon))&&+
}
}
/*
*
* Approximate algebraic structures.
*
*/
product_space_inherit_structure!($t, GroupAbelianApprox<Additive>);
product_space_inherit_structure!($t, GroupApprox<Additive>);
product_space_inherit_structure!($t, LoopApprox<Additive>);
product_space_inherit_structure!($t, MonoidApprox<Additive>);
product_space_inherit_structure!($t, QuasigroupApprox<Additive>);
product_space_inherit_structure!($t, SemigroupApprox<Additive>);
/*
* Module.
*/
impl<N> ModuleApprox<N> for $t<N> where N: Copy + Neg<Output = N> + Add<N, Output = N> +
AlgebraApproxEq + RingCommutativeApprox
{ }
/*
* Vector spaces.
*/
impl<N> VectorSpaceApprox<N> for $t<N>
where N: Copy + Neg<Output = N> + Add<N, Output = N> +
AlgebraApproxEq + FieldApprox { }
impl<N> FiniteDimVectorSpaceApprox<N> for $t<N>
where N: Copy + Zero + One + Neg<Output = N> + Add<N, Output = N> +
AlgebraApproxEq + FieldApprox {
#[inline]
fn dimension() -> usize {
$dimension
}
#[inline]
fn canonical_basis<F: FnOnce(&[$t<N>])>(f: F) {
let basis = [
$($t::$compN()),*
];
f(&basis[..])
}
#[inline]
fn component(&self, i: usize) -> N {
self[i]
}
#[inline]
unsafe fn component_unchecked(&self, i: usize) -> N {
self.at_fast(i)
}
}
impl<N: RealApprox> NormedSpaceApprox<N> for $t<N> {
#[inline]
fn norm_squared(&self) -> N {
self.inner_product(self)
}
#[inline]
fn norm(&self) -> N {
self.norm_squared().sqrt()
}
#[inline]
fn normalize(&self) -> Self {
*self / self.norm()
}
#[inline]
fn normalize_mut(&mut self) -> N {
let n = self.norm();
*self /= n;
n
}
#[inline]
fn try_normalize(&self, min_norm: &N) -> Option<Self> {
let n = self.norm();
if n <= *min_norm {
None
}
else {
Some(*self / n)
}
}
#[inline]
fn try_normalize_mut(&mut self, min_norm: &N) -> Option<N> {
let n = self.norm();
if n <= *min_norm {
None
}
else {
*self /= n;
Some(n)
}
}
}
impl<N: RealApprox> InnerSpaceApprox<N> for $t<N> {
#[inline]
fn inner_product(&self, other: &Self) -> N {
fold_add!($(self.$compN * other.$compN ),+)
}
}
/*
*
* Exact algebraic structures.
*
*/
product_space_inherit_structure!($t, GroupAbelian<Additive>);
product_space_inherit_structure!($t, Group<Additive>);
product_space_inherit_structure!($t, Loop<Additive>);
product_space_inherit_structure!($t, Monoid<Additive>);
product_space_inherit_structure!($t, Quasigroup<Additive>);
product_space_inherit_structure!($t, Semigroup<Additive>);
impl<N> VectorSpace<N> for $t<N>
where N: Copy + Neg<Output = N> + Add<N, Output = N> + AlgebraApproxEq + Field
{ }
impl<N> Module<N> for $t<N>
where N: Copy + Neg<Output = N> + Add<N, Output = N> + AlgebraApproxEq + RingCommutative
{ }
}
);
macro_rules! product_space_inherit_structure(
($t: ident, $marker: ident<$operator: ident>) => {
impl<N> $marker<$operator> for $t<N>
where N: Copy + Neg<Output = N> + Add<N, Output = N> + AlgebraApproxEq +
$marker<$operator>
{ }
}
);

View File

@ -349,3 +349,16 @@ macro_rules! component_new(
}
);
);
macro_rules! fold_add(
// base case
($x:expr) => {
$x
};
// `$x` followed by at least one `$y,`
($x:expr, $($y:expr),+) => {
// call min! on the tail `$y`
Add::add($x, fold_add!($($y),+))
}
);

View File

@ -18,9 +18,13 @@ use traits::structure::{Cast, Row, Column, Iterable, IterableMut, Dimension, Ind
use traits::operations::{Absolute, Transpose, Inverse, Outer, EigenQR, Mean};
use traits::geometry::{ToHomogeneous, FromHomogeneous, Origin};
use linalg;
#[cfg(feature="arbitrary")]
use quickcheck::{Arbitrary, Gen};
#[cfg(feature="abstract_algebra")]
use_matrix_group_modules!();
/// Special identity matrix. All its operation are no-ops.
#[repr(C)]

View File

@ -2,6 +2,9 @@
macro_rules! matrix_impl(
($t: ident, $dimension: expr, $vector: ident, $dvector: ident, $($compN: ident),+) => (
matrix_group_approx_impl!($t, $($compN),+);
impl<N> $t<N> {
#[inline]
pub fn new($($compN: N ),+) -> $t<N> {

View File

@ -17,6 +17,7 @@ pub use self::unit::Unit;
pub use self::vectorn::VectorN;
mod common_macros;
mod algebra;
mod dmatrix_macros;
mod dmatrix;
mod vectorn_macros;

View File

@ -12,9 +12,13 @@ use traits::structure::{Cast, Dimension, Indexable, Iterable, IterableMut, Point
NumPoint, FloatPoint, BaseFloat, BaseNum, Bounded, Repeat};
use traits::geometry::{Origin, FromHomogeneous, ToHomogeneous};
use structs::vector::{Vector1, Vector2, Vector3, Vector4, Vector5, Vector6};
#[cfg(feature="arbitrary")]
use quickcheck::{Arbitrary, Gen};
#[cfg(feature="abstract_algebra")]
use_euclidean_space_modules!();
/// Point of dimension 1.
///

View File

@ -3,6 +3,8 @@
macro_rules! point_impl(
($t: ident, $tv: ident | $($compN: ident),+) => (
euclidean_space_impl!($t, $tv);
/*
*
* Origin.

View File

@ -17,7 +17,7 @@ use traits::geometry::{Norm, Rotation, RotationMatrix, Rotate, RotationTo, Trans
use quickcheck::{Arbitrary, Gen};
/// A quaternion. See `UnitQuaternion` for a quaternion that can be used as a rotation.
/// A quaternion. See the `UnitQuaternion` type alias for a quaternion that can be used as a rotation.
#[repr(C)]
#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)]
pub struct Quaternion<N> {

View File

@ -11,9 +11,12 @@ use traits::operations::{Absolute, Inverse, Transpose, ApproxEq};
use structs::vector::{Vector1, Vector2, Vector3};
use structs::point::{Point2, Point3};
use structs::matrix::{Matrix2, Matrix3, Matrix4};
#[cfg(feature="arbitrary")]
use quickcheck::{Arbitrary, Gen};
#[cfg(feature="abstract_algebra")]
use_special_orthogonal_group_modules!();
/// Two dimensional rotation matrix.
#[repr(C)]

View File

@ -2,6 +2,9 @@
macro_rules! rotation_impl(
($t: ident, $submatrix: ident, $vector: ident, $rotvector: ident, $point: ident, $homogeneous: ident) => (
special_orthogonal_group_impl!($t, $point, $vector);
impl<N> $t<N> {
/// This rotation's underlying matrix.
#[inline]

View File

@ -2,6 +2,8 @@ use traits::geometry::Norm;
/// A wrapper that ensures the undelying algebraic entity has a unit norm.
///
/// Use `.as_ref()` or `.unwrap()` to obtain the undelying value by-reference or by-move.
#[repr(C)]
#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)]
pub struct Unit<T> {
@ -47,7 +49,7 @@ impl<T: Norm> Unit<T> {
/// Normalizes this value again. This is useful when repeated computations
/// might cause a drift in the norm because of float inaccuracies.
///
/// Returns the norm beform re-normalization (should be close to `1.0`).
/// Returns the norm before re-normalization (should be close to `1.0`).
#[inline]
pub fn renormalize(&mut self) -> T::NormType {
self.v.normalize_mut()

View File

@ -17,6 +17,9 @@ use structs::point::{Point1, Point2, Point3, Point4, Point5, Point6};
#[cfg(feature="arbitrary")]
use quickcheck::{Arbitrary, Gen};
#[cfg(feature="abstract_algebra")]
use_vector_space_modules!();
/// Vector of dimension 1.
///
@ -29,7 +32,7 @@ pub struct Vector1<N> {
pub x: N
}
vector_impl!(Vector1, Point1, x);
vector_impl!(Vector1, Point1, 1, x);
vectorlike_impl!(Vector1, 1, x);
from_iterator_impl!(Vector1, iterator);
// (specialized); basis_impl!(Vector1, 1);
@ -51,7 +54,7 @@ pub struct Vector2<N> {
pub y: N
}
vector_impl!(Vector2, Point2, x, y);
vector_impl!(Vector2, Point2, 2, x, y);
vectorlike_impl!(Vector2, 2, x, y);
from_iterator_impl!(Vector2, iterator, iterator);
// (specialized); basis_impl!(Vector2, 1);
@ -75,7 +78,7 @@ pub struct Vector3<N> {
pub z: N
}
vector_impl!(Vector3, Point3, x, y, z);
vector_impl!(Vector3, Point3, 3, x, y, z);
vectorlike_impl!(Vector3, 3, x, y, z);
from_iterator_impl!(Vector3, iterator, iterator, iterator);
// (specialized); basis_impl!(Vector3, 1);
@ -100,7 +103,7 @@ pub struct Vector4<N> {
pub w: N
}
vector_impl!(Vector4, Point4, x, y, z, w);
vector_impl!(Vector4, Point4, 4, x, y, z, w);
vectorlike_impl!(Vector4, 4, x, y, z, w);
from_iterator_impl!(Vector4, iterator, iterator, iterator, iterator);
basis_impl!(Vector4, 4);
@ -128,7 +131,7 @@ pub struct Vector5<N> {
pub a: N
}
vector_impl!(Vector5, Point5, x, y, z, w, a);
vector_impl!(Vector5, Point5, 5, x, y, z, w, a);
vectorlike_impl!(Vector5, 5, x, y, z, w, a);
from_iterator_impl!(Vector5, iterator, iterator, iterator, iterator, iterator);
basis_impl!(Vector5, 5);
@ -157,7 +160,7 @@ pub struct Vector6<N> {
pub b: N
}
vector_impl!(Vector6, Point6, x, y, z, w, a, b);
vector_impl!(Vector6, Point6, 6, x, y, z, w, a, b);
vectorlike_impl!(Vector6, 6, x, y, z, w, a, b);
from_iterator_impl!(Vector6, iterator, iterator, iterator, iterator, iterator, iterator);

View File

@ -270,7 +270,7 @@ macro_rules! vectorlike_impl(
);
macro_rules! vector_impl(
($t: ident, $tp: ident, $($compN: ident),+) => (
($t: ident, $tp: ident, $dimension: expr, $($compN: ident),+) => (
pointwise_add!($t, $($compN),+);
pointwise_sub!($t, $($compN),+);
pointwise_mul!($t, $($compN),+);
@ -279,7 +279,7 @@ macro_rules! vector_impl(
componentwise_one!($t, $($compN),+);
componentwise_absolute!($t, $($compN),+);
component_basis_element!($t, $($compN),+);
vector_space_impl!($t, $dimension, $($compN),+);
/*
*
@ -289,11 +289,10 @@ macro_rules! vector_impl(
impl<N: BaseNum> Dot<N> for $t<N> {
#[inline]
fn dot(&self, other: &$t<N>) -> N {
add!($(self.$compN * other.$compN ),+)
fold_add!($(self.$compN * other.$compN ),+)
}
}
/*
*
* Norm
@ -597,18 +596,6 @@ macro_rules! basis_impl(
);
macro_rules! add (
// base case
($x:expr) => {
$x
};
// `$x` followed by at least one `$y,`
($x:expr, $($y:expr),+) => {
// call min! on the tail `$y`
Add::add($x, add!($($y),+))
}
);
macro_rules! from_iterator_impl(
($t: ident, $param0: ident) => (

View File

@ -11,7 +11,7 @@ use traits::structure::{Iterable, IterableMut, Indexable, Shape, BaseFloat, Base
#[cfg(feature="arbitrary")]
use quickcheck::{Arbitrary, Gen};
/// A static array of arbitrary dimension.
/// A stack-allocated vector of arbitrary dimension.
#[repr(C)]
#[derive(Eq, PartialEq, Debug)] // FIXME: Hash, RustcEncodable, RustcDecodable
pub struct VectorN<N, D: ArrayLength<N>> {