Home

Awesome

linalg.h

Release is 2.2 License is Unlicense Travis CI build status Appveyor build status

linalg.h is a single header, public domain, short vector math library for C++. It is inspired by the syntax of popular shading and compute languages and is intended to serve as a lightweight alternative to projects such as GLM, Boost.QVM or Eigen in domains such as computer graphics, computational geometry, and physical simulation. It allows you to easily write programs like the following:

#include <linalg.h>
using namespace linalg::aliases;

// Compute the coefficients of the equation of a plane containing points a, b, and c
float4 compute_plane(float3 a, float3 b, float3 c)
{
    float3 n = cross(b-a, c-a);
    return {n, -dot(n,a)};
}

linalg.h aims to be:

The documentation for v2.2 is still in progress.

Data structures

Vectors

linalg::vec<T,M> defines a fixed-length vector containing exactly M elements of type T. Convenience aliases such as float3, float4, or int2 are provided in the linalg::aliases namespace. This data structure can be used to store a wide variety of types of data, including geometric vectors, points, homogeneous coordinates, plane equations, colors, texture coordinates, or any other situation where you need to manipulate a small sequence of numbers. As such, vec<T,M> is supported by a set of algebraic and component-wise functions, as well as a set of standard reductions.

vec<T,M>:

Matrices

linalg::mat<T,M,N> defines a fixed-size matrix containing exactly M rows and N columns of type T, in column-major order. Convenience aliases such as float4x4 or double3x3 are provided in the linalg::aliases namespace. This data structure is supported by a set of algebraic functions and component-wise functions, as well as a set of standard reductions.

mat<T,M,N>:

Function listing

Vector algebra

Quaternion algebra

A small set of functions provides support for quaternion math, using vec<T,4> values to represent quaternions of the form xi + yj + zk + w.

A second set of functions provides support for using unit-length quaternions to represent 3D spatial rotations. Their results are undefined for quaternions which are not of unit-length.

It is possible to use the nlerp and slerp functions to interpolate rotation quaternions as though they were simply four-dimensional vectors. However, the rotation quaternions form a double cover over spatial rotations in three dimensions. This means that there are two distinct rotation quaternions representing each spatial rotation. Naively interpolating between two spatial rotations using quaternions could follow the "short path" or the "long path" between these rotations, depending on which specific quaternions are being interpolated.

Matrix algebra

Component-wise operations

The unary functions abs, floor, ceil, exp, log, log10, sqrt, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, round accept a vector-valued argument and produce a vector-valued result by passing individual elements to the function of the same name in the std:: namespace, as defined by <cmath> or <cstdlib>.

float4 a {1,-4,9,-16}; // a contains 1,-4,9,-16
float4 b = abs(a);     // b contains 1,4,9,16
float4 c = sqrt(b);    // c contains 1,2,3,4

The binary functions fmod, pow, atan2, and copysign function similarly, except that either argument can be a vector or a scalar.

float2 a {5,4}, b {2,3};
float2 c = pow(a, 2);    // c contains 25,16
float2 d = pow(2, b);    // d contains 4,8
float2 e = pow(a, b);    // e contains 25,64

The binary functions equal, nequal, less, greater, lequal, and gequal apply operators ==, !=, <, >, <= and >= respectively in a component-wise fashion, returning a vec<bool,M>. As before, either argument can be a vector or a scalar.

int2 a {2,5}, b {3,4};
bool2 c = less(a,3);    // c contains true, false
bool2 d = equal(4,b);   // d contains false, true
bool2 e = greater(a,b); // e contains false, true

min(a,b) -> vec<T,M> performs the component-wise selection of lesser elements, as by a[i] < b[i] ? a[i] : b[i]. Either argument can be a vector or a scalar.

max(a,b) -> vec<T,M> performs the component-wise selection of greater elements, as by a[i] > b[i] ? a[i] : b[i]. Either argument can be a vector or a scalar.

clamp(x,l,h) -> vec<T,M> performs the component-wise clamping of elements between a low and high boundary, as by min(max(x,l),h). Any argument can be a vector or a scalar.

select(p,a,b) -> vec<T,M> performs a component-wise ternary operator, as by p[i] ? a[i] : b[i]. Any argument can be a vector or a scalar.

lerp(a,b,t) -> vec<T,M> performs a component-wise linear interpolation, as by a[i]*(1-t[i]) + b[i]*t[i]. Any argument can be a vector or a scalar.

Reductions

Comparisons

compare(a,b) is conceptually equivalent to operator <=> from C++20. It compares two values of equivalent shape and returns a value which supports all six standard comparisons against 0. It provides the same ordering guarantees as the underlying scalar type. That is, a vec<int,M> provides a strong ordering, where a vec<float,M> provides a partial odering.

Optional features

Type aliases

By default, linalg.h does not define any symbols in the global namespace, and a three-element vector of single-precision floating point values must be spelled linalg::vec<float,3>. In various libraries and shading languages, such a type might be spelled float3, vec3, vec3f, point3f, simd_float3, or any one of a hundred other possibilities. linalg.h provides a collection of useful aliases in the linalg::aliases namespace. If the names specified in this namespace are suitable for a user's purposes, they can quickly be brought into scope as follows:

