linalg.h is a single header, public domain, short vector math library for C++

Related tags

Math linalg
Overview

linalg.h

Release is 2.2-beta 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:

  • Lightweight: The library is defined in a single header file which is less than a thousand lines of code.
  • Dependency free: There are no dependencies beyond a compliant C++11 compiler and a small subset of the standard library.
  • Standards compliant: Almost all operations are free of undefined behavior and can be evaluated in a constexpr context.
  • Generic: All types and operations are parameterized over scalar type, and can be mixed within expressions. Type promotion rules roughly match the C standard.
  • Consistent: Named functions and overloaded operators perform the same conceptual operation on all data types for which they are supported.
  • Complete: There are very few restrictions on which operations may be applied to which data types.
  • Easy to integrate: The library defines no symbols in the public namespace, and provides a mechanism for defining implicit conversions to external or user-provided data types.

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>:

  • is DefaultConstructible:
    float3 v; // v contains 0,0,0
  • is constructible from M elements of type T:
    float3 v {1,2,3}; // v contains 1,2,3
  • is CopyConstructible and CopyAssignable:
    float3 v {1,2,3}; // v contains 1,2,3
    float3 u {v};     // u contains 1,2,3
    float3 w;         // w contains 0,0,0 
    w = u;            // w contains 1,2,3
  • is EqualityComparable and LessThanComparable:
    if(v == y) cout << "v and u contain equal elements in the same positions" << endl;
    if(v < u) cout << "v precedes u lexicographically" << endl;
  • is explicitly constructible from a single element of type T:
    float3 v = float3{4}; // v contains 4,4,4
  • is explicitly constructible from a vec<U,M> of some other type U:
    float3 v {1.1f,2.3f,3.5f}; // v contains 1.1,2.3,3.5
    int3 u = int3{v};          // u contains 1,2,3
  • has fields x,y,z,w:
    float y = point.y;    // y contains second element of point
    pixel.w = 0.5;        // fourth element of pixel set to 0.5
    float s = tc.x;       // s contains first element of tc
  • supports indexing:
    float x = v[0]; // x contains first element of v
    v[2] = 5;       // third element of v set to 5
  • supports unary operators +, -, ! and ~ in component-wise fashion:
    auto v = -float{2,3}; // v is float2{-2,-3}
  • supports binary operators +, -, *, /, %, |, &, ^, << and >> in component-wise fashion:
    auto v = float2{1,1} + float2{2,3}; // v is float2{3,4}
  • supports binary operators with a scalar on the left or the right:
    auto v = 2 * float3{1,2,3}; // v is float3{2,4,6}
    auto u = float3{1,2,3} + 1; // u is float3{2,3,4}
  • supports operators +=, -=, *=, /=, %=, |=, &=, ^=, <<= and >>= with vectors or scalars on the right:
    float2 v {1,2}; v *= 3; // v is float2{3,6}
  • supports operations on mixed element types:
    auto v = float3{1,2,3} + int3{4,5,6}; // v is float3{5,7,9}
  • supports range-based for:
    for(auto elem : float3{1,2,3}) cout << elem << ' '; // prints "1 2 3 "
  • has a flat memory layout:
    float3 v {1,2,3}; 
    float * p = v.data(); // &v[i] == p+i
    p[1] = 4; // v contains 1,4,3

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>:

  • is DefaultConstructible:

    float2x2 m; // m contains columns 0,0; 0,0
  • is constructible from N columns of type vec<T,M>:

    float2x2 m {{1,2},{3,4}}; // m contains columns 1,2; 3,4
  • is constructible from linalg::identity:

    float3x3 m = linalg::identity; // m contains columns 1,0,0; 0,1,0; 0,0,1
  • is CopyConstructible and CopyAssignable:

    float2x2 m {{1,2},{3,4}}; // m contains columns 1,2; 3,4
    float2x2 n {m};           // n contains columns 1,2; 3,4
    float2x2 p;               // p contains columns 0,0; 0,0
    p = n;                    // p contains columns 1,2; 3,4
  • is EqualityComparable and LessThanComparable:

    if(m == n) cout << "m and n contain equal elements in the same positions" << endl;
    if(m < n) cout << "m precedes n lexicographically when compared in column-major order" << endl;
  • is explicitly constructible from a single element of type T:

    float2x2 m {5}; // m contains columns 5,5; 5,5
  • is explicitly constructible from a mat<U,M,N> of some other type U:

    float2x2 m {int2x2{{5,6},{7,8}}}; // m contains columns 5,6; 7,8
  • supports indexing into columns:

    float2x3 m {{1,2},{3,4},{5,6}}; // m contains columns 1,2; 3,4; 5,6
    float2 c = m[0];                // c contains 1,2
    m[1]     = {7,8};               // m contains columns 1,2; 7,8; 5,6
  • supports retrieval (but not assignment) of rows:

    float2x3 m {{1,2},{3,4},{5,6}}; // m contains columns 1,2; 3,4; 5,6
    float3 r = m.row(1);            // r contains 2,4,6
  • supports unary operators +, -, ! and ~ in component-wise fashion:

    float2x2 m {{1,2},{3,4}}; // m contains columns 1,2; 3,4
    float2x2 n = -m;          // n contains columns -1,-2; -3,-4
  • supports binary operators +, -, *, /, %, |, &, ^, << and >> in component-wise fashion:

    float2x2 a {{0,0},{2,2}}; // a contains columns 0,0; 2,2
    float2x2 b {{1,2},{1,2}}; // b contains columns 1,2; 1,2
    float2x2 c = a + b;       // c contains columns 1,2; 3,4
  • supports binary operators with a scalar on the left or the right:

    auto m = 2 * float2x2{{1,2},{3,4}}; // m is float2x2{{2,4},{6,8}}
  • supports operators +=, -=, *=, /=, %=, |=, &=, ^=, <<= and >>= with matrices or scalars on the right:

    float2x2 v {{5,4},{3,2}}; 
    v *= 3; // v is float2x2{{15,12},{9,6}}
  • supports operations on mixed element types:

  • supports range-based for over columns

  • has a flat memory layout

