Crate geo_nd

Source
Expand description

§Geometry library

This library provides for N-dimensional geometrical objects, particularly Vectors, Matrix, Quaternion operations.

The underlying numeric type that must be supported for these operations is either a type supporting Num - which includes integers - or in most circumstances Float. Particularly the latter is provided by f32 anf f64.

It provides libraries that provide functions that implement vector, matrix and quaternion operations using arrays of values. For quaternions this is [F; 4], in the order i, j, k, r.

It additionally provides traits for Vector, Quaternion, and SqMatrix. These traits provide consistent means of manipulating such items, which are expected to be in some manner a wrapper around a [F;D], but with perhaps greater alignment restrictions. In theory this could be a SIMD type.

The purpose of these traits is to provide simple access to the vector, matrix, and quaternion operations using methods, rather than invoking these operations through explicit function calls. For example, quaternions can be multiplied together with a*b, rather than invoking quat::multiply.

The library mirrors the operation of ‘glm’ in some sense.

The desire for the library is that it does not undergo much development; it provides a stable and simple basis for operations that are common mathematical operations, with no aim for it to grow into a larger linear algebra library.

§Caveats

The functions in the library use const generics, but as const generic evaulations are currently unstable it requires more consts than should be required. For example, to create an identity square matrix the matrix::identity function has the generic signature <V:Num, const D2:usize, const D:usize>. The value of D2 must equal D*D. The function returns [V; D2].

Ideally the function should just take D as a const generic argument and the type would be [V;D*D], but that is unstable (and there are some other issues).

Additionally, the inference of a type for V is sometimes required to be forced, so there may be a small amount of turbofish notation such as identity2::<f32>().

§Trait method policy

The purpose of the traits is to permit simple operation on arrays such as [f32;4], without necessarily requiring conversion of those arrays into a specific type first. For example, it is simple to add [1,2,3] to a Vector type without having to convert that first into a Vector type - but it must also work for any Vector type too. Hence the arguments to methods are generally &[F; D].

However, for methods that return a (e.g.) Vector type (that is not Self), this would force the caller to convert the resultant type back to the required Vector type. The policy is therefore, for such methods, to use a generic on the method T:Deref<Target = [}>; to take an argument of type &T, and to return a T. This usually requires T to also provide From<F;D>. These methods can be described as ‘apply M to type T to produce a modified T’. In general there will then be a method on the type T that operates on ‘&mut T’ and explicitly takes a &M'. These methods also return &mut self` to permit chaining.

For example, a matrix M can transform a vector T by using

    use geo_nd::{FArray, FArray2, SqMatrix, Vector};
    let t : FArray::<f32, 3> = [0.0, 1.0, 2.0].into();
    let m = FArray2::<f32, 3, 9>::identity();
    let mut t = m.transform(&t);
    // or
    t.transformed_by_m(&m);

The downside to this policy is that one cannot apply a matrix to transform an array [F;D]

    use geo_nd::{FArray, FArray2, SqMatrix, Vector};
    let m = FArray2::<f32, 3, 9>::identity();
    let t = m.transform(&[1.0,3.0,5.0]);

§Function operation

The functions for geometry manipulation are provided in the vector, [mat] and quat modules.

§Basic operation

use geo_nd::vector;
let y = [0., 1.];
let x = [1., 0.];
assert_eq!( vector::dot(&x, &y), 0., "Dot product of X and Y axis vectors is zero");
let xy = vector::add(x,&y,2.);
assert_eq!( xy, [1., 2.], "x + 2*y");
assert_eq!( vector::length_sq(&xy), (5.), "|x + 2*y|^2 = 5");
assert_eq!( vector::length(&xy), (5.0f64).sqrt(), "|x + 2*y| = sqrt(5)");

§Provided traits

The library provides traits for types that can be vectors, matrices, and quaternions.

§Vector

Types that provide Vector can be manipulated with negation, addition, subtraction, and can be scaled with multiplication and division by their ‘float’; their components can be accessed through indexing (e.g. a[0]) immutably and mutably.

As non-traditional vector operations they can be piece-wise multiplied and divided also, which can be useful in graphcis applications; they can also be piece-wise added and subtracted from using their ‘float’.

They also support Copy, std::default::Default, std::fmt::Debug, and std::fmt::Display, serde::Serialize, serde::Deserialize.

They provide AsRef for float arrays of length 4 and slices for fast import and export from memory structures.

§Vector3

Vector3 types are 3-element vectors which additionally provide a [Vector3::cross_product] method, which does not exist (in a simply well defined manner) for other vector sizes.

§SqMatrix

Types that provide SqMatrix are square matrices that can be manipulated with negation, addition, subtraction, and multiplicaton, and can be scaled with multiplication and division by their ‘float’; their components can be accessed through indexing (e.g. a[0]) immutably and mutably. The index is a usize, in row-major order (i.e. [0] is row zero column zero, [1] is row 0 column 1, and [nr] is row 1 column 0 for a matrixt that is ‘nr’ by ‘nc’ rows by columns.)

They can also be piece-wise added and subtracted from using their ‘float’.