#include <linalg.h>
using namespace linalg::aliases;

float3 a_vector;
float4x4 a_matrix;

Note that this only brings the type aliases into global scope. The core types and all functions and operator overloads defined by the library remain in namespace linalg.

If the spellings in namespace linalg::aliases conflict with other types that have been defined in the global namespace or in other namespaces of interest, the user can choose to omit the using namespace directive and instead define their own aliases as desired.

#include <linalg.h>
using v3f = linalg::vec<float,3>;
using m44f = linalg::mat<float,4,4>;

v3f a_vector;
m44f a_matrix;

It is, of course, always possible to use the core linalg.h types directly if operating in an environment where no additional symbols should be defined.

#include <linalg.h>

linalg::vec<float,3> a_vector;
linalg::mat<float,4,4> a_matrix;

The set of type aliases defined in namespace linalg::aliases is as follows:

All combinations of up to four elements, rows, or columns are provided.

ostream overloads

By default, linalg.h does not provide operators for interaction with standard library streams. This is to permit maximum flexibility for users who wish to define their own formatting (with or without delimiters, row versus column major matrices, human-readable precision or round-trip exact). However, as it is often useful to simply be able to show something when writing small programs, we provide some default stream operator overloads which can be brought into scope with:

#include "linalg.h"
using namespace linalg::ostream_overloads;

The provided behavior is to output a string using the currently specified stream properties (width, precision, padding, etc) which matches the braced-initialization syntax that could be used to construct that same value, without any extra whitespace.

int3 v {1, 2, 3};
int2x2 m {{4, 5}, {6, 7}};
std::cout << v << std::endl; // Prints {1,2,3}
std::wcout << m << std::endl; // Prints {{4,5},{6,7}}

User-defined conversions

A mechanism exists to define automatic conversions between linalg and user-provided types. As an example, this mechanism has already been used to defined bidirectional conversions between linalg::vec<T,M> and std::array<T,M>.

TODO: Explain converter<T,U>

Higher order functions

linalg::fold(f, a, b)

fold(f, a, b) is a higher order function which accepts a function of the form A,B => A and repeatedly invokes a = f(a, element_of_b) until all elements have been consumed, before returning a. It is approximately equal to a left fold with an initial value. When b is a vec<T,M>, elements are folded from least to greatest index. When b is a mat<T,M,N>, elements are folded in column-major order.

See also: Reductions

linalg::apply(f, a...)

apply(f, a...) is a higher order function which accepts a function of the form A... => T and applies it to component-wise sets of elements from data structures of compatible shape and dimensions. It is approximately equal to a convolution followed by a map. The shape of the result (that is, whether it is a scalar, vector, or matrix, and the dimensions thereof) is determined by the arguments. If more than one argument is a non-scalar, the shape of those arguments must agree. Scalars can be freely intermixed with non-scalars, and element types can also be freely mixed. The element type of the returned value is determined by the return type of the provided mapping function f. The supported call signatures are enumerated in the following table:

calltype of atype of btype of cresult typeresult elements
apply(f,a)ATf(a)
apply(f,a)vec<A,M>vec<T,M>f(a[i])...
apply(f,a)mat<A,M,N>mat<T,M,N>f(a[j][i])...
apply(f,a,b)ABTf(a, b)...
apply(f,a,b)Avec<B,M>vec<T,M>f(a, b[i])...
apply(f,a,b)vec<A,M>Bvec<T,M>f(a[i], b)...
apply(f,a,b)vec<A,M>vec<B,M>vec<T,M>f(a[i], b[i])...
apply(f,a,b)Amat<B,M,N>mat<T,M,N>f(a, b[j][i])...
apply(f,a,b)mat<A,M,N>Bmat<T,M,N>f(a[j][i], b)...
apply(f,a,b)mat<A,M,N>mat<B,M,N>mat<T,M,N>f(a[j][i], b[j][i])...
apply(f,a,b,c)ABCTf(a, b, c)...
apply(f,a,b,c)ABvec<C,M>vec<T,M>f(a, b, c[i])...
apply(f,a,b,c)Avec<B,M>Cvec<T,M>f(a, b[i], c)...
apply(f,a,b,c)Avec<B,M>vec<C,M>vec<T,M>f(a, b[i], c[i])...
apply(f,a,b,c)vec<A,M>BCvec<T,M>f(a[i], b, c)...
apply(f,a,b,c)vec<A,M>Bvec<C,M>vec<T,M>f(a[i], b, c[i])...
apply(f,a,b,c)vec<A,M>vec<B,M>Cvec<T,M>f(a[i], b[i], c)...
apply(f,a,b,c)vec<A,M>vec<B,M>vec<C,M>vec<T,M>f(a[i], b[i], c[i])...

TODO: Explain apply_t<F, A...> and SFINAE helpers.

See also: Component-wise operations

Changes from v2.1

Improvements in v2.2

Deprecations in v2.2

You can #define LINALG_FORWARD_COMPATIBLE before including linalg.h to remove all deprecated features.

Breaking changes in v2.2-beta

It is intended that compatibility will be restored before officially tagging v2.2