sgorsten / linalg Goto Github PK
View Code? Open in Web Editor NEWlinalg.h is a single header, public domain, short vector math library for C++
License: The Unlicense
linalg.h is a single header, public domain, short vector math library for C++
License: The Unlicense
With rotation matrix {0,-1,0,0,-1,0,0,0,-1}
, the function rotation_quat
returns {0.7071068,0.7071068,0,0}
instead of {-0.7071068,0.7071068,0,0}
.
linalg.h
is textually short, but very general. Most operations have been generalized over:
Additionally, names have been deliberately chosen to be terse, allowing for the concise expression of complex mathematical formula. This means plenty of potential name collisions with the std:: namespace and the potential for unintended argument-dependent lookup.
We're going to need a good set of unit tests to make sure that all templates are being instantiated across the range of reasonable types, sizes, and argument combinations. We don't need to get super exhaustive, given that most functions are one-liners and many functions share common structure, but we should make sure we're equipped to catch regressions as the library evolves.
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).Finish all remaining TODO lines in the README.md
file for the v3
branch.
Hi,
I am not sure if I am missing something so please correct me if there already is an easier way to do this. Here's what I want to do:
mat4
transformation matrix to a mat3
rotation matrix (essentially dropping the translation part)mat4
with the translation part set to {0, 0, 0}
This is a pretty common operation for a bunch of 3D rendering related tasks. I have been doing that manually so far (the types are just linalg typedefs):
auto skyboxViewRot = Mat3f { transform.x.xyz(), transform.y.xyz(), transform.z.xyz() };
auto skyboxView = Mat4f { Vec4f { skyboxViewRot.x, 0.0f },
Vec4f { skyboxViewRot.y, 0.0f },
Vec4f { skyboxViewRot.z, 0.0f },
Vec4f { 0.0f, 0.0f, 0.0f, 1.0f } };
I am pretty sure in glm
the mat3
and mat4
constructors allow you to do this as one liners. Is there a similar way to achieve this in linalg? Otherwise I think it might be useful to add utilities for these operations (not sure if this is beyond the scope of the library but these seem to be very common operations to me).
Thanks!
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?
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.
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?
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
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.
n00b question: I use the factory functions a lot. My math skills aren't great. How would I go about look-at functionality in different coordinate spaces (OpenGL, Vulkan, Directx, etc.)?
EDIT: Apparently, the function in question does a transform from world space "into the specific eye space that the projective matrix functions...are designed to expect". Hmm. A more generalized function might be better.
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.
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.
It's shorter.
#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.
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
.
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.
I just tried to call argmax()
with a matrix as an argument and realized that the reductions here work only for vectors.
So here is a feature request to consider: functions such as sum()
and argmax()
for 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.
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
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?
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.
I want to ask a question that does not give me peace for a long time, and to which I can’t find a definite answer.
Why in general and in this library in particular the column order is used?
What is the advantage of column order over row order and otherwise?
Why did you choose the column order for this library?
#include "linalg.h"
#include <iostream>
using namespace linalg::aliases;
using namespace linalg::ostream_overloads;
using namespace std;
int main() {
float3x3 A = {
{1.f, 0.f, 3.f},
{0.f, 1.f, 3.f},
{0.f, 0.f, 1.f},
};
float3x3 B = {
{2.f, 0.f, 0.f},
{0.f, 2.f, 0.f},
{0.f, 0.f, 1.f},
};
cout << mul(A, B) << endl;
/*
* the result output by this program is {{2,0,6},{0,2,6},{0,0,1}}, while by Octave:
*
* >> a = [ [1 0 3]; [0 1 3]; [0 0 1] ]
* a =
*
* 1 0 3
* 0 1 3
* 0 0 1
*
* >> b = [[2 0 0]; [0 2 0]; [0 0 1]]
* b =
*
* 2 0 0
* 0 2 0
* 0 0 1
*
* >> a * b
* ans =
*
* 2 0 3
* 0 2 3
* 0 0 1
* */
return 0;
}
while the document states mul(mat<T,M,N> a, mat<T,N,P> b) -> mat<T,M,P>
, the library seems to calculate mul(b, a)
.
Is something I made wrong? or there is a bug in the library?
At first glance it is only about constructors.
Would it be OK to change vec(T x, T y) : x(x), y(y)
etc to, say, vec(T x_, T y_) : x(x_), y(y_)
?
-Wshadow needs to be enabled explicitely so I guess few people use it. What's your opinion? I could provide PR if it'd be helpful.
../linalg.h: In constructor ‘constexpr linalg::vec<T, 2>::vec(T, T)’:
../linalg.h:69:69: warning: declaration of ‘y’ shadows a member of ‘linalg::vec<T, 2>’ [-Wshadow]
constexpr vec(T x, T y) : x(x), y(y) {}
^
../linalg.h:67:39: note: shadowed declaration is here
T x,y;
^
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
.
I found this problem trying to compile the tests with clang (Apple LLVM version 8.0.0 / clang-800.0.42.1) on my OS 10.11.6 workstation. The compilation with GNU g++ works ok on the same machine.
I could fix it on my machine by including as commented here:
http://stackoverflow.com/questions/30084577/ambiguous-call-to-abs
Thanks for the library.
Hi and thanks for the lib! It's by far one of the least bloated linear algebra libs we have found, and we have had great success with it so far!
Well, except for one aspect. We have had quite a few bugs where people implicitly assumed that the * operator was matrix multiply (and not element-wise multiply). We have become more used to using mul()
now, but it's still a problem that pops up every now and then.
So the feature request we have is to be able to disable operator * for matrices (but not for vectors) so that we don't accidentally do element-wise multiplication instead of matrix multiplication.
Have a nice day!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.