They also support Copy, std::default::Default, std::fmt::Debug, and std::fmt::Display, serde::Serialize, serde::Deserialize.

They provide AsRef for float arrays of length 4 and slices for fast import and export from memory structures.

§SqMatrix4

Types that provide SqMatrix4 are 4-by-4 matrices. Additional methods are provided for graphics operations, and so the matrices are treated as 3-by-3 transformation matrices with a translation vector and the last element the distance scaling.

They provide SqMatrix and additionally support graphically-useful constructors ‘perspective’ and ‘look_at’, and support translation by vectors.

§Quaternion

Quaternions are a mathematical means for describing a 3 dimensional rotation around the origin.

Types that provide Quaternion can be manipulated with negation, addition, subtraction, multiplicaton, and division, and can be scaled with multiplication and division by their ‘float’.

They also support Copy, std::default::Default, std::fmt::Debug, and std::fmt::Display, serde::Serialize, serde::Deserialize.

They Deref to their float arrays of length 4. The mapping of the arrays is (i, j, k, r).

§Constructors

Types providing the Quaternion trait can be constructed from:

  • a unit quaternion (1.0 + 0i + 0j + 0*k)

  • (r,i,j,k) tuples

  • the conjugate of another quaternion, i.e. (r,-i,-j,-k)

  • a rotation around a [F; 3] axis by an angle (in radians)

  • a rotation around one of the axes applied to another quaternion

  • another quaternion applied to a rotation around one of the axes

  • from a square matrix that describes a pure rotation (no scaling)

  • that describes a rotation of a camera looking down the negative Z axis with the Y axis as up, to one looking along a specified direction with a (perpendicular) up direction

  • the rotation that provides the shortest great circle path for one unit vector to another (the axis of the rotation is the perpendicular to both)

  • the weighted average of a number of quaternions

The trait provides many application methods for quaternions, perhaps the most important being Quaternion::apply3 and Quaternion::apply4, which allow the quaternion to be applied to a 3-element or 4-element vector (the latter being common in graphics, where the fourth element is usually 1 for a point, and 0 for a vector translation).

§Provided types

The library provides types that simply wrap f32 and f64 arrays, providing imlpementations of the traits and hence supporting vectors, matrices and quaternions. This is perhaps the simplest way to use the library.

§Vector types

The FArray type is a wrapper around an N-element array of floats, and it supports the Vector trait.

§SqMatrix types

The FArray2 type is a wrapper around an N-by-N-element array of floats, and it supports the SqMatrix trait.

§Quaternion types

The QArray type is a wrapper around an 4-element array of floats, and it supports the Quaternion trait.

§Examples

§Two dimensions

// Import the traits
use geo_nd::{Vector, SqMatrix};

// Aliases for the types
pub type Point2D = geo_nd::FArray<f64, 2>;
pub type Mat2x2 = geo_nd::FArray2<f64, 2, 4>;

let x : Point2D = [1.0, 0.0].into();
let y : Point2D = [0.0, 1.0].into();

let c = 30.0_f64.to_radians().cos();
let s = 30.0_f64.to_radians().sin();
let rot30 : Mat2x2 = [c, -s, s, c].into();

let rot60 = rot30 * rot30;

// Rotating x anticlockwise by 30 and 60 should turn it into y
let is_it_y = rot60.transform(&rot30.transform(&x));

// Check that the distance between them is tiny
assert!((y-is_it_y).length_sq() < 1.0E-8);

assert!(y.distance(&is_it_y) < 1.0E-8);

let rot90 = rot60 * rot30;
let rot180 = rot90 * rot90;

let xy = x + y;
let is_it_zero = xy + rot180.transform(&xy);
assert!(is_it_zero.length() < 1.0E-8);

§Three dimensions

// Import the traits
use geo_nd::{Quaternion, SqMatrix, Vector};

// Aliases for the types
pub type Point3D = geo_nd::FArray<f64, 3>;
pub type Mat3x3 = geo_nd::FArray2<f64, 3, 9>;
pub type Point4D = geo_nd::FArray<f64, 4>;
pub type Quat = geo_nd::QArray<f64>;

let x : Point3D = [1., 0., 0.].into();
let y : Point3D = [0., 1., 0.].into();
let z : Point3D = [0., 0., 1.].into();

// qx rotates around the X axis by 90 degrees
// [X,0,0] is unchanged
// [0,1,0] maps to [0,0,1]
// [0,0,1] maps to [0,-1,0]
let qx = Quat::default().rotate_x(90.0_f64.to_radians());
assert!(z.distance(&y.apply_q3(&qx)) < 1.0E-8);
assert!(y.distance(&(-z).apply_q3(&qx)) < 1.0E-8);
assert!(x.distance(&(x).apply_q3(&qx)) < 1.0E-8);

// qy rotates around the Y axis by 90 degrees
// [1,0,0] maps to [0,0,-1]
// [0,Y,0] is unchanged
// [0,0,1] maps to [1,0,0]
let qy = Quat::default().rotate_y(90.0_f64.to_radians());
assert!(x.distance(&(z).apply_q3(&qy)) < 1.0E-8);
assert!(z.distance(&(-x).apply_q3(&qy)) < 1.0E-8);
assert!(y.distance(&(y).apply_q3(&qy)) < 1.0E-8);