Function listing

Vector algebra

  • cross(vec<T,3> a, vec<T,3> b) -> vec<T,3> is the cross or vector product of vectors a and b

    • cross(vec<T,2> a, vec<T,2> b) -> T is shorthand for cross({a.x,a.y,0}, {b.x,b.y,0}).z
    • cross(T a, vec<T,2> b) -> vec<T,2> is shorthand for cross({0,0,a.z}, {b.x,b.y,0}).xy()
    • cross(vec<T,2> a, T b) -> vec<T,2> is shorthand for cross({a.x,a.y,0}, {0,0,b.z}).xy()
  • dot(vec<T,M> a, vec<T,M> b) -> T is the dot or inner product of vectors a and b

  • length(vec<T,M> a) -> T is the length or magnitude of a vector a

  • length2(vec<T,M> a) -> T is the square of the length or magnitude of vector a

  • normalize(vec<T,M> a) -> vec<T,M> is a unit length vector in the same direction as a (undefined for zero-length vectors)

  • distance(vec<T,M> a, vec<T,M> b) -> T is the Euclidean distance between points a and b

  • distance2(vec<T,M> a, vec<T,M> b) -> T is the square of the Euclidean distance between points a and b

  • angle(vec<T,M> a, vec<T,M> b) -> T is the angle in radians between vectors a and b

  • uangle(vec<T,M> a, vec<T,M> b) -> T is the angle in radians between unit vectors a and b (undefined for non-unit vectors)

  • rot(T a, vec<T,2> v) -> vec<T,2> is the vector v rotated counter-clockwise by the angle a in radians

  • nlerp(vec<T,M> a, vec<T,M> b, T t) -> vec<T,M> is shorthand for normalize(lerp(a,b,t))

  • slerp(vec<T,M> a, vec<T,M> b, T t) -> vec<T,M> is the spherical linear interpolation between unit vectors a and b (undefined for non-unit vectors) by parameter t

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.

  • qmul(vec<T,4> a, vec<T,4> b) -> vec<T,4> is the Hamilton product of quaternions a and b

  • qconj(vec<T,4> q) -> vec<T,4> is the conjugate of quaternion q

  • qinv(vec<T,4> q) -> vec<T,4> is the inverse or reciprocal of quaternion q (undefined for zero-length quaternions)

  • qexp(vec<T,4> q) -> vec<T,4> is the exponential of quaternion q

  • qlog(vec<T,4> q) -> vec<T,4> is the logarithm of quaternion q

  • qpow(vec<T,4> q T p) -> vec<T,4> is the quaternion q raised to the exponent p

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.

  • qangle(vec<T,4> q) is the angle in radians of the rotation expressed by quaternion q

  • qaxis(vec<T,4> q) is the axis of rotation expression by quaternion q (undefined for zero-angle quaternions)

  • qrot(vec<T,4> q, vec<T,3> v) -> vec<T,3> is vector v rotated via rotation quaternion q

  • qmat(vec<T,4> q) is a 3x3 rotation matrix which performs the same operation as rotation quaternion q

  • qxdir(vec<T,4> q) is (efficient) shorthand for qrot(q, {1,0,0})

  • qydir(vec<T,4> q) is (efficient) shorthand for qrot(q, {0,1,0})

  • qzdir(vec<T,4> q) is (efficient) shorthand for qrot(q, {0,0,1})

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.

  • qnlerp(vec<T,4> a, vec<T,4> b, T t) is similar to nlerp(a,b,t), but always chooses the "short path" between the rotations represented by a and b.
  • qslerp(vec<T,4> a, vec<T,4> b, T t) is similar to slerp(a,b,t), but always chooses the "short path" between the rotations represented by a and b.

