Quick Start

Klein is header only, so you may use it by adding the contents of the public directory to your include path. If you use cmake, you may opt to link the klein target interface. If you have CMake 3.15 or later, you can use the following snippet to easily fetch Klein into your build tree:

include(FetchContent)

# This tracks the latest commit on the master branch of the Klein
# repository. Instead of `origin/master`, you can specify a specific
# commit hash or tag.
FetchContent_Declare(
    klein
    GIT_REPOSITORY https://github.com/jeremyong/Klein.git
    GIT_TAG origin/master
)
FetchContent_MakeAvailable(klein)

# Now, you can use target_link_libraries(your_lib PUBLIC klein::klein)
# If you can target SSE4.1 (~97% market penetration), you can link against
# the target klein::klein_sse42 instead.

The primary "catch-all" header provided can be included using #include <klein/klein.hpp>. The klein.hpp header includes the following:

File Purpose
direction.hpp Defines the direction class.
dual.hpp Defines the dual class.
plane.hpp Defines the plane class.
point.hpp Defines the point class.
line.hpp Defines the line, branch, and ideal_line classes.
rotor.hpp Defines the rotor class.
translator.hpp Defines the translator class.
motor.hpp Defines the motor class.
dual.hpp Defines the dual class.
geometric_product.hpp Defines the geometric product between all supported entities.
meet.hpp Defines the exterior product between all supported entities.
join.hpp Defines the regressive product between all supported entities.
inner_product.hpp Defines the inner product between all supported entities.
project.hpp Defines the project function to project between entities.
exp_log.hpp Defines the exp and log functions between supported entities.

Here's a simple snippet to get you started:

#include <klein/klein.hpp>

// Create a rotor representing a pi/2 rotation about the z-axis
// Normalization is done automatically
rotor r{M_PI * 0.5f, 0.f, 0.f, 1.f};

// Create a translator that represents a translation of 1 unit
// in the yz-direction. Normalization is done automatically.
translator t{1.f, 0.f, 1.f, 1.f};

// Create a motor that combines the action of the rotation and
// translation above.
motor m = r * t;

// Construct a point at position (1, 0, 0)
point p1{1, 0, 0};

// Apply the motor to the point. This is equivalent to the conjugation
// operator m * p1 * ~m where * is the geometric product and ~ is the
// reverse operation.
point p2 = m(p1);

// We could have also written p2 = m * p1 * ~m but this will be slower
// because the call operator eliminates some redundant or cancelled
// computation.
// point p2 = m * p1 * ~m;

// We can access the coordinates of p2 with p2.x(), p2.y(), p2.z(),
// and p2.w(), where p.2w() is the homogeneous coordinate (initialized
// to one). It is recommended to localize coordinate access in this way
// as it requires unpacking storage that may occupy an SSE register.

// Rotors and motors can produce 4x4 transformation matrices suitable
// for upload to a shader or for interoperability with code expecting
// matrices as part of its interface. The matrix returned in this way
// is a column-major matrix
mat4x4 m_matrix = m.as_mat4x4();

The spherical interpolation (aka slerp) employed to produce smooth incremental rotations/transformations in the quaternion algebra is available in Klein using the exp and log functions as in the snippet below.

// Blend between two motors with a parameter t in the range [0, 1]
kln::motor blend_motors(kln::motor const& a, kln::motor const& b, float t)
{
    // Starting from a, the motor needed to get to b is b * ~a.
    // To perform this motion continuously, we can take the principal
    // branch of the logarithm of b * ~a, and subdivide it before
    // re-exponentiating it to produce a motor again.

    // In practice, this should be cached whenever possible.
    line motor_step = log(b * ~a);

    // exp(log(m)) = exp(t*log(m) + (1 - t)*log(m))
    // = exp(t*(log(m))) * exp((1 - t)*log(m))
    motor_step *= t;

    // The exponential of the step here can be cached if the blend occurs
    // with fixed steps toward the final motor. Compose the interpolated
    // result with the start motor to produce the intermediate blended
    // motor.
    return exp(motor_step) * a;
}