quaternion
Pure Gleam quaternion math library for 3D rotations.
Quaternions are a mathematical representation of rotations in 3D space that:
- Avoid gimbal lock
- Provide smooth interpolation (slerp)
- Are more compact than rotation matrices
- Compose efficiently
Quick Start
import q
import vec/vec3
// Create quaternion from axis-angle
let rotation = q.from_axis_angle(vec3.Vec3(0.0, 1.0, 0.0), 1.57)
// Or from Euler angles
let rotation = q.from_euler(vec3.Vec3(0.0, 1.57, 0.0))
// Rotate a vector
let rotated = q.rotate(rotation, vec3.Vec3(1.0, 0.0, 0.0))
// Interpolate between rotations
let halfway = q.slerp(from: rot1, to: rot2, t: 0.5)
Types
Quaternion represents a rotation in 3D space.
Quaternions use four components (x, y, z, w) where:
- (x, y, z) represents the rotation axis scaled by sin(angle/2)
- w represents cos(angle/2)
pub type Quaternion {
Quaternion(x: Float, y: Float, z: Float, w: Float)
}
Constructors
-
Quaternion(x: Float, y: Float, z: Float, w: Float)
Values
pub fn axis(quat: Quaternion) -> Result(vec3.Vec3(Float), Nil)
Get the rotation axis.
Returns Error if the quaternion represents no rotation (identity).
pub fn conjugate(quat: Quaternion) -> Quaternion
Compute the conjugate of a quaternion.
The conjugate represents the inverse rotation.
pub fn dot(q1: Quaternion, q2: Quaternion) -> Float
Compute the dot product of two quaternions.
pub fn from_axis_angle(
axis: vec3.Vec3(Float),
angle: Float,
) -> Quaternion
Create a quaternion from axis-angle representation.
Parameters
axis: The rotation axisangle: The rotation angle in radians
Example
// 90 degree rotation around Y axis
let rotation = q.from_axis_angle(vec3.Vec3(0.0, 1.0, 0.0), 1.57)
pub fn from_euler(euler: vec3.Vec3(Float)) -> Quaternion
Convert Euler angles (radians) to quaternion using XYZ rotation order.
Example
// Rotate 90 degrees around Y axis
let rotation = q.from_euler(vec3.Vec3(0.0, 1.57, 0.0))
pub fn from_to_rotation(
from: vec3.Vec3(Float),
to: vec3.Vec3(Float),
) -> Quaternion
Create a quaternion that rotates from one direction to another.
pub fn inverse(quat: Quaternion) -> Quaternion
Compute the inverse of a quaternion.
For unit quaternions (normalized), this is equivalent to the conjugate.
pub fn linear_interpolation(
from from: Quaternion,
to to: Quaternion,
t t: Float,
) -> Quaternion
Linear interpolation between two quaternions.
Faster than slerp but doesn’t maintain constant angular velocity. Result should be normalized.
pub fn look_at(
forward forward: vec3.Vec3(Float),
target target: vec3.Vec3(Float),
up up: vec3.Vec3(Float),
) -> Quaternion
Create a quaternion that looks from one direction toward a target direction.
Creates a rotation that orients the forward direction to point toward the target direction,
with the given up vector for orientation. Useful for cameras and billboards.
Parameters
forward: The current forward direction (usually Vec3(0.0, 0.0, -1.0) for cameras)target: The direction to look towardup: The up vector for orientation (usually Vec3(0.0, 1.0, 0.0))
Example
// Make camera look at target from position
let camera_pos = Vec3(10.0, 10.0, 10.0)
let target_pos = Vec3(0.0, 0.0, 0.0)
let direction = vec3f.normalize(vec3f.subtract(target_pos, camera_pos))
let quat = look_at(Vec3(0.0, 0.0, -1.0), direction, Vec3(0.0, 1.0, 0.0))
pub fn loosely_equals(
q1: Quaternion,
q2: Quaternion,
tolerating epsilon: Float,
) -> Bool
Check if two quaternions are approximately equal within a tolerance.
Useful for floating-point comparisons where exact equality is problematic. Note: Quaternions q and -q represent the same rotation, so this function checks both orientations.
Parameters
q1: First quaternionq2: Second quaternionepsilon: Tolerance for comparison (typically 0.0001 to 0.001)
Example
let q1 = from_euler(Vec3(0.0, 1.57, 0.0))
let q2 = from_euler(Vec3(0.0, 1.57001, 0.0))
loosely_equals(q1, q2, epsilon: 0.001) // True
pub fn multiply(q1: Quaternion, q2: Quaternion) -> Quaternion
Multiply two quaternions (q1 * q2).
Represents the combined rotation of applying q1 then q2.
Example
let rotate_y = q.from_axis_angle(vec3.Vec3(0.0, 1.0, 0.0), 1.57)
let rotate_x = q.from_axis_angle(vec3.Vec3(1.0, 0.0, 0.0), 0.5)
let combined = q.multiply(rotate_y, rotate_x)
pub fn normalize(quat: Quaternion) -> Quaternion
Normalize a quaternion to unit length.
All rotation quaternions should be normalized.
pub fn rotate(
quat: Quaternion,
v: vec3.Vec3(Float),
) -> vec3.Vec3(Float)
Rotate a vector by a quaternion.
Example
let rotation = q.from_axis_angle(vec3.Vec3(0.0, 1.0, 0.0), 1.57)
let point = vec3.Vec3(1.0, 0.0, 0.0)
let rotated = q.rotate(rotation, point) // ~Vec3(0.0, 0.0, -1.0)
pub fn spherical_linear_interpolation(
from from: Quaternion,
to to: Quaternion,
t t: Float,
) -> Quaternion
Spherical linear interpolation (slerp) between two quaternions.
Provides smooth rotation interpolation without gimbal lock issues.
Parameters
from: Starting quaternionto: Target quaterniont: Interpolation factor (0.0 = from, 1.0 = to)
Example
let start = q.from_euler(vec3.Vec3(0.0, 0.0, 0.0))
let end = q.from_euler(vec3.Vec3(0.0, 1.57, 0.0))
let halfway = q.slerp(from: start, to: end, t: 0.5)
pub fn to_euler(quat: Quaternion) -> vec3.Vec3(Float)
Convert quaternion to Euler angles (radians) using XYZ rotation order.
Returns Vec3(roll, pitch, yaw).