Matrix algebra

  • mul(mat<T,M,N> a, mat<T,N,P> b) -> mat<T,M,P> is the matrix product of matrices a and b ** mul(mat<T,M,N> a, vec<T,N> b) -> vec<T,M> is the matrix product of matrix a and a column matrix containing the elements of vector b ** mul(a, b, c) is shorthand for mul(mul(a, b), c)

  • outerprod(vec<T,M> a, vec<T,N> b) -> mat<T,M,N> is the outer product of vectors a and b

  • diagonal(mat<T,N,N> a) -> vec<T,N> is a vector containing the elements along the main diagonal of matrix a

  • trace(mat<T,N,N> a) -> T is the sum of the elements along the main diagonal of matrix a

  • transpose(mat<T,M,N> a) -> mat<T,N,M> is the transpose of matrix a

  • adjugate(mat<T,N,N> a) -> mat<T,N,N> is the adjugate or classical adjoint of matrix a (the transpose of its cofactor matrix, or the numerator in the expression of its inverse)

  • comatrix(mat<T,N,N> a) -> mat<T,N,N> is the comatrix or cofactor matrix of matrix a (the transpose of its adjugate matrix)

  • determinant(mat<T,N,N> a) -> T is the determinant of matrix a

  • inverse(mat<T,N,N> a) -> mat<T,N,N> is the multiplicative inverse of the invertible matrix a (undefined for singular inputs)

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

  • any(vec<bool,M> a) -> bool is true if any element of the vector a is true
  • all(vec<bool,M> a) -> bool is true if all elements of the vector a are true
  • sum(vec<T,M> a) -> T is the sum of all elements in the vector a
  • product(vec<T,M> a) -> T returns the product of all elements in the vector a
  • minelem(vec<T,M> a) -> T returns the value of the least element in the vector a
  • maxelem(vec<T,M> a) -> T returns the value of the greatest element in the vector a
  • argmin(vec<T,M> a) -> int returns the zero-based index of the least element in the vector a
  • argmax(vec<T,M> a) -> int returns the zero-based index of the greatest element in the vector a

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:

  • vec<float,M> aliased to floatM, as in: float1, float2, float3, float4
  • vec<double,M> aliased to doubleM, as in: double1, double2, double3, double4
  • vec<int,M> aliased to intM as in: int1, int2, int3, int4
  • vec<unsigned,M> aliased to uintM as in: uint1, uint2, uint3, uint4
  • vec<bool,M> aliased to boolM as in: bool1, bool2, bool3, bool4
  • vec<int16_t,M> aliased to shortM as in: short1, short2, short3, short4
  • vec<uint16_t,M> aliased to ushortM as in: ushort1, ushort2, ushort3, ushort4
  • vec<uint8_t,M> aliased to byteM as in: byte1, byte2, byte3, byte4
  • mat<float,M,N> aliased to floatMxN as in: float1x3, float3x2, float4x4, etc.
  • mat<double,M,N> aliased to doubleMxN as in: double1x3, double3x2, double4x4, etc.
  • mat<int,M,N> aliased to intMxN as in: int1x3, int3x2, int4x4, etc.
  • mat<bool,M,N> aliased to boolMxN as in: boolx3, bool3x2, bool4x4, etc.

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:

