I've just finished putting together what should be a relatively complete but untested set of math code for game engines and GUIs: https://git.sr.ht/~icedquinn/icedgmath
Proper testing will come somewhen. I'm sure there are some wrinkles.
Projective geometric algebra is a real bear to figure out but I think I managed to scrounge together enough references to figure out some of the basic Motor and Rotor concepts. It's very similar to quaternion based rotations but there is a whole conceptual framework behind it that explains why all of the parts work like they do.
(Someone sent me this https://marctenbosch.com/quaternions/ which is where this part started.)
The non-PGA stuff is staying in the lib.
I love this and I'm interested in contributing!
I have a branch here: https://git.sr.ht/~rotu/icedgmath I'm not familiar with this site so I don't know how to submit a pull request, but here's my first commit: https://git.sr.ht/~rotu/icedgmath/tree/patch-1
BTW, I'm not sure I'm comfortable with defining pointwise operations on vectors. To my mind, abs(vec(1,-1,-1)) should be sqrt(3), not vec(1,1,1). A vector is not a collection - it's a geometric object.
submit a pull request
sourcehut doesn't have those. you either export the patch and mail it or do what you did and ask a maintainer to pull it manually.
i should go set up the mailing list.
abs
i think there's actually a better way to do that one.
Exiled the game engine specific broadcast functions like abs to a separate vectors/game.nim for now. Also refactored those on to the broadcast template, and enforced the naming scheme. It should have been absed, maxed, etc, since the lib generally uses past tense to denote copying functions and present tense for mutating ones.
i'm not sure much else needs to be moved to /game.nim type files. most of the interface is mathematically correct. i don't see a problem putting the very common things that are not correct yet are very common in practical use in a side file like this because it makes the symbols optional without too much overhead.
There's another issue of mathematical correctness. The * and / operators operate elementwise, which does not seem a geometrically meaningful operation. These do have meaningful definitions in geometric algebra, but...
There's a fundamental tension between linear algebra and geometric algebra that needs to be addressed:
element-wise vector operations
i'll replace those with the hadamard operator.
The Hadamard product is not geometrically meaningful! While it's great for data processing, it doesn't really make sense to compute the Hadamard product of two spatial vectors (i.e. two relative positions in space)
The geometric product makes sense as a product, and so do the inner product and the outer product. You should think of the inner product as a generalized cosine of the angle between two vectors - it measures how much they are pointing in the same direction. And the outer product as a generalized sine of the angle between two vectors - it measures how much they are pointing in directions irrelevant to each other. (both scaled up by the length of the vectors, which is how strongly they are pointing in any direction whatsoever)
@dlesnoff Numpy is geared toward arbitrary computation, not geometry in particular. Elementwise operations can be very useful, but not if what you want is to compute rotations, reflections, etc. in space.
Elementwise abs is very dependent on the basis - if I draw a line on the ground and ask two people to compute its abs, the value is going to depend how they drew their x/y axes. But you want something that describes the line per se, not its representation.
The 1-norm isn't physical (unless you're PacMan and can't travel diagonally!) It's also why we define abs to be the 2-norm on the complex plane (instead of abs(a)+abs(b)*i)
The Hadamard product is not geometrically meaningful!
As of d0975 there is no longer a * defined for vectors. hadamard(v1, v2) does what that operator used to do and a version of that for matrices already existed.
The definition of a Rotor is identical to a geometric product (scalar+bivector) so returning one as something like geometric(v1, v2) is not inherently wrong but it does seem like it could be misleading because it would mean the Rotor becomes the general object used to store any geometric products.
The definition of a Rotor is identical to a geometric product (scalar+bivector) so returning one as something like geometric(v1, v2) is not inherently wrong but it does seem like it could be misleading because it would mean the Rotor becomes the general object used to store any geometric products.
That's not exactly right. The product of two vectors is a rotor, and the product of two rotors is a rotor, but the product of a vector with a rotor is NOT a rotor. In general, a geometric product results in a multivector. In 2D, a multivector has a scalar, vector, and bivector part. In 3D it also contains a trivector part:
2D: (a) + (xe0 + ye1) + (be01)
a is the real part, x,y are the vector part, b is the imaginary part. You should think of the units like "e0 is 1 meter x-ward, e1 is one meter y-ward" and "e01 is one square meter in the xy-plane.
3D: (a) + (xe0 + ye1 + ze1) + (be12, ce02, de01) + (fe012)
a is the real part, x,y,z are the vector part, b,c,d are the imaginary part (note 3 coordinates because it's a quaternion!).
You should think of the new units like "e2 is one meter z-ward", "e02 is one square meter in the xz-plane, e12 is one square in the yz-plane", "e012 is one cubic meter in the xyz-space".
The fundamental problem here, to my mind, is "what is multiplication supposed to mean?" Do you want your library to feel like linear algebra or geometric algebra?
That makes sense, and I don’t think you really want a rotor class at all - just a “rotation” class that has methods to e.g. rotate a vector or return the axis of rotation or return the angle of rotation.
It’s very much a design decision what to put at the type level and what to put at the value level. For instance, you might want rotation, translation, dilation to be separate classes, or you may want to have a single class that just represents “affine transformation”. Similarly, you might constrain rotations to be a quaternion with unit norm, or you might allow them to have any norm (so it’s a simultaneous rotation and dilation).
I’d be tempted to put as much as possible at the value level and then specialize as necessary.
rotors some form of Rotor3 may stick around for optimization purposes.
i just pushed multivectors in R^2 and R^3 and changed the geometric products to produce those. however i noticed the tutorial code for rotors is abridging the math somewhat.
they are supposed to carry the vectors along with them but because they are fixed to the basis planes they are constant so they are not stored and are clipped out of the math. the 'correct' multivector format (according to euclideanspace.com) has to carry the vectors and those also participate in reversal operations. addition/subtraction is elementwise, so adding two multivectors does also alter the planar spaces.
for an interactive simulation (games) we just need to spin and possibly move something so a handful of these assertions are useful (ex. how the trivector is always zero when the basis planes are used to do a double reflection to rotate.)
i haven't done the inner/outer products on the multivector type yet. those are needed to change grades (and implement the Rotor without the special rotor module.)
It's very similar to quaternion based rotations but there is a whole conceptual framework behind it that explains why all of the parts work t should be technically usable