#geometric-algebra #vector-math #geometric #algebra #projective #2d-vector #pga

simply_2dpga

A 2d vector math library using 2d projective geometric algebra! Designed to be programmer-friendly, specifically because I wanted to use PGA in game development. Development is ongoing.

2 releases

0.1.2 Dec 16, 2023
0.1.1 Dec 15, 2023

#158 in Math

Download history 6/week @ 2023-12-14 5/week @ 2024-02-15 17/week @ 2024-02-22 6/week @ 2024-02-29 8/week @ 2024-03-07 54/week @ 2024-03-14

75 downloads per month

MIT license

69KB
1.5K SLoC

Simply: 2D PGA!

Simply, a 2d pga library.

If you're not familiar with projective geometric algebra, see:

Otherwise, this library is exactly what it sounds like.

This is my first published Rust crate, so please be gentle. If something isn't quite right with the math or with the Rust code, let me know!

Usage

Usage of this library should be fairly straightforward, assuming a basic understanding of projective geometric algebra.

Multivectors

A Multivector<N> is the most fundamental unit in this library. If you're coming from traditional vector math, think of it sort of like a transformation matrix.

The Multivector<N> struct contains:

  • A Scalar N
  • A Vector<N>
  • A Bivector<N>
  • A Trivector<N>

Note the generic 'N'. N must implement Float. This is to keep this library generalizable.

declaring a multivector

A multivector with all coefficients set to 0 can be initialized in one line:

let my_multivector: Multivector<f32> = Multivector::zero();

Sometimes you want to initialize a multivector with only a single grade defined. That can be done as such:

// A multivector with only grade-0 coefficients:
let my_scalar = Multivector::from_scalar(scalar_number_here);
// A multivector with only grade-1 coefficients:
let my_vector = Multivector::from_vector(vector_defn_here);
// A multivector with only grade-2 coefficients:
let my_bivector = Multivector::from_bivector(bivector_defn_here);
// A multivector with only grade-3 coefficients:
let my_trivector = Multivector::from_trivector(trivector_defn_here);

Alternately, the Vector, Bivector, and Trivector structs have a to_multivector() function:

let my_vector = Vector{ e0: 1.0, e1: 1.0, e2: 1.0}.to_multivector();
let my_bivector = Bivector{ e01: 1.0, e20: 1.0, e12: 1.0}.to_multivector();
let my_trivctor = Trivector{ e012: 1.0 }.to_multivector();

Vectors

A vector is a a geometric object of grade 1 in projective geometric algebra. It is constructed from basis vectors e0, e1, and e2, where e02 == 0.

In 2d PGA, a vector represents a line, with direction and magnitude.

For a line ax+by+c = 0, the corresponding vector will be ae1+be2+ce0. In code, this will look like:

let example_line: Vector<f32> = Vector {
            e0: number_c,
            e1: number_a,
            e2: number_b
        };

Bivectors

A bivector is a geometric object of grade 2. In 2d projective geometric algebra, it is the point where intersecting lines meet. That is to say: a bivector represents a 2d point.

For a point (x, y), the corresponding bivector will be xe20+ye01+1e12. Note that the basis bivector e12 is 1. When constructing a 2d point, the e12 bivector should be normalized such that this coefficient is 1. In code, this will look like:

let example_bivector: Bivector<f32> = Bivector { 
            e01: x, 
            e20: y, 
            e12: 1.0 
        };

e12 is a projective coordinate. When it is '0', you will be representing a point at infinity, which is sometimes simply referred to as a 'direction'.

The struct extras::point2d::Point2d exists as a wrapper around bivectors to make working with 2d eucludian points a bit more intuitive.

Trivectors

The k-vector of grade 3 in 2d projective geometric algebra is called the 'pseudoscalar'. It has a single coefficient. Wrapping it was likely unnecessary, but I did so anyways to remain consistent.

Here is an example of a trivector:

let example_trivector: Trivector<f32> = Trivector { e012: 2.0 }

The K-Vector struct