call type of a type of b type of c result type result elements
apply(f,a) A T f(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) A B T f(a, b)...
apply(f,a,b) A vec<B,M> vec<T,M> f(a, b[i])...
apply(f,a,b) vec<A,M> B vec<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) A mat<B,M,N> mat<T,M,N> f(a, b[j][i])...
apply(f,a,b) mat<A,M,N> B mat<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) A B C T f(a, b, c)...
apply(f,a,b,c) A B vec<C,M> vec<T,M> f(a, b, c[i])...
apply(f,a,b,c) A vec<B,M> C vec<T,M> f(a, b[i], c)...
apply(f,a,b,c) A vec<B,M> vec<C,M> vec<T,M> f(a, b[i], c[i])...
apply(f,a,b,c) vec<A,M> B C vec<T,M> f(a[i], b, c)...
apply(f,a,b,c) vec<A,M> B vec<C,M> vec<T,M> f(a[i], b, c[i])...
apply(f,a,b,c) vec<A,M> vec<B,M> C vec<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

  • map(a,f) and zip(a,b,f) subsumed by new apply(f,a...)
    • apply(...) supports unary, binary, and ternary operations for vec
    • apply(...) supports unary and binary operations for mat and quat
    • apply(...) can also be invoked exclusively with scalars, and supports arbitrary numbers of arguments
    • apply(...) supports mixed element types
    • Template type alias apply_t<F,A...> provides the return type of apply(f,a...)
  • vec<T,1> and mat<T,M,1> specializations are now provided
  • compare(a,b) provide three-way comparison between compatible types
  • clamp(a,b,c) can be invoked with three distinct (but compatible) types
  • select(a,b,c) provides the a component-wise equivalent to a ? b : c
  • lerp(a,b,t) has been generalized to a component-wise operation where any of a, b, and t can be vectors or scalars
  • User can specialize converter<T,U> to enable implicit conversions from U to T, if either type is a vec, mat, or quat
    • identity is implemented using this facility to serve as an in-library example
  • No undefined behavior according to the C++11 standard
  • Almost all operations which do not internally call <cmath> functions are constexpr, except for argmin and argmax
  • No lambdas are used in linalg.h, avoiding potential ODR violations

Deprecations in v2.2

  • operator * has been deprecated between pairs of matrices.
    • Call cmul(...) if the original, component-wise product was intended
    • Call mul(...) if the algebraic matrix product was intended

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

  • linalg.h no longer supports Visual Studio 2013. However, it is known to work on GCC 4.9+, Clang 3.5+ in C++11 mode and Visual Studio 2015+.
  • vec<T,M> and mat<T,M,N> may only be used with a T which is an arithmetic type
    • This requirement will likely be relaxed, but will require specializing some trait type to indicate additional scalar types