// qx * qy applies qx to (qy applied to a vector)
// Hence this chains the qx mapping onto the qy mapping
// [1,0,0] -> [0,0,-1] -> [0,1,0]
// [0,1,0] -> [0,1,0] -> [0,0,1]
// [0,0,1] -> [1,0,0] -> [1,0,0]
//
// This is actually a 120 degree rotation around (1,1,1)
// (qy * qx is a 120 degree rotation around (1,-1,1))
let qxy = qx * qy;
assert!(y.distance(&(x).apply_q3(&qxy)) < 1.0E-8);
assert!(z.distance(&(y).apply_q3(&qxy)) < 1.0E-8);
assert!(x.distance(&(z).apply_q3(&qxy)) < 1.0E-8);

let mut m = Mat3x3::default();
qxy.set_rotation3(&mut m);
// qxy will be [0,0,1,  1,0,0, 0,1,0]
// give or take floating point errors
assert!((m.transform(&x) - y).length() < 1.0E-8);
assert!((m.transform(&y) - z).length() < 1.0E-8);
assert!((m.transform(&z) - x).length() < 1.0E-8);

§Trait discussion

The libraries operate on arrays [F;D], and so types that implement the Vector, Quaternion etc traits are expected to be wrappers around such array (but perhaps with greater alignment restrictions).

An array [f32/f64; D] supports:

  • Copy, Clone, Debug, Default, PartialEq, PartialOrd

  • Index and IndexMut

  • IntoIterator for T, &T, &mut T

  • PartialEq of [U;N] and [U] (where T: PartialEq of U)

  • TryFrom of &[T], Vec<T, A>, Box<[T]> with mut where appropriate; some times for &[F; D] too

  • AsRef[T], Borrow[T] (and mut for those three)

  • Serialize, Deserialize

Note: Hash is not supported as f32/f64 do not support it

Hence a Vector or Quaternion should provide:

  • Copy, Clone, Debug, Default, PartialEq

  • Index and IndexMut

  • Deref <Target = [F; D]>, DerefMut

  • AsRef and AsMut of [F; D]

  • From of &[F], [F;D], &[F;D]

  • Into [F;D] (deref provides equivalent of &[F; D])

  • TryFrom of &[F], Vec<F>

  • Serialize, Deserialize

Note: Not PartialOrd, as vectors dont’t have an ordering

Addition/Subtraction/Multiplication/Division

Add/Sub with Rhs of Self, &Self, Deref<Target = [F;D]> are required for utility; we cannot require the last of these, as Rust has no syntax for that.

Mul/Div with Rhs of F, &F are required for utility; previous versions supported Self to do element-wise operations, which has been removed This is in part because it would need to work for Deref<Target = [F;D]>, but f32/f64 do not implement Deref, and they might in the future do so which breaks the F and &F operation.

Not AsRef/Mut of [F], as [F; D] implement that, and Vector gets it through Deref

Note AsRef<[F]> is implemented through Deref to [F; D]

Note AsMut<[F]> is implemented through DerefMut to [F; D]

if required, then it blocks Add<Deref<Target = [F;D]>> !

Modules§

glsl
The glsl module is a place-holder for types that are compatible with GLSL
matrix
Matrix library
quat
Quaternion module
vector
Vector functions module

Structs§

FArray
The FArray is a wrapper around a D sized array of Floats.
FArray2
The FArray2 is a wrapper around a D2 = D^2` sized array of Floats.
FQArrayTrans
A transformation that is a translation . scaling . rotation (i.e. it applies the rotation to an object, then scales it, then translates it)
QArray
The QArray is a wrapper around a D sized array of Floats.

Traits§

Float
The Float trait is required for matrix or vector elements which have a float aspect, such as sqrt.
Geometry2D
This is an experimental trait - it bundles together a Vec2 and a Mat2.
Geometry3D
The Geometry3D trait supplies a framework for implementing 3D vector and matrix operations, and should also include the quaternion type.
Num
The Num trait is required for matrix or vector elements; it is not a float, and so some of the matrix and vector operations can operate on integer types such as i32, i64 and isize
Quaternion
The Quaternion trait describes a 4-dimensional vector of Float type.
SqMatrix
The SqMatrix trait describes an N-dimensional square matrix of Float type that operates on a Vector.
SqMatrix2
The SqMatrix2 trait describes a 2-dimensional vector of Float
SqMatrix3
The SqMatrix3 trait describes a 2-dimensional vector of Float
SqMatrix4
The SqMatrix4 trait describes a 2-dimensional vector of Float
Transform
The Transform trait describes a translation, rotation and scaling for 3D, represented eventually as a Mat4
Vector
The Vector trait describes an N-dimensional vector of Float type.
Vector2
The Vector2 trait describes a 3-dimensional vector of Float
Vector3
The Vector3 trait describes a 3-dimensional vector of Float
Vector4
The Vector4 trait describes a 3-dimensional vector of Float
Vector3D
This is probably a temporary trait used until SIMD supports Geometry3D and Geometry2D