Admittedly, this part of the library ended up being more or less useless. It was meant as a convenience feature, but most geometric objects are converted to multivectors during operations anyways. It has been kept regardless incase anyone finds it interesting or useful.

KVector<N> is an enum which encapsulates every valid grade of k-vector in 2d PGA.

Operators

I have tried to include all operators fundamental to 2d PGA.

  • Geometric product

    The geometric product is the most fundamental operation in geometric algebra, so naturally it exists here. Geometric product between any two k-vectors will produce a multivector as a result. The geometric product between two multivectors likewise can be computed.

    Note that most of the k-vector geoemtric products simply convert to a multivector first, then take the geometric product between two multivectors. Hardcoding certain geometric products that happen commonly (such as between two vectors) could be a source of future optimizations.

    let mv1 = Multivector { ... };
    let mv2 = Multivector { ... };
    let product1 = mv1.geo(&mv2);
    let product2 = mv2.geo(&mv1);
    assert_ne!(product1, product2); // Should pass!
    
  • Grade projection

    A grade projection on a multivector simply spits out the components of the multivector correlating to a particular grade. For example, performing a grade projection of grade '1' on a multivector will give you its vector components.

    This has been implemented as member function on the multivector struct, but that turned out to be unnecessary as you can simply extract the component you want from the multivector manually. The function has been kept anyways.

    let example_multivector = Multivector { ... };
    
    // This syntax exists.
    let vector_component = example_multivector.grade_proj(1)
        .to_vector().unwrap();
    
    // But it's really easier to just do this.
    let vector_component = example_multivector.vector;
    
  • Wedge product

    Also sometimes called the 'meet', as it can be used to build the meet of two lines (which would be a point). The wedge product of two k-vectors 'A' and 'B': A^B=C where 'A' has a grade 'i' and 'B' has a grade 'j' will produce 'C' with grade 'i+j'. It is simply the geometric product followed by a grade projection.

    Hardcoding particular wedge products that are commonly used could be a potential source of future optimizations, if deemed necessary.

    let v1 = Vector { ... };
    let v2 = Vector { ... };
    
    let resulting_bivector = v1.wedge(&v2);
    
  • Regressive product

    The 'join' of two points will be a line. This relationship is neatly captured by the regressive product. The regressive product has been manaully implemented for two bivectors.

    Note: I have not yet validated the correctness of the regressive product!

    let bv1 = Bivector { ... };
    let bv2 = Bivector { ... };
    
    let joining_line = bv1.regressive(&bv2);
    
  • Inner product, left/right contractions

    The inner product is, of course, the dot product! The left and right contractions exist too, just in case someone needs them.

    The inner product is the geometric product of two k-vectors, grade projected to take the |i-j| component. Practically speaking, this is almost always the scalar component.

    This operator might need to be optimized in the future for particular use cases.

    let v1 = Vector { ... };
    let v2 = Vector { ... };
    
    let dot_product = v1.inner(&v2);
    
  • Reverse

    Often notated with the 'dagger' symbol, the reverse operator in theory reverses the ordering of the basis components of each element of a multivector. In practice, this flips the sign every 2 grades.

    let example_multivector = Multivector { ... };
    let example_reversed = example_multivector.reverse();
    
  • Grade involution

    Unlike reverse, grade involution flips the sign every grade. Likewise, it can be performed with a simple call:

    let example_multivector = Multivector { ... };
    let example_involuted = example_multivector.grade_involution();
    
  • Magnitude Squared

    The square magnitude is implemented for vectors and bivectors:

    let v1: Vector<f32> = Vector { ... };
    let v1_norm: f32 = v1.magnitude_sqr()
       .sqrt(); // Take the sqrt if you want the magnitude.
    
  • Normalize

    Normalization is implemented for vectors and bivectors:

    let v1: Vector<f32> = Vector { ... };
    // Note: This will always perform a square root operation.  Norms are not cached by simply_2dpga.
    let v1_normalized = v1.normalized();
    

Extras

Things that aren't strictly 2d PGA primitives have been placed in the 'extras' crate.

2d Points

...

Rotors Motors and Transformers

...

Dependencies

~155KB