Issues
  • glm::lookAt equivalent?

    glm::lookAt equivalent?

    opened by ghost 10
  • Impossible to create row vectors

    Impossible to create row vectors

    mat<T, 1, N> is not defined. transpose(vec<T, 3>()) does not work. I see no way to construct row vectors. But then, how do I multiply something with a row vector specifically?

    opened by LudwikJaniuk 5
  • mul(a,b,c) with visual studio 2017 release candidate

    mul(a,b,c) with visual studio 2017 release candidate

    Only hit one snag with visual studio 2017 RC.
    With existing projects that worked fine with earlier compiler (20150), I now get the compiler error:

    c:\users...\include\linalg.h(301): fatal error C1202: recursive type or function dependency context too complex

    which refers to the following line of code:

        template<class T, int M, int N, class... R> auto mul(const mat<T,M,N> & a, R... r) -> decltype(mul(a, mul(r...))) { return mul(a, mul(r...)); }
    

    This routine is typically used in lines of code that multiply 3 matrices together such as:

        float3x3 Iinv = mul(s_orientation_matrix, tensorinv, transpose(s_orientation_matrix));
    

    As a temporary workaround, i just commented out this implementation of mul(...) and replaced it with a specific one takes 3 float3x3s since that's the only use case i need at the moment.

        inline float3x3 mul(const float3x3 &a, const float3x3 &b, const float3x3 &c) { return mul(mul(a, b), c);  }  // VS2017 release candidate failing to compile the multi-arg mul from linalg.h
    
    

    Sure, that works. However, ideally would rather have a general mul that takes an arbitrary number of matrices of any compatible types. Any ideas? Wait for MS to fix compiler? or is there something that can be reworked within the linalg.h implementation?

    note I'm submitting this issue primarily for awareness, this is not an urgent issue for me.

    thanks

    bug 
    opened by melax 5
  • Future usability wishlist

    Future usability wishlist

    linalg.h was my first formal introduction to linear algebra, and most of my current game projects depend on it. Thank you. I feel that the library can be further improved for n00b-friendliness. Here is my current usability wishlist, for the next release that has breaking changes.

    Change linalg namespace to alg

    It's shorter.

    Global precision qualifier macro

    #define alg::FLOAT float #define alg::FLOAT double #define alg::FLOAT long double

    Similar to GLSL behavior, i.e. GLSL's precision mediump. Precision qualifiers would only affect the following new types.

    GLSL types

    alg::mat2 / alg::mat3 / alg::mat4 alg::vec2 / alg::vec3 / alg::vec4 alg::FLOAT

    These types are set to the precision specified by alg::FLOAT.

    GLM-style matrix multiplication

    4x4 matrix multiplication is one of the most common things graphics programmers do. Having to type linalg::mul for each operation is crazy, and Lobster has gone ahead and adapted linalg's syntax to use * multiplication.

    opened by ghost 4
  • Fix semantics of operator overloads on matrices.

    Fix semantics of operator overloads on matrices.

    Based on consistent feedback from many users, I have come to the conclusion that my original design decision of having all non-comparison operator overloads be defined in terms of element-wise application to values of the same dimensions to be in error.

    Most users expect the expression matrix * matrix to perform matrix multiplication, and matrix * vector to perform matrix multiplication by interpreting the vector as a single column matrix, matching the semantics of GLSL. Currently, linalg.h has the somewhat surprising behavior of treating matrix * matrix as an element-wise multiplication.

    It is my intention to modify the library to match users' expectations by restricting the set of operator overloads on matrices to those operations which are well defined in linear algebra, to more closely match users' expectations. This is a breaking change, and is thus targeted for v3.0.

    enhancement 
    opened by sgorsten 4
  • Initializing a std::array of vecs

    Initializing a std::array of vecs

    I have the following code:

    std::array<linalg::aliases::int2,8> offsets = { {0, -1}, {-1, 0}, {1, 0}, {0, 1}, {-1, -1}, {1, -1}, {-1, 1}, {1, 1} };

    This does not compile (error: too many initializers for ‘std::array<linalg::vec<int, 2>, 8>’). However, this does compile:

    linalg::aliases::int2 offsets[8] = { {0, -1}, {-1, 0}, {1, 0}, {0, 1}, {-1, -1}, {1, -1}, {-1, 1}, {1, 1} };

    As does this:

    std::vector<linalg::aliases::int2> offsets = { {0, -1}, {-1, 0}, {1, 0}, {0, 1}, {-1, -1}, {1, -1}, {-1, 1}, {1, 1} };

    Is it to be expected that initializing a std::array of vecs in this manner does not work, whereas a plain array or std::vector does?

    opened by crotron 3
  • Doesn't build on MSVC 2017

    Doesn't build on MSVC 2017

    1>c:\vs_projs\vulkan repo\example\include\linalg.h(305): error C2988: unrecognizable template declaration/definition 1>c:\vs_projs\vulkan repo\example\include\linalg.h(305): error C2059: syntax error: 'const' 1>c:\vs_projs\vulkan repo\example\include\linalg.h(305): error C2059: syntax error: ')' 1>c:\vs_projs\vulkan repo\example\include\linalg.h(305): error C2143: syntax error: missing ';' before '{' 1>c:\vs_projs\vulkan repo\example\include\linalg.h(305): error C2447: '{': missing function header (old-style formal list?)

    "Fixed" by commenting out the affected line

    invalid 
    opened by mlfarrell 3
  • Undefined behavior on operator[] overload prevents use in constexpr context

    Undefined behavior on operator[] overload prevents use in constexpr context

    Good time of day. For start, my applause to this great library.

    I've done some experiments with the code and I want to share a few considerations. An attempt to create a general form of mat and vec is complicated for compat with constexpr semantic in current library form.

    	constexpr const T & operator[] (int i) const { return (&x)[i]; }
    

    It isn't really constexpr and that is very sad, becourse we cann't use access in general form's constexpr method.

    I intended to offer change declaration

    	T x,y;
    

    to

        union { std::array<T,2> a; struct { T x,y; }; };
    

    with

        T & operator[] (int i) { return a[i]; }
    

    for support general constexpr access. But, for some reason of current implementation of constexp in c++, we can not effectively use initialization through x, y, z, and access through an array.

    I tried to rewrite the entire library to work through an array. The result is here https://github.com/Mirmik/linalg/blob/master/linalg.h. This is a slightly more ugly solution, but in the context of it we can create a comfortable general form mat and vec (sketch here: https://github.com/Mirmik/linalg/blob/master/linalg_ext.h).

    What do you think about it all?

    bug 
    opened by mirmik 3
  • User Code Extensions

    User Code Extensions

    Hi, I'm currently looking into linalg as a replacement for glm, and so far I like linalg a lot.

    However I'd like a little more flexibility; specifically I think it would be useful if linalg directly supported user code extensions to the vec and mat data structures via macros, e.g. in each of the template instantiations there would be a declaration like this:

    template<class T> struct vec<T,2>
    {
    #ifdef LINALG_VEC2_USER
       LINALG_VEC2_USER
    #endif
     // ...
    

    This trivially allows user code to extend these types, for example by adding custom ctors or operator overloads. In my case this would significantly simplify the transition from glm -> linalg by injecting glm behavior:

    • mat * mat operators for matrix-matrix multiplication.
    • mat * vec operators for matrix-vector multiplication.
    • mat3 ctor from the upper 3x3 elements of a mat4 (extract rotation/scale matrix).
    • Per-element scalar ctors for matrix types.
    enhancement 
    opened by john-chapman 3
  • Identity()

    Identity()

    I see that the design of linalg is rather minimalistic and this may be seen as a feature creep. So I don't mind if you just close this issue. That said, a function to generate an identity matrix is often handy, even if it's trivial to write it manually.

    enhancement 
    opened by wojdyr 3
  • Mixed-mode arithmetic

    Mixed-mode arithmetic

    The C standard allows for arithmetic operators to act on different data types, for instance, int + short, float * double, or int + float. In these cases, type promotion rules come into effect to determine the resulting type.

    We currently allow integer promotion on arithmetic operators, for instance, short2() + short2() == int2().

    Should we allow mixed-mode operations, such as short2() + int2(), float3() * double3(), etc? The result of doing so seems fairly well defined, and could make it easier to convert existing scalar algorithms to use linalg vector types.

    If so, should we also permit scalars to participate, such as short2() + int(), float3() * double()? This seems a little bit more problematic. In the expression float() * 0.5, 0.5 is treated by the compiler as a floating point constant, but in the expression float3() * 0.5, 0.5 would be treated as a double and cause the result of the expression to be promoted to double3.

    enhancement wontfix 
    opened by sgorsten 3
  • Suggestion: projection_onto() and rejection_from() functions for vectors

    Suggestion: projection_onto() and rejection_from() functions for vectors

    For example:

    template<class T> struct vec<T,4>
    {
        // ...
        vec<T,4>                    projected_onto(vec<T,4> const& v)   { return (dot(*this,v)/dot(v,v)) * v;}
        vec<T,4>                    rejection_from(vec<T,4> const& v)   { return *this - this->projected_onto(v); }
        // ...
    }
    

    I feel like projection_onto() at least is common enough to warrant inclusion.

    opened by csp256 1
  • `using namespace linalg::ostream_overloads` sometimes does not work.

    `using namespace linalg::ostream_overloads` sometimes does not work.

    using namespace linalg::ostream_overloads sometimes does not work as planed. For example doctest library(https://github.com/onqtam/doctest) does not found operator defined like this.

    It might be a good idea to change it in the documentation for this way:

    #include "linalg.h"
    namespace linalg { using linalg::ostream_overloads::operator <<; }
    

    It works better because c++ adl mechanism lookup function in argument`s namespaces first. So, in other places it can ignore overloaded operator for various reasons.

    (In fact, it's not entirely clear to me why the overloads are generally hidden. )

    Thanks for your work.

    opened by mirmik 1
  • General implementation of `vec<T,M>` and `mat<T,M,N>`

    General implementation of `vec` and `mat`

    Presently, vec<T,M> is only defined for M equal to 2, 3, or 4, and mat<T,M,N> is only defined for M and N equal to 2, 3, or 4. This is mainly to allow for the presence of member variables x, y, z, and w when appropriate, and to allow for construction using the {x,y,z} syntax.

    Should we also provide a general implementation of vec and mat which would handle sizes greater than four? These could simply use a backing array (or perhaps std::array). They would not support element access with .x, .y, etc., but would support access by array index, as well as all other functions and operators.

    enhancement 
    opened by sgorsten 4
Owner
Sterling Orsten
I make math libraries and game engines in my spare time. This profile is unrelated to my work at any current or past employers unless otherwise indicated.
Sterling Orsten
C++ header-only fixed-point math library

fpm A C++ header-only fixed-point math library. "fpm" stands for "fixed-point math". It is designed to serve as a drop-in replacement for floating-poi

Mike Lankamp 296 Jun 28, 2022
a lean linear math library, aimed at graphics programming. Supports vec3, vec4, mat4x4 and quaternions

linmath.h -- A small library for linear math as required for computer graphics linmath.h provides the most used types required for programming compute

datenwolf 684 Jun 12, 2022
📽 Highly optimized 2D|3D math library, also known as OpenGL Mathematics (glm) for `C

Highly optimized 2D|3D math library, also known as OpenGL Mathematics (glm) for `C`. cglm provides lot of utils to help math operations to be fast and quick to write. It is community friendly, feel free to bring any issues, bugs you faced.

Recep Aslantas 1.4k Jul 2, 2022
Simple long integer math library for C++

SLIMCPP Simple long integer math library for C++ SLIMCPP is C++ header-only library that implements long integers that exceed maximum size of native t

null 19 May 19, 2022
C++ Matrix -- High performance and accurate (e.g. edge cases) matrix math library with expression template arithmetic operators

Matrix This is a math and arithmetic matrix library. It has stood many years of performing in mission critical production for financial systems. It ha

Hossein Moein 67 Jun 5, 2022
Math library using hlsl syntax with SSE/NEON support

HLSL++ Small header-only math library for C++ with the same syntax as the hlsl shading language. It supports any SSE (x86/x64 devices like PC, Mac, PS

null 253 Jun 13, 2022
tiny recursive descent expression parser, compiler, and evaluation engine for math expressions

TinyExpr TinyExpr is a very small recursive descent parser and evaluation engine for math expressions. It's handy when you want to add the ability to

Lewis Van Winkle 1.1k Jun 24, 2022
Fast math tool written on asm/c

math_tool fast math tool written on asm/c This project was created for easy use of mathematical / geometric rules and operations. This project contain

portable executable 3 Mar 8, 2022
SIMD Vector Classes for C++

You may be interested in switching to std-simd. Features present in Vc 1.4 and not present in std-simd will eventually turn into Vc 2.0, which then de

null 1.2k Jun 23, 2022
A wrapper for intel SSE/AVX vector instructions

VMath A wrapper for intel SSE/AVX vector instructions This is just a toy thing to figure out what working with intrinsics is like. I tried to keep it

Dennis 7 Apr 24, 2022
The public CGAL repository, see the README below

The Computational Geometry Algorithms Library (CGAL) is a C++ library that aims to provide easy access to efficient and reliable algorithms in computa

The CGAL Project 3.2k Jun 27, 2022
A modern, C++20-native, single-file header-only dense 2D matrix library.

A modern, C++20-native, single-file header-only dense 2D matrix library. Contents Example usage creating matrices basic operations row, col, size, sha

feng wang 45 May 11, 2022
Header only, single file, simple and efficient C++ library to compute the signed distance function to a triangle mesh

TriangleMeshDistance Header only, single file, simple and efficient C++11 library to compute the signed distance function to a triangle mesh. The dist

Interactive Computer Graphics 71 Jun 16, 2022
A C++ header-only library of statistical distribution functions.

StatsLib StatsLib is a templated C++ library of statistical distribution functions, featuring unique compile-time computing capabilities and seamless

Keith O'Hara 377 Jun 19, 2022
RcppFastFloat: Rcpp Bindings for the fastfloat C++ Header-Only Library

Converting ascii text into (floating-point) numeric values is a very common problem. The fast_float header-only C++ library by Daniel Lemire does this very well, and very fast at up to or over to 1 gigabyte per second as described in more detail in a recent arXiv paper.

Dirk Eddelbuettel 18 May 2, 2022
Header only FFT library

dj_fft: Header-only FFT library Details This repository provides a header-only library to compute fourier transforms in 1D, 2D, and 3D. Its goal is to

Jonathan Dupuy 119 Jun 27, 2022
C++ header-only library with methods to efficiently encode/decode Morton codes in/from 2D/3D coordinates

Libmorton v0.2.7 Libmorton is a C++ header-only library with methods to efficiently encode/decode 64, 32 and 16-bit Morton codes and coordinates, in 2

Jeroen Baert 447 Jun 30, 2022
Extremely simple yet powerful header-only C++ plotting library built on the popular matplotlib

matplotlib-cpp Welcome to matplotlib-cpp, possibly the simplest C++ plotting library. It is built to resemble the plotting API used by Matlab and matp

Benno Evers 3.2k Jul 3, 2022
A header-only C++ library for large scale eigenvalue problems

NOTE: Spectra 1.0.0 is released, with a lot of API-breaking changes. Please see the migration guide for a smooth transition to the new version. NOTE:

Yixuan Qiu 566 Jun 23, 2022