moonmath

    Vectors, matrices, quaternions and geometry for MoonBit: column-major, right-handed, and ready to copy into a GPU buffer, with a special-function subpackage in double and arbitrary precision.

    math
    linear-algebra
    vector
    vec2
    vec3
    vec4
    matrix
    mat3
    mat4
    quaternion
    geometry
    transform
    graphics
    3d
    gamedev
    column-major
    wgsl
    webgpu
    gpu
    glm
    half-precision
    euler-angles
    shear
    color
    interval
    random-sampling
    special-functions
    gamma
    bessel
    hypergeometric
    elliptic-integral
    zeta
    arbitrary-precision
    moonbit
    Download zip
    Version
    0.1.2
    License
    Apache-2.0
    Last updated
    16 hours ago
    Downloads
    28
    moonmath

    let eye = @moonmath.Vec3::new(0.0, 1.6, 6.0)
    let view = @moonmath.Mat4::look_at(eye, @moonmath.Vec3::zero(), @moonmath.Vec3::unit_y())
    let projection = @moonmath.Mat4::perspective(@moonmath.to_radians(60.0), 16.0 / 9.0, 0.1, 100.0)
    let view_projection = projection * view

    let on_screen = view_projection.transform_point(@moonmath.Vec3::zero())

    #Installing

    moon add moonbitstack/moonmath

    The package is moonbitstack/moonmath, imported as @moonmath. Double precision is the same library under moonbitstack/moonmath/f64, imported as @f64.

    #What it is, and what it is not

    It is the arithmetic a renderer, a layout engine or a physics step needs: the vectors and matrices a shader also has, the rotations that are easier to interpolate than a matrix, the affine transforms a scene graph stores, and the geometry that says whether two things touch.

    It is not a rendering library. Nothing here talks to a GPU, loads a mesh, or knows what a camera is beyond the matrix that describes one. There is no four-component SIMD trickery and nothing allocates beyond the values it returns.

    #Conventions

    Five decisions shape the whole surface, and all five are visible in every signature.

    Right-handed, column-major. x runs east, y north, z towards the viewer, so cross(x, y) == z. A matrix stores its columns, c0 first, which is the order a mat4x4<f32> occupies in a buffer: to_array gives the sixteen floats in that order and from_array reads them back. A matrix product a * b applies b first.

    Depth runs from zero to one. perspective, perspective_infinite, orthographic and frustum map the near plane to depth 0 and the far plane to 1, as WebGPU clips. The _gl variants of the same four map them to -1 and 1 for a pipeline that clips like OpenGL.

    Angles are radians. Every angle in the library, in a signature or out of one, is in radians; to_radians and to_degrees convert at the edge, where the number came from a file or a human.

    Operators take two values of the same type. a + b, a - b, a * b, a / b, -a do what they look like on two vectors, two matrices or two quaternions. MoonBit's arithmetic traits are declared fn mul(Self, Self) -> Self, so the two sides of an operator are the same type by definition: there is no Vec3 * Float to define, and no Mat4 * Vec4 either. Scaling is scale and its inverse divide, and a matrix times a vector is mul_vec4, mul_vec3 or mul_vec2.

    A matrix that cannot be inverted says so. inverse returns an Option, None for a singular matrix, and the caller decides whether that is an error. The operator / has nowhere to report it, so a / b aborts when b is singular; inverse is what to reach for when the case can happen.

    Two more, smaller ones. The zero vector has no direction, and normalize returns Vec3::zero() for it rather than a NaN — try_normalize and normalize_or exist for callers that need to tell the case apart. And every value type implements Eq, Show and Debug; the integer vectors also implement Hash. Equality is exact — approx is the comparison to use on results of arithmetic, and it takes an optional epsilon.

    #Using it

    #Rotations

    A quaternion is four numbers that interpolate; a matrix is what a shader wants. Both directions are here, and a rotation round-trips through either.

    let turn = @moonmath.Quat::from_axis_angle(@moonmath.Vec3::unit_y(), @moonmath.quarter_pi)
    let half_way = @moonmath.Quat::identity().slerp(turn, 0.5)

    let ahead = turn.rotate(@moonmath.Vec3::unit_z())
    let matrix = turn.to_mat3()

    // Euler angles, in one of the twelve orders, by name.
    let from_yaw = @moonmath.Quat::from_euler(
    @moonmath.EulerOrder::Zyx,
    @moonmath.Vec3::new(0.0, 0.0, @moonmath.to_radians(90.0)),
    )
    let back = from_yaw.to_euler(@moonmath.EulerOrder::Zyx)

    The three components of an Euler angle vector are the turns about x, y and z — the component says which axis, wherever that axis turns up in the order. Zyx above turns about z first, and the turn is in the third component because it is a turn about z.

    Six of the twelve orders turn about one axis twice — Xyx and its siblings — and two turns about x cannot both live in the single component named after it. Those six have no by-axis form, so from_euler and to_euler abort on them; from_euler_turns and to_euler_turns take a Turns record whose fields are named after their place in the order, and cover all twelve:

    let precessing = @moonmath.Quat::from_euler_turns(
    @moonmath.EulerOrder::Xyx,
    @moonmath.Turns::new(0.3, 1.2, -0.4),
    )
    let turns = precessing.to_euler_turns(@moonmath.EulerOrder::Xyx)

    #Scene graph transforms

    Position, rotation and scale are three separate pieces, and so is a matrix built from them:

    let placement = @moonmath.Trs::new(
    @moonmath.Vec3::new(0.0, 2.0, 0.0),
    @moonmath.Quat::from_axis_angle(@moonmath.Vec3::unit_y(), @moonmath.half_pi),
    @moonmath.Vec3::splat(0.5),
    )
    let model = @moonmath.Mat4::from_trs(placement)

    // And back out again, for an editor or an animation to edit.
    let editable = model.to_trs().unwrap()

    Affine2 and Affine3 are the same transform without the row that does nothing: cheaper to compose and apply, and impossible to make non-affine.

    #Geometry

    Boxes, rays, planes, spheres, triangles and the frustum, with the tests a broad phase and a pick need.

    let room = @moonmath.Aabb3::new(
    @moonmath.Vec3::new(-4.0, 0.0, -4.0),
    @moonmath.Vec3::new(4.0, 3.0, 4.0),
    )
    let ray = @moonmath.Ray3::new(
    @moonmath.Vec3::new(0.0, 2.0, 0.0),
    @moonmath.Vec3::new(0.0, -1.0, -1.0).normalize(),
    )

    match ray.intersect_aabb(room) {
    Some(t) => println("the room is \{t} away along the ray, at \{ray.at(t)}")
    None => println("the ray leaves the room alone")
    }

    let frustum = @moonmath.Frustum::from_matrix(view_projection).unwrap()
    if frustum.intersects_aabb(room) {
    println("there is something to draw")
    }

    Rectangles are separate from boxes because a layout thinks in a corner and a size, not in two opposite corners: Rect is origin plus size, and Aabb2 is min plus max. They convert to each other with to_aabb and to_rect.

    #Double precision

    moonbitstack/moonmath/f64 holds the same types at Double, generated from the same template as the single-precision package. A simulation that runs for hours, or spans a planet, converts at the boundary rather than living in Float throughout:

    let far_away = @f64.Vec3::new(1.0e9, 0.0, 0.0)
    let scaled = far_away.scale(2.0)

    #Half precision

    A packed vertex or a texture often carries a sixteen-bit float, and Half is that encoding: the bits in and out, the conversion to and from Float rounded to nearest with ties to even, and the special values kept.

    let packed = @moonmath.Half::from_float(1.5)
    let restored = packed.to_float()
    let raw = packed.to_bits()

    #Colour, shear, intervals and sampling

    Color3 and Color4 are the component arithmetic a blend or a gradient needs; rgb_to_hsv and hsv_to_rgb convert between the two forms. Shear is the off-diagonal linear map a skew is. Interval is a one-dimensional range with union, intersection and growth. Line3 is an infinite line with the closest-point and plane-intersection questions. The sampling functions turn uniform values in [0, 1) into points with the right distribution, and leave the random source to the caller:

    let red = @moonmath.Color3::new(1.0, 0.0, 0.0)
    let hue = @moonmath.rgb_to_hsv(red)

    let skew = @moonmath.Shear::new(0.3, 0.0, 0.0).to_mat3()
    let span = @moonmath.Interval::new(0.0, 1.0).union(
    @moonmath.Interval::new(2.0, 3.0),
    )

    // The two values are a uniform random source the caller owns.
    let on_sphere = @moonmath.Vec3::random_on_sphere(0.25, 0.75)

    #The interface

    Every type below is a value type: immutable, and returning a new value from every operation. Unless a row says otherwise, each also carries + - * /, unary -, Eq, Show and Debug.

    #Scalars

    SignatureMeaning
    to_radians(degrees : Float) -> Floatdegrees to radians
    to_degrees(radians : Float) -> Floatradians to degrees
    lerp(a, b, t) -> Floata at t = 0, b at t = 1, extrapolating outside
    inverse_lerp(start, end, value) -> Floatwhere a value sits in a range; zero for an empty range
    remap(value, from_start, from_end, to_start, to_end) -> Floatthe same position in another range
    smoothstep(start, end, value) -> Floatthe cubic easing with zero slope at both ends
    wrap(value, span) -> Floatfolded into 0..span
    wrap_range(value, start, end) -> Floatfolded into start..end
    approx(a, b, eps? : Float) -> Boolequal to within eps, or within default_epsilon
    approx_angle(a, b, eps? : Float) -> Boolthe same, for angles that wrap at tau
    clamp01(value) -> Floatpinned to 0..1
    pi, tau, half_pi, quarter_pi, default_epsilonthe constants

    Half is the 16-bit float of a packed vertex or a texture: from_float, to_float, from_bits, to_bits, the predicates is_finite, is_nan, is_inf, is_normal, is_denormal, and the constants from zero and one to max, min and epsilon.

    #Vectors

    Vec2, Vec3 and Vec4 share this much:

    SignatureMeaning
    Vec::new(...), splat(v), zero(), one(), unit_x(), unit_y(), unit_z(), unit_w()constructors; unit_z and unit_w where the type has them
    dot(other) -> Floatthe dot product
    length(), length_squared() -> Floatthe length, and its square
    distance(other), distance_squared(other) -> Floatbetween two points
    normalize() -> Vecto length one; the zero vector stays zero
    try_normalize() -> Vec?, normalize_or(fallback) -> Vecthe same, reporting the case instead
    scale(factor), divide(divisor) -> Vecmultiplied and divided by a scalar
    lerp(other, t), min(other), max(other), clamp(low, high) -> Veccomponent-wise
    abs(), floor(), ceil(), round(), fract() -> Veccomponent-wise rounding; a half rounds up
    to_array() -> FixedArray[Float]the components in order
    is_finite() -> Boolno NaN and no infinity
    approx(other, eps? : Float) -> Boolequal to within eps

    What each type adds:

    TypeSignatureMeaning
    Vec2cross(other) -> Floatthe signed area the two span
    perp() -> Vec2a quarter turn counter-clockwise
    angle_between(other) -> Floatthe signed angle from this to that, in -pi..pi
    yx() -> Vec2the components swapped
    to_vec3(z), to_vec4(z, w)lifted into a higher dimension
    Vec3cross(other) -> Vec3the perpendicular, right-handed
    angle_between(other) -> Floatthe unsigned angle, in 0..pi
    xy(), xz(), yz() -> Vec2a plane's worth of components
    zyx(), xzy(), yxz(), yzx(), zxy() -> Vec3the six orders of the three components
    to_vec4(w) -> Vec4lifted into 4D
    Vec4xy() -> Vec2, xyz() -> Vec3the leading components
    wzyx() -> Vec4all four backwards

    Vec2 and Vec3 also carry reflect(normal), refract(normal, eta) — which returns None past the critical angle — project(onto) and reject(onto). reflect and refract assume unit vectors, as a shader does.

    #Integer vectors

    IVec2/3/4 are Int, for grid cells and pixel coordinates; UVec2/3/4 are UInt, for sizes and counts. Both carry + - * / component-wise (integer division, truncating), Eq, Hash, Show and Debug; the signed ones also carry unary - and abs.

    SignatureMeaning
    new(...), splat(v), zero(), one()constructors
    dot(other) -> Int (or UInt)the dot product
    min(other), max(other), clamp(low, high)component-wise, inclusive
    to_array()the components in order
    IVec::abs() -> IVecabsolute value
    IVec::as_vec2/3/4(), UVec::as_vec2/3/4()the same values as floats, exactly
    IVec::from_vec2/3/4(v)the floats truncated towards zero

    #Matrices

    Mat2, Mat3 and Mat4 store their columns: c0, and c1, c2, c3 where the type has them.

    SignatureMeaning
    identity(), zero()the two that need no arguments
    from_cols(...), from_rows(...)built one way or the other
    from_array(values) -> Matfrom the column-major array a buffer holds; aborts on the wrong length
    col(i) -> Vec, row(i) -> Vec, get(row, column) -> Floatreading it
    transpose() -> Matrows and columns exchanged
    determinant() -> Floatthe volume it scales by
    inverse() -> Mat?None when it is singular
    mul_vec2/3/4(v) -> Vecapplied to a vector
    to_array() -> FixedArray[Float]column-major, ready for a buffer
    is_finite(), approx(other, eps?) -> Bool

    TypeSignatureMeaning
    Mat2from_scale(Vec2), from_angle(radians), to_angle() -> Floata scaling, a rotation, and the angle back out
    Mat3from_scale(Vec3), from_axis_angle(axis, radians), from_quat(q), from_euler(order, angles), from_euler_turns(order, turns)
    from_mat4(m)the upper-left block, without the translation
    Mat4from_scale(Vec3), from_translation(Vec3), from_axis_angle(axis, radians), from_quat(q), from_euler(order, angles), from_euler_turns(order, turns)
    from_mat3(m)the 3×3 with w = 1 and no translation
    from_trs(trs), to_trs() -> Trs?scale, then rotate, then move; and back
    transform_point(p) -> Vec3a point, divided by w when there is one
    transform_vector(v) -> Vec3a direction, untouched by the translation
    look_at(eye, target, up) -> Mat4the view matrix of a camera
    perspective(fov_y, aspect, near, far)depth 0..1
    perspective_infinite(fov_y, aspect, near)depth 0..1, no far plane
    orthographic(left, right, bottom, top, near, far)depth 0..1
    frustum(left, right, bottom, top, near, far)depth 0..1, off-centre
    perspective_gl, perspective_infinite_gl, orthographic_gl, frustum_glthe same four, depth -1..1
    Shearnew(xy, xz, yz), zero(), to_mat3(), to_mat4()the off-diagonal terms as a linear map

    #Rotations

    SignatureMeaning
    Quat::identity(), Quat::new(x, y, z, w)no rotation, and a raw one
    Quat::from_axis_angle(axis, radians)the right-hand rule about an axis
    Quat::from_euler(order, angles), Quat::to_euler(order) -> Vec3the turns about x, y and z, by axis; aborts for an order that turns about one axis twice
    Quat::from_euler_turns(order, turns), Quat::to_euler_turns(order) -> Turnsthe same, with the three turns in order, for all twelve orders
    Quat::from_rotation_arc(from, to)the shortest rotation between two directions
    Quat::from_mat3(m), Quat::from_mat4(m)out of a matrix
    Quat::to_mat3(), Quat::to_mat4()into one
    Quat::to_axis_angle() -> (Vec3, Float)the axis and the angle
    Quat::rotate(v) -> Vec3the vector turned
    Quat::inverse() -> Quat?, conjugate(), normalize(), length(), length_squared(), dot(other)
    Quat::slerp(other, t), Quat::nlerp(other, t)at constant angular speed, and along the straight line
    Quat::to_array(), is_finite(), approx(other, eps?)

    EulerOrder names the twelve orders: Xyz, Xzy, Yxz, Yzx, Zxy, Zyx, Xyx, Xzx, Yxy, Yzy, Zxz, Zyz. Turns is the record of three turns in order — first, second, third — with Turns::new and Turns::approx.

    #Affine transforms

    SignatureMeaning
    Affine2::new(linear, translation), Affine3::new(linear, translation)
    identity(), from_translation(v), from_scale(v)
    Affine2::from_angle(radians), from_angle_translation(radians, offset)
    Affine3::from_rotation(q), from_axis_angle(axis, radians), from_trs(trs), to_trs() -> Trs?
    transform_point(p), transform_vector(v)
    to_mat3() / to_mat4(), from_mat3(m) -> Affine2? / from_mat4(m) -> Affine3?to a matrix and back; None when the matrix is not affine
    inverse() -> Affine?None when the linear part is singular
    is_finite(), approx(other, eps?)

    Trs is the three parts of a transform — translation : Vec3, rotation : Quat, scale : Vec3 — built with Trs::new, and what Mat4::to_trs recovers from a matrix built the same way.

    #Geometry

    TypeSignatureMeaning
    Rectnew(origin, size), from_xywh(x, y, w, h), from_edges(left, top, right, bottom)a corner and an extent
    min(), max(), center() -> Vec2, width(), height(), area() -> Float
    contains(point), intersects(other), intersection(other) -> Rect?, union(other)
    inset(amount), translate(offset), is_empty(), corners(), to_aabb() -> Aabb2
    Aabb2, Aabb3new(min, max), from_center_half_extents(center, extents), from_points(points) -> Aabb?, empty()
    center(), size(), extents(), area() / volume(), surface_area()
    contains_point(p), contains(other), intersects(other), intersection(other) -> Aabb?, union(other), expand(amount)
    corners(), is_empty(), Aabb3::transform(m : Mat4) -> Aabb3the box around the transformed box
    Ray2, Ray3new(origin, direction), at(t) -> Veca start and a direction; t may be any distance
    intersect_aabb(box) -> Float?, intersect_aabb_interval(box) -> (Float, Float)?the first crossing, and the stretch inside
    Ray2::intersect_rect(rect), Ray2::intersect_circle(center, radius)
    Ray3::intersect_sphere(sphere), intersect_plane(plane), intersect_triangle(triangle)
    Planenew(normal, distance), from_point_normal(point, normal) -> Plane?, from_points(a, b, c) -> Plane?dot(normal, p) == distance
    signed_distance(p), project(p) -> Vec3, intersect_ray(ray) -> Float?, flip()
    Spherenew(center, radius), contains_point(p), intersects_sphere(other), intersects_aabb(box), surface_area(), volume()
    Trianglenew(a, b, c), normal() -> Vec3, area(), barycentric(p) -> Vec3?, intersect_ray(ray) -> Float?
    Frustumfrom_matrix(view_projection) -> Frustum?, from_matrix_gl(view_projection) -> Frustum?six planes facing inwards
    planes() -> FixedArray[Plane], plane(i) -> Planeleft, right, bottom, top, near, far
    contains_point(p), contains_aabb(box), intersects_aabb(box), contains_sphere(s), intersects_sphere(s)
    Line3new(point, direction), at(t), closest_point_to(p), distance_to_point(p), closest_point_to_line(line), distance_to_line(line), intersect_plane(plane) -> Vec3?an infinite line
    Intervalnew(min, max), empty(), size(), center(), contains(x), intersects(other), intersection(other) -> Interval?, union(other), extend_by(x), expand(amount)a one-dimensional range

    The intersection functions return the t of the crossing rather than the point, because the distance is usually what a caller wants and the point is at(t). A ray that starts inside a shape reports the crossing on its way out.

    #Colour

    TypeSignatureMeaning
    Color3, Color4new(...), splat(v), zero(), one()red, green and blue, and alpha
    min(other), max(other), lerp(other, t)component-wise
    to_array(), is_finite(), approx(other, eps?)
    rgb_to_hsv(c), hsv_to_rgb(c)between the two forms; the hue in turns

    #Special functions

    moonbitstack/moonmath/special is a subpackage for the functions a probability, a physics or a numerical caller reaches for. Every body is written once, in special/generic, against an Ops algebra of the number type; special is the double-precision face of it and moonnum's Fixed supplies the arbitrary-precision one, so the two precisions cannot drift apart.

    FunctionMeaning
    gamma(x), lgamma(x)the gamma function and its log; Stirling with the reflection below one half
    beta(a, b), lbeta(a, b)the beta function and its log
    digamma(x), polygamma(n, x)the logarithmic derivative of gamma, and its n-th derivative
    gammainc(a, x), gammaincc(a, x)the regularised incomplete gamma, lower and upper
    incbeta(a, b, x)the regularised incomplete beta
    erf(x), erfc(x), erfcx(x), erfi(x)the error function, its complement, the scaled complement, the imaginary one
    erfinv(x), erfcinv(x)the inverses of the first two
    bessel_j(v, x), bessel_y(v, x)the Bessel functions of the first and second kind, real order
    bessel_i(v, x), bessel_k(v, x)the modified ones
    ellipk(m), ellipe(m), ellipkm1(m)the complete elliptic integrals, m the parameter
    ellipkinc(phi, m), ellipeinc(phi, m)the incomplete ones
    elliprf, elliprd, elliprc, elliprj, elliprgthe Carlson symmetric forms the others are built on
    hyper0f1(b, x), hyper1f1(a, b, x), hyper2f1(a, b, c, x)the hypergeometric functions
    zeta(s)the Riemann zeta, above and below one

    let root_pi = @special.gamma(0.5)
    let tail = @special.erfc(2.0)
    let drum = @special.bessel_j(0.0, 2.4)

    The arbitrary-precision side runs the same functions on moonnum's Fixed — a fixed-point real carried as mantissa / 2^bits — at whatever width the caller asks for:

    let bits = @moonnum.Fixed::bits_for_digits(50)
    let ops = @generic.fixed_ops(bits)
    let x = @moonnum.Fixed::of_decimal("0.5", bits)
    let value = @generic.gamma(x, ops)

    Fixed lives in moonbitstack/moonnum, which is where a number belongs, and moonmath depends on it. Its release predates the type, so until moonnum cuts a version with it the dependency is resolved from a sibling checkout through moon.work:

    members = [ ".", "../moonnum", ]

    A checkout of moonbitstack/moonnum at branch feat/bigfloat alongside this one is therefore required to build. Once the release lands, deleting moon.work is the whole of the change; moon.mod already names the module.

    The precision a caller should expect is not the same for every function, and the tests say so rather than averaging it away. In double the gamma, error, zeta and elliptic families agree with the references to about 1e-15; the Bessel functions to about 1e-12, the widest case being a nearly integer order, where the reflection divides by the sine of a small angle; and at fifty and a hundred digits the same table is replayed at 1e-45 and 1e-80.

    hyper1f1 is the one function here that calls the wider type as a working tool rather than as a check: the body reports the largest term its series formed, and the double-precision face re-runs the sum at forty-five digits and rounds once when that term ran far past the answer. That is what carries the large-parameter region, where the direct sum cancels — against Boost's table, which lives entirely in that region, the worst row goes from 4e-4 to 1.1e-12.

    hyper2f1 has no such rescue and says so in its doc comment: with parameters of order a hundred and the argument near one, its transformation needs six hundred digits of dynamic range, which no fixed point supplies. It claims the moderate region, as does hyper0f1, and a caller outside it should not believe a double from it.

    Two independent reference sets back that up. scripts/gen_special.py reads mpmath at a hundred and thirty working digits and fills both the double and the arbitrary-precision tables. scripts/gen_boost.py takes the tables that ship with Boost.Math's own test suite — twenty-seven of them, nine thousand rows — and boost_check_wbtest.mbt replays them, every row except a documented sample of the 1F1 table, whose escalated rows are the one place the suite spends real time. The tables are checked in, so a test run needs neither Python, mpmath nor a Boost checkout.

    #Examples

    Four runnable programs, each moon run-able on its own:

    ExampleWhat it shows
    src/examples/01-tourthe whole surface in one run
    src/examples/02-cameraa camera, what lands on screen, frustum culling, and a pick
    src/examples/03-layoutrectangles, insets and hit testing for a window of panes
    src/examples/04-collisiona ray narrowed from a sphere to a triangle, and a bounce

    #Performance

    Throughput on the native backend, moon bench --target native --release, single precision, on an x86-64 machine with the 2026-10-23 toolchain. Each row is one operation, measured 64 at a time inside a timed closure; the numbers are the best of three runs.

    Operationnsper second
    Vec3::dot1.9540,000,000
    Vec4::dot1.7600,000,000
    Frustum::intersects_aabb2147,000,000
    Vec3::normalize5618,000,000
    Mat4::transform_point5618,000,000
    Vec3::cross6116,000,000
    Quat::rotate6416,000,000
    Quat::slerp9610,000,000
    Ray3::intersect_aabb9710,000,000
    Ray3::intersect_sphere1119,000,000
    Quat::to_mat32204,500,000
    Mat4 * Mat42823,500,000
    Mat4::inverse3303,000,000

    The first two rows are arithmetic with nothing to build; the rest return a value type, and on this backend a struct is a heap value, so part of every row above them is the allocation of the result. What that means in practice is that an operation returning a Vec3 costs about the same whether it is a cross product or a normalisation, and that a loop over ten thousand vectors spends much of its time in the allocator rather than in the arithmetic.

    #Licence

    Apache-2.0.

    Aabb2

    pub(all) struct Aabb2 {
    min : Vec2
    max : Vec2
    } derive(Eq)

    An axis-aligned box in the plane, given by two opposite corners.

    Broad-phase collision and culling ask "is this thing anywhere near that thing", and a box around each answers it without touching the contents. The corners are min and max rather than a corner and a size because that is the form the tests read, and because merging two boxes is then two component comparisons.

    Values are immutable.
    impl Show for Aabb2

    Aabb2::approx

    fn Aabb2::approx(self : Aabb2, other : Aabb2, eps? : Float) -> Bool

    Whether the two boxes agree to within eps.

    Aabb2::area

    fn Aabb2::area(self : Aabb2) -> Float

    The area the box covers, zero when it is empty.

    Aabb2::center

    fn Aabb2::center(self : Aabb2) -> Vec2

    The point in the middle.

    Aabb2::contains

    fn Aabb2::contains(self : Aabb2, other : Aabb2) -> Bool

    Whether other lies entirely inside this box.

    Aabb2::contains_point

    fn Aabb2::contains_point(self : Aabb2, point : Vec2) -> Bool

    Whether the point is inside or on the boundary.

    Aabb2::corners

    fn Aabb2::corners(self : Aabb2) -> FixedArray[Vec2]

    The four corners, starting at min and going counter-clockwise.

    Aabb2::empty

    fn Aabb2::empty() -> Aabb2

    A box with nothing in it: the identity for union.

    Its corners are the wrong way round on purpose, so that merging anything into it gives that thing back unchanged.

    Aabb2::equal

    fn Aabb2::equal(Aabb2, Aabb2) -> Bool

    Aabb2::expand

    fn Aabb2::expand(self : Aabb2, amount : Float) -> Aabb2

    The same box grown by amount in every direction.

    Aabb2::extents

    fn Aabb2::extents(self : Aabb2) -> Vec2

    The half extent along each axis: the distance from the middle to a face.

    Aabb2::from_center_half_extents

    fn Aabb2::from_center_half_extents(center : Vec2, half_extents : Vec2) -> Aabb2

    The box with the given centre and half-extents.

    Aabb2::from_points

    fn Aabb2::from_points(points : FixedArray[Vec2]) -> Aabb2?

    The smallest box around the points, or None when there are none.

    Aabb2::intersection

    fn Aabb2::intersection(self : Aabb2, other : Aabb2) -> Aabb2?

    The overlapping box, or None when they do not overlap.

    Aabb2::intersects

    fn Aabb2::intersects(self : Aabb2, other : Aabb2) -> Bool

    Whether the two boxes overlap in positive area.

    Aabb2::is_empty

    fn Aabb2::is_empty(self : Aabb2) -> Bool

    Whether the box holds nothing.

    Aabb2::new

    fn Aabb2::new(min : Vec2, max : Vec2) -> Aabb2

    The box with the two given corners.

    Aabb2::not_equal

    fn Aabb2::not_equal(x : Aabb2, y : Aabb2) -> Bool

    Aabb2::output

    fn Aabb2::output(self : Aabb2, logger : &Logger) -> Unit

    Aabb2::size

    fn Aabb2::size(self : Aabb2) -> Vec2

    The full extent along each axis.

    Aabb2::to_rect

    fn Aabb2::to_rect(self : Aabb2) -> Rect

    The same points as a rectangle.

    Aabb2::to_repr

    Aabb2::to_string

    fn Aabb2::to_string(self : Aabb2) -> String

    Aabb2::union

    fn Aabb2::union(self : Aabb2, other : Aabb2) -> Aabb2

    The smallest box containing both.

    Aabb3

    pub(all) struct Aabb3 {
    min : Vec3
    max : Vec3
    } derive(Eq)

    An axis-aligned box in space, given by two opposite corners.

    The same broad-phase role as Aabb2, with a third axis: a frustum is tested against one of these per object per frame, so the tests here are written to read the extremes of a plane's distance rather than to visit eight corners.

    Values are immutable.
    impl Show for Aabb3

    Aabb3::approx

    fn Aabb3::approx(self : Aabb3, other : Aabb3, eps? : Float) -> Bool

    Whether the two boxes agree to within eps.

    Aabb3::center

    fn Aabb3::center(self : Aabb3) -> Vec3

    The point in the middle.

    Aabb3::contains

    fn Aabb3::contains(self : Aabb3, other : Aabb3) -> Bool

    Whether other lies entirely inside this box.

    Aabb3::contains_point

    fn Aabb3::contains_point(self : Aabb3, point : Vec3) -> Bool

    Whether the point is inside or on the boundary.

    Aabb3::corners

    fn Aabb3::corners(self : Aabb3) -> FixedArray[Vec3]

    The eight corners.

    Aabb3::empty

    fn Aabb3::empty() -> Aabb3

    A box with nothing in it: the identity for union.

    Aabb3::equal

    fn Aabb3::equal(Aabb3, Aabb3) -> Bool

    Aabb3::expand

    fn Aabb3::expand(self : Aabb3, amount : Float) -> Aabb3

    The same box grown by amount in every direction.

    Aabb3::extents

    fn Aabb3::extents(self : Aabb3) -> Vec3

    The half extent along each axis.

    Aabb3::from_center_half_extents

    fn Aabb3::from_center_half_extents(center : Vec3, half_extents : Vec3) -> Aabb3

    The box with the given centre and half-extents.

    Aabb3::from_points

    fn Aabb3::from_points(points : FixedArray[Vec3]) -> Aabb3?

    The smallest box around the points, or None when there are none.

    Aabb3::intersection

    fn Aabb3::intersection(self : Aabb3, other : Aabb3) -> Aabb3?

    The overlapping box, or None when they do not overlap.

    Aabb3::intersects

    fn Aabb3::intersects(self : Aabb3, other : Aabb3) -> Bool

    Whether the two boxes overlap in positive volume.

    Aabb3::is_empty

    fn Aabb3::is_empty(self : Aabb3) -> Bool

    Whether the box holds nothing.

    Aabb3::new

    fn Aabb3::new(min : Vec3, max : Vec3) -> Aabb3

    The box with the two given corners.

    Aabb3::not_equal

    fn Aabb3::not_equal(x : Aabb3, y : Aabb3) -> Bool

    Aabb3::output

    fn Aabb3::output(self : Aabb3, logger : &Logger) -> Unit

    Aabb3::random_point

    fn Aabb3::random_point(self : Aabb3, u1 : Float, u2 : Float, u3 : Float) -> Vec3

    A point uniformly distributed through the box.

    u1, u2 and u3 are independent uniform samples in [0, 1), one per axis.

    Aabb3::size

    fn Aabb3::size(self : Aabb3) -> Vec3

    The full extent along each axis.

    Aabb3::surface_area

    fn Aabb3::surface_area(self : Aabb3) -> Float

    The total area of the six faces, zero when it is empty.

    Aabb3::to_repr

    Aabb3::to_string

    fn Aabb3::to_string(self : Aabb3) -> String

    Aabb3::transform

    fn Aabb3::transform(self : Aabb3, m : Mat4) -> Aabb3

    The box around the transformed box.

    A box turned by a rotation is no longer axis-aligned, so the result is the smallest axis-aligned box around the turned one: correct, and looser than it was before the turn.

    Aabb3::union

    fn Aabb3::union(self : Aabb3, other : Aabb3) -> Aabb3

    The smallest box containing both.

    Aabb3::volume

    fn Aabb3::volume(self : Aabb3) -> Float

    The volume the box encloses, zero when it is empty.

    Affine2

    pub(all) struct Affine2 {
    linear : Mat2
    translation : Vec2
    } derive(Eq)

    A 2D affine transform: a linear map and a translation, without the row of a matrix that does nothing but hold (0, 0, 1).

    Composition, inversion and applying are all cheaper than the 3×3 matrix that would carry the same thing, and there is no way to build one of these that is not affine — which is what UI layout, sprite placement and 2D physics want.

    Values are immutable.
    impl Div for Affine2
    impl Mul for Affine2
    impl Show for Affine2

    Affine2::approx

    fn Affine2::approx(self : Affine2, other : Affine2, eps? : Float) -> Bool

    Whether the two transforms agree to within eps element by element.

    Affine2::div

    fn Affine2::div(self : Affine2, other : Affine2) -> Affine2

    Affine2::equal

    fn Affine2::equal(Affine2, Affine2) -> Bool

    Affine2::from_angle

    fn Affine2::from_angle(radians : Float) -> Affine2

    The transform that turns counter-clockwise by radians about the origin.

    Affine2::from_angle_translation

    fn Affine2::from_angle_translation(radians : Float, offset : Vec2) -> Affine2

    The transform that turns by radians and then moves by offset.

    Affine2::from_mat3

    fn Affine2::from_mat3(m : Mat3) -> Affine2?

    The affine transform a 3×3 matrix holds, or None when the matrix is not affine.

    A matrix whose last row is not (0, 0, 1) — a projection, say — carries a perspective that this type cannot hold, and there is no transform to return.

    Affine2::from_scale

    fn Affine2::from_scale(scale : Vec2) -> Affine2

    The transform that scales each axis by the matching component of scale.

    Affine2::from_translation

    fn Affine2::from_translation(offset : Vec2) -> Affine2

    The transform that moves every point by offset.

    Affine2::identity

    fn Affine2::identity() -> Affine2

    The transform that changes nothing.

    Affine2::inverse

    fn Affine2::inverse(self : Affine2) -> Affine2?

    The transform that undoes this one, or None when the linear part is singular.

    A singular linear part flattens the plane onto a line or a point, and no transform brings that back.

    Affine2::is_finite

    fn Affine2::is_finite(self : Affine2) -> Bool

    Whether every element is finite.

    Affine2::mul

    fn Affine2::mul(self : Affine2, other : Affine2) -> Affine2

    Affine2::new

    fn Affine2::new(linear : Mat2, translation : Vec2) -> Affine2

    The transform that maps v to linear * v + translation.

    Affine2::not_equal

    fn Affine2::not_equal(x : Affine2, y : Affine2) -> Bool

    Affine2::output

    fn Affine2::output(self : Affine2, logger : &Logger) -> Unit

    Affine2::to_mat3

    fn Affine2::to_mat3(self : Affine2) -> Mat3

    The same transform as a 3×3 matrix, with (0, 0, 1) as its last row.

    Affine2::to_repr

    Affine2::to_string

    fn Affine2::to_string(self : Affine2) -> String

    Affine2::transform_point

    fn Affine2::transform_point(self : Affine2, p : Vec2) -> Vec2

    The point the transform takes p to.

    Affine2::transform_vector

    fn Affine2::transform_vector(self : Affine2, v : Vec2) -> Vec2

    The direction the transform takes v to, without the translation.

    A direction has no position, so moving the origin must leave it alone.

    Affine3

    pub(all) struct Affine3 {
    linear : Mat3
    translation : Vec3
    } derive(Eq)

    A 3D affine transform: a linear map and a translation, without the fourth row of a matrix that does nothing but hold (0, 0, 0, 1).

    This is the transform a scene node carries — position, rotation and scale applied to its geometry — and it is cheaper than the 4×4 matrix that would also happen to work, since the wasted row would be multiplied four times per vector.

    Values are immutable.
    impl Div for Affine3
    impl Mul for Affine3
    impl Show for Affine3

    Affine3::approx

    fn Affine3::approx(self : Affine3, other : Affine3, eps? : Float) -> Bool

    Whether the two transforms agree to within eps element by element.

    Affine3::div

    fn Affine3::div(self : Affine3, other : Affine3) -> Affine3

    Affine3::equal

    fn Affine3::equal(Affine3, Affine3) -> Bool

    Affine3::from_axis_angle

    fn Affine3::from_axis_angle(axis : Vec3, radians : Float) -> Affine3

    The transform that turns by radians about axis, by the right-hand rule.

    Affine3::from_mat4

    fn Affine3::from_mat4(m : Mat4) -> Affine3?

    The affine transform a 4×4 matrix holds, or None when the matrix is not affine.

    Affine3::from_rotation

    fn Affine3::from_rotation(rotation : Quat) -> Affine3

    The transform that applies the rotation, about the origin.

    Affine3::from_scale

    fn Affine3::from_scale(scale : Vec3) -> Affine3

    The transform that scales each axis by the matching component of scale.

    Affine3::from_translation

    fn Affine3::from_translation(offset : Vec3) -> Affine3

    The transform that moves every point by offset.

    Affine3::from_trs

    fn Affine3::from_trs(trs : Trs) -> Affine3

    The transform that scales, then rotates, then moves: the three parts of a Trs.

    Affine3::identity

    fn Affine3::identity() -> Affine3

    The transform that changes nothing.

    Affine3::inverse

    fn Affine3::inverse(self : Affine3) -> Affine3?

    The transform that undoes this one, or None when the linear part is singular.

    Affine3::is_finite

    fn Affine3::is_finite(self : Affine3) -> Bool

    Whether every element is finite.

    Affine3::mul

    fn Affine3::mul(self : Affine3, other : Affine3) -> Affine3

    Affine3::new

    fn Affine3::new(linear : Mat3, translation : Vec3) -> Affine3

    The transform that maps v to linear * v + translation.

    Affine3::not_equal

    fn Affine3::not_equal(x : Affine3, y : Affine3) -> Bool

    Affine3::output

    fn Affine3::output(self : Affine3, logger : &Logger) -> Unit

    Affine3::to_mat4

    fn Affine3::to_mat4(self : Affine3) -> Mat4

    The same transform as a 4×4 matrix, with (0, 0, 0, 1) as its last row.

    Affine3::to_repr

    Affine3::to_string

    fn Affine3::to_string(self : Affine3) -> String

    Affine3::to_trs

    fn Affine3::to_trs(self : Affine3) -> Trs?

    The three parts the transform was built from, or None when its linear part is singular or has shear in it. See Mat4::to_trs.

    Affine3::transform_point

    fn Affine3::transform_point(self : Affine3, p : Vec3) -> Vec3

    The point the transform takes p to.

    Affine3::transform_vector

    fn Affine3::transform_vector(self : Affine3, v : Vec3) -> Vec3

    The direction the transform takes v to, without the translation.

    Color3

    pub(all) struct Color3 {
    r : Float
    g : Float
    b : Float
    } derive(Eq)

    A colour with three components, red, green and blue, and no alpha.

    The components are plain numbers: this library does the arithmetic — adding a light, blending two values, lerping a gradient — and leaves the encoding to the caller, who knows whether the numbers are linear or sRGB and whether they are in 0..1 or 0..255. The HSV conversion is rgb_to_hsv, with the hue in turns and the other two in 0..1.

    Values are immutable, and the operators work component by component.
    impl Add for Color3
    impl Div for Color3
    impl Mul for Color3
    impl Neg for Color3
    impl Show for Color3
    impl Sub for Color3

    Color3::add

    fn Color3::add(self : Color3, other : Color3) -> Color3

    Color3::approx

    fn Color3::approx(self : Color3, other : Color3, eps? : Float) -> Bool

    Whether the two colours agree to within eps component by component.

    Color3::clamp

    fn Color3::clamp(self : Color3, low : Color3, high : Color3) -> Color3

    The colour with every component pinned between low and high.

    Color3::div

    fn Color3::div(self : Color3, other : Color3) -> Color3

    Color3::equal

    fn Color3::equal(Color3, Color3) -> Bool

    Color3::is_finite

    fn Color3::is_finite(self : Color3) -> Bool

    Whether every component is finite.

    Color3::lerp

    fn Color3::lerp(self : Color3, other : Color3, t : Float) -> Color3

    The colour a fraction of the way from self to other.

    Color3::max

    fn Color3::max(self : Color3, other : Color3) -> Color3

    The component-wise larger of the two.

    Color3::min

    fn Color3::min(self : Color3, other : Color3) -> Color3

    The component-wise smaller of the two.

    Color3::mul

    fn Color3::mul(self : Color3, other : Color3) -> Color3

    Color3::neg

    fn Color3::neg(self : Color3) -> Color3

    Color3::new

    fn Color3::new(r : Float, g : Float, b : Float) -> Color3

    The colour from its three components.

    Color3::not_equal

    fn Color3::not_equal(x : Color3, y : Color3) -> Bool

    Color3::one

    fn Color3::one() -> Color3

    White: the most of every component.

    Color3::output

    fn Color3::output(self : Color3, logger : &Logger) -> Unit

    Color3::splat

    fn Color3::splat(value : Float) -> Color3

    The colour with the same value in all three components: a grey.

    Color3::sub

    fn Color3::sub(self : Color3, other : Color3) -> Color3

    Color3::to_array

    fn Color3::to_array(self : Color3) -> FixedArray[Float]

    The three components in order, [r, g, b], for a buffer.

    Color3::to_repr

    Color3::to_string

    fn Color3::to_string(self : Color3) -> String

    Color3::zero

    fn Color3::zero() -> Color3

    Black: no light at all.

    Color4

    pub(all) struct Color4 {
    r : Float
    g : Float
    b : Float
    a : Float
    } derive(Eq)

    A colour with alpha: the three components of Color3 and an opacity.

    The arithmetic is the same as Color3, with the fourth component carried along, so an alpha-premultiplied or straight-alpha pipeline can blend with the operators and decide the policy itself.

    Values are immutable.
    impl Add for Color4
    impl Div for Color4
    impl Mul for Color4
    impl Neg for Color4
    impl Show for Color4
    impl Sub for Color4

    Color4::add

    fn Color4::add(self : Color4, other : Color4) -> Color4

    Color4::approx

    fn Color4::approx(self : Color4, other : Color4, eps? : Float) -> Bool

    Whether the two colours agree to within eps component by component.

    Color4::div

    fn Color4::div(self : Color4, other : Color4) -> Color4

    Color4::equal

    fn Color4::equal(Color4, Color4) -> Bool

    Color4::from_color3

    fn Color4::from_color3(rgb : Color3, a : Float) -> Color4

    The colour of rgb at the opacity a.

    Color4::is_finite

    fn Color4::is_finite(self : Color4) -> Bool

    Whether every component is finite.

    Color4::lerp

    fn Color4::lerp(self : Color4, other : Color4, t : Float) -> Color4

    The colour a fraction of the way from self to other.

    Color4::max

    fn Color4::max(self : Color4, other : Color4) -> Color4

    The component-wise larger of the two.

    Color4::min

    fn Color4::min(self : Color4, other : Color4) -> Color4

    The component-wise smaller of the two.

    Color4::mul

    fn Color4::mul(self : Color4, other : Color4) -> Color4

    Color4::neg

    fn Color4::neg(self : Color4) -> Color4

    Color4::new

    fn Color4::new(r : Float, g : Float, b : Float, a : Float) -> Color4

    The colour from its four components.

    Color4::not_equal

    fn Color4::not_equal(x : Color4, y : Color4) -> Bool

    Color4::output

    fn Color4::output(self : Color4, logger : &Logger) -> Unit

    Color4::splat

    fn Color4::splat(value : Float) -> Color4

    The colour with the same value in every component.

    Color4::sub

    fn Color4::sub(self : Color4, other : Color4) -> Color4

    Color4::to_array

    fn Color4::to_array(self : Color4) -> FixedArray[Float]

    The four components in order, [r, g, b, a], for a buffer.

    Color4::to_color3

    fn Color4::to_color3(self : Color4) -> Color3

    The three colour components, dropping the alpha.

    Color4::to_repr

    Color4::to_string

    fn Color4::to_string(self : Color4) -> String

    EulerOrder

    pub(all) enum EulerOrder {
    Xyz
    Xzy
    Yxz
    Yzx
    Zxy
    Zyx
    Xyx
    Xzx
    Yxy
    Yzy
    Zxz
    Zyz
    } derive(Eq,
    Debug
    )

    The order the three Euler angles are applied in.

    The name says which axis each angle turns about, read left to right: Xyz turns about x, then about the new y, then about the new z. That is the intrinsic reading, the one an aircraft's roll, pitch and yaw use, and it is the same as applying the three turns about the fixed axes in the opposite order.

    Six orders turn about three different axes and six about two — the last group repeats the first axis and is what a turntable and a gyroscope do. Between them they cover every way to name a rotation by three angles; the choice is a convention of the file format or the editor that produced the angles, not of the arithmetic.

    EulerOrder::equal

    fn EulerOrder::equal(EulerOrder, EulerOrder) -> Bool

    EulerOrder::not_equal

    fn EulerOrder::not_equal(x : EulerOrder, y : EulerOrder) -> Bool

    Frustum

    pub struct Frustum {
    planes : FixedArray[Plane]
    }

    The six planes bounding what a camera can see, each facing inwards.

    A view-projection matrix describes the visible volume as a set of inequalities in clip space; each of the six becomes one plane in world space, and culling is then a signed distance per plane per object. That is the cheapest test that still rejects most of a scene, which is why it is the one every renderer starts with.

    The planes are built for a matrix whose depth range is 0..1. For an OpenGL-style matrix, whose near plane sits at -1, use from_matrix_gl.
    impl Eq for Frustum
    impl Show for Frustum

    Frustum::approx

    fn Frustum::approx(self : Frustum, other : Frustum, eps? : Float) -> Bool

    Whether two frusta have the same six planes to within eps.

    Frustum::contains_aabb

    fn Frustum::contains_aabb(self : Frustum, box : Aabb3) -> Bool

    Whether the box lies entirely inside the frustum.

    The corner furthest from each plane — the one the normal points away from — decides: if even that corner is inside, all eight are.

    Frustum::contains_point

    fn Frustum::contains_point(self : Frustum, point : Vec3) -> Bool

    Whether the point is inside all six planes, boundary included.

    Frustum::contains_sphere

    fn Frustum::contains_sphere(self : Frustum, sphere : Sphere) -> Bool

    Whether the sphere lies entirely inside the frustum.

    Frustum::equal

    fn Frustum::equal(self : Frustum, other : Frustum) -> Bool

    Frustum::from_matrix

    fn Frustum::from_matrix(m : Mat4) -> Frustum?

    The frustum of a view-projection matrix whose depth range is 0..1, or None when the matrix does not describe one.

    The six planes are the six clip-space inequalities pulled back into world space: the left plane is the row w + x of the matrix, and so on. A matrix that is singular or degenerate — a projection with no near plane, say — gives a plane with no normal, and there is no frustum to return.

    test {
    let projection = @moonmath.Mat4::perspective(
    @moonmath.half_pi, 1.0, 0.1, 100.0,
    )
    let frustum = @moonmath.Frustum::from_matrix(projection).unwrap()
    // A point just in front of the camera is inside, one behind it is not.
    assert_true(frustum.contains_point(@moonmath.Vec3::new(0.0, 0.0, -1.0)))
    assert_false(frustum.contains_point(@moonmath.Vec3::new(0.0, 0.0, 1.0)))
    }

    Frustum::from_matrix_gl

    fn Frustum::from_matrix_gl(m : Mat4) -> Frustum?

    The frustum of a view-projection matrix whose depth range is -1..1.

    Frustum::intersects_aabb

    fn Frustum::intersects_aabb(self : Frustum, box : Aabb3) -> Bool

    Whether the frustum overlaps any part of the box.

    The opposite corner from contains_aabb, for the opposite question: a box is out only when it lies wholly behind one plane.

    Frustum::intersects_line

    fn Frustum::intersects_line(self : Frustum, line : Line3) -> Bool

    Whether the infinite line meets the frustum at all.

    Each of the six planes is a linear inequality in the line's parameter, so the part of the line inside the frustum is the intersection of six half-lines: one interval. The line meets the frustum exactly when that interval is not empty, which is the honest test an infinite line needs — unlike a ray, it has no start that could already be outside one plane while inside another.

    Frustum::intersects_sphere

    fn Frustum::intersects_sphere(self : Frustum, sphere : Sphere) -> Bool

    Whether the frustum overlaps any part of the sphere.

    Frustum::not_equal

    fn Frustum::not_equal(x : Frustum, y : Frustum) -> Bool

    Frustum::output

    fn Frustum::output(self : Frustum, logger : &Logger) -> Unit

    Frustum::plane

    fn Frustum::plane(self : Frustum, index : Int) -> Plane

    The plane at index, counted from zero in the order left, right, bottom, top, near, far.

    Frustum::planes

    fn Frustum::planes(self : Frustum) -> FixedArray[Plane]

    The planes of the frustum, in the order left, right, bottom, top, near, far.

    A copy: the array the frustum holds cannot be reached from outside, so a caller cannot break the six-plane invariant by writing into it.

    Frustum::to_repr

    Frustum::to_string

    fn Frustum::to_string(self : Frustum) -> String

    Half

    pub struct Half {
    bits : UInt16
    } derive(Eq)

    A 16-bit IEEE 754 binary16 value: the half precision a texture, a packed vertex or a GPU buffer carries when thirty-two bits are more than the data needs and sixteen are enough.

    The value is the sixteen bits themselves, so a half read from a buffer is from_bits and one written back is to_bits. Arithmetic runs at Float, the precision the rest of this library works in: a half is a storage format, not a second arithmetic. Conversion rounds to nearest with ties to even, as the standard and the graphics formats do, and the special values — signed zeros, the two infinities, a quiet NaN and the subnormals — survive the round trip.

    Fixed at sixteen bits, this type has no double-precision counterpart; it lives only in the single-precision package.
    impl Show for Half
    impl Debug for Half

    Half::approx

    fn Half::approx(self : Half, other : Half, eps? : Float) -> Bool

    Whether the two halves hold the same value to within eps, compared at Float precision.

    Half::denorm_min

    fn Half::denorm_min() -> Half

    The smallest positive subnormal, 2^-24.

    Half::epsilon

    fn Half::epsilon() -> Half

    The smallest number that, added to one, gives a half different from one: 2^-10.

    Half::equal

    fn Half::equal(Half, Half) -> Bool

    Half::from_bits

    fn Half::from_bits(bits : UInt16) -> Half

    The half made of the sixteen raw bits, as a buffer holds it.

    Half::from_float

    fn Half::from_float(value : Float) -> Half

    The 16-bit encoding of value, rounded to the nearest representable half, ties to even.

    Values beyond the largest finite half become an infinity of the same sign; values below the smallest subnormal become a signed zero; a NaN stays a NaN.

    Half::is_denormal

    fn Half::is_denormal(self : Half) -> Bool

    Whether the half is a subnormal: non-zero, smaller than the smallest normal.

    Half::is_finite

    fn Half::is_finite(self : Half) -> Bool

    Whether the half is neither an infinity nor a NaN.

    Half::is_inf

    fn Half::is_inf(self : Half) -> Bool

    Whether the half is an infinity, of either sign.

    Half::is_nan

    fn Half::is_nan(self : Half) -> Bool

    Whether the half is a NaN.

    Half::is_normal

    fn Half::is_normal(self : Half) -> Bool

    Whether the half is a normal number: not zero, not subnormal, not infinite.

    Half::lowest

    fn Half::lowest() -> Half

    The most negative finite half, -65504.

    Half::max

    fn Half::max() -> Half

    The largest finite half, 65504.

    Half::min

    fn Half::min() -> Half

    The smallest positive normal, 2^-14.

    Half::nan

    fn Half::nan() -> Half

    A quiet NaN.

    Half::neg_infinity

    fn Half::neg_infinity() -> Half

    Negative infinity.

    Half::not_equal

    fn Half::not_equal(x : Half, y : Half) -> Bool

    Half::one

    fn Half::one() -> Half

    One.

    Half::output

    fn Half::output(self : Half, logger : &Logger) -> Unit

    Half::pos_infinity

    fn Half::pos_infinity() -> Half

    Positive infinity.

    Half::to_bits

    fn Half::to_bits(self : Half) -> UInt16

    The sixteen raw bits, in the order a buffer expects.

    Half::to_float

    fn Half::to_float(self : Half) -> Float

    The Float the half stands for.

    Exact: every half is a Float, though not every Float is a half.

    Half::to_repr

    Half::to_string

    fn Half::to_string(self : Half) -> String

    Half::zero

    fn Half::zero() -> Half

    Positive zero.

    IVec2

    pub(all) struct IVec2 {
    x : Int
    y : Int
    } derive(Eq, Hash)

    Two whole numbers: a grid cell or a pixel coordinate.

    Division is integer division and truncates towards zero, and dividing by zero aborts, as it does for Int itself.
    impl Add for IVec2
    impl Div for IVec2
    impl Mul for IVec2
    impl Neg for IVec2
    impl Show for IVec2
    impl Sub for IVec2

    IVec2::abs

    fn IVec2::abs(self : IVec2) -> IVec2

    The pair with every component replaced by its absolute value.

    IVec2::add

    fn IVec2::add(self : IVec2, other : IVec2) -> IVec2

    IVec2::as_vec2

    fn IVec2::as_vec2(self : IVec2) -> Vec2

    The same cell as a floating-point vector, exactly.

    IVec2::clamp

    fn IVec2::clamp(self : IVec2, low : IVec2, high : IVec2) -> IVec2

    The pair with every component pinned between low and high, inclusive.

    IVec2::div

    fn IVec2::div(self : IVec2, other : IVec2) -> IVec2

    IVec2::dot

    fn IVec2::dot(self : IVec2, other : IVec2) -> Int

    The dot product, in whole numbers.

    IVec2::equal

    fn IVec2::equal(IVec2, IVec2) -> Bool

    IVec2::from_vec2

    fn IVec2::from_vec2(value : Vec2) -> IVec2

    The cell a floating-point position falls in, truncated towards zero.

    Truncation rather than rounding, because a pixel coordinate of -0.5 is in cell 0 when the cell is counted from the point, and the caller that wants the floor asks for floor first.

    IVec2::hash

    fn IVec2::hash(self : IVec2) -> Int

    IVec2::hash_combine

    fn IVec2::hash_combine(IVec2, Hasher) -> Unit

    IVec2::max

    fn IVec2::max(self : IVec2, other : IVec2) -> IVec2

    The component-wise larger of the two.

    IVec2::min

    fn IVec2::min(self : IVec2, other : IVec2) -> IVec2

    The component-wise smaller of the two.

    IVec2::mul

    fn IVec2::mul(self : IVec2, other : IVec2) -> IVec2

    IVec2::neg

    fn IVec2::neg(self : IVec2) -> IVec2

    IVec2::new

    fn IVec2::new(x : Int, y : Int) -> IVec2

    The pair (x, y).

    IVec2::not_equal

    fn IVec2::not_equal(x : IVec2, y : IVec2) -> Bool

    IVec2::one

    fn IVec2::one() -> IVec2

    One in both components: the step between neighbouring cells.

    IVec2::output

    fn IVec2::output(self : IVec2, logger : &Logger) -> Unit

    IVec2::splat

    fn IVec2::splat(value : Int) -> IVec2

    The same value in both components.

    IVec2::sub

    fn IVec2::sub(self : IVec2, other : IVec2) -> IVec2

    IVec2::to_array

    fn IVec2::to_array(self : IVec2) -> FixedArray[Int]

    The components in order, [x, y].

    IVec2::to_repr

    IVec2::to_string

    fn IVec2::to_string(self : IVec2) -> String

    IVec2::zero

    fn IVec2::zero() -> IVec2

    The origin of the grid.

    IVec3

    pub(all) struct IVec3 {
    x : Int
    y : Int
    z : Int
    } derive(Eq, Hash)

    Three whole numbers: a cell in a voxel grid.
    impl Add for IVec3
    impl Div for IVec3
    impl Mul for IVec3
    impl Neg for IVec3
    impl Show for IVec3
    impl Sub for IVec3

    IVec3::abs

    fn IVec3::abs(self : IVec3) -> IVec3

    The triple with every component replaced by its absolute value.

    IVec3::add

    fn IVec3::add(self : IVec3, other : IVec3) -> IVec3

    IVec3::as_vec3

    fn IVec3::as_vec3(self : IVec3) -> Vec3

    The same cell as a floating-point vector, exactly.

    IVec3::clamp

    fn IVec3::clamp(self : IVec3, low : IVec3, high : IVec3) -> IVec3

    The triple with every component pinned between low and high, inclusive.

    IVec3::div

    fn IVec3::div(self : IVec3, other : IVec3) -> IVec3

    IVec3::dot

    fn IVec3::dot(self : IVec3, other : IVec3) -> Int

    The dot product, in whole numbers.

    IVec3::equal

    fn IVec3::equal(IVec3, IVec3) -> Bool

    IVec3::from_vec3

    fn IVec3::from_vec3(value : Vec3) -> IVec3

    The cell a floating-point position falls in, truncated towards zero.

    IVec3::hash

    fn IVec3::hash(self : IVec3) -> Int

    IVec3::hash_combine

    fn IVec3::hash_combine(IVec3, Hasher) -> Unit

    IVec3::max

    fn IVec3::max(self : IVec3, other : IVec3) -> IVec3

    The component-wise larger of the two.

    IVec3::min

    fn IVec3::min(self : IVec3, other : IVec3) -> IVec3

    The component-wise smaller of the two.

    IVec3::mul

    fn IVec3::mul(self : IVec3, other : IVec3) -> IVec3

    IVec3::neg

    fn IVec3::neg(self : IVec3) -> IVec3

    IVec3::new

    fn IVec3::new(x : Int, y : Int, z : Int) -> IVec3

    The triple (x, y, z).

    IVec3::not_equal

    fn IVec3::not_equal(x : IVec3, y : IVec3) -> Bool

    IVec3::one

    fn IVec3::one() -> IVec3

    One in every component.

    IVec3::output

    fn IVec3::output(self : IVec3, logger : &Logger) -> Unit

    IVec3::splat

    fn IVec3::splat(value : Int) -> IVec3

    The same value in all three components.

    IVec3::sub

    fn IVec3::sub(self : IVec3, other : IVec3) -> IVec3

    IVec3::to_array

    fn IVec3::to_array(self : IVec3) -> FixedArray[Int]

    The components in order, [x, y, z].

    IVec3::to_repr

    IVec3::to_string

    fn IVec3::to_string(self : IVec3) -> String

    IVec3::zero

    fn IVec3::zero() -> IVec3

    The origin of the grid.

    IVec4

    pub(all) struct IVec4 {
    x : Int
    y : Int
    z : Int
    w : Int
    } derive(Eq, Hash)

    Four whole numbers.
    impl Add for IVec4
    impl Div for IVec4
    impl Mul for IVec4
    impl Neg for IVec4
    impl Show for IVec4
    impl Sub for IVec4

    IVec4::abs

    fn IVec4::abs(self : IVec4) -> IVec4

    The quadruple with every component replaced by its absolute value.

    IVec4::add

    fn IVec4::add(self : IVec4, other : IVec4) -> IVec4

    IVec4::as_vec4

    fn IVec4::as_vec4(self : IVec4) -> Vec4

    The same values as a floating-point vector, exactly.

    IVec4::clamp

    fn IVec4::clamp(self : IVec4, low : IVec4, high : IVec4) -> IVec4

    The quadruple with every component pinned between low and high.

    IVec4::div

    fn IVec4::div(self : IVec4, other : IVec4) -> IVec4

    IVec4::dot

    fn IVec4::dot(self : IVec4, other : IVec4) -> Int

    The dot product, in whole numbers.

    IVec4::equal

    fn IVec4::equal(IVec4, IVec4) -> Bool

    IVec4::from_vec4

    fn IVec4::from_vec4(value : Vec4) -> IVec4

    The values a floating-point vector carries, truncated towards zero.

    IVec4::hash

    fn IVec4::hash(self : IVec4) -> Int

    IVec4::hash_combine

    fn IVec4::hash_combine(IVec4, Hasher) -> Unit

    IVec4::max

    fn IVec4::max(self : IVec4, other : IVec4) -> IVec4

    The component-wise larger of the two.

    IVec4::min

    fn IVec4::min(self : IVec4, other : IVec4) -> IVec4

    The component-wise smaller of the two.

    IVec4::mul

    fn IVec4::mul(self : IVec4, other : IVec4) -> IVec4

    IVec4::neg

    fn IVec4::neg(self : IVec4) -> IVec4

    IVec4::new

    fn IVec4::new(x : Int, y : Int, z : Int, w : Int) -> IVec4

    The quadruple (x, y, z, w).

    IVec4::not_equal

    fn IVec4::not_equal(x : IVec4, y : IVec4) -> Bool

    IVec4::one

    fn IVec4::one() -> IVec4

    One in every component.

    IVec4::output

    fn IVec4::output(self : IVec4, logger : &Logger) -> Unit

    IVec4::splat

    fn IVec4::splat(value : Int) -> IVec4

    The same value in all four components.

    IVec4::sub

    fn IVec4::sub(self : IVec4, other : IVec4) -> IVec4

    IVec4::to_array

    fn IVec4::to_array(self : IVec4) -> FixedArray[Int]

    The components in order, [x, y, z, w].

    IVec4::to_repr

    IVec4::to_string

    fn IVec4::to_string(self : IVec4) -> String

    IVec4::zero

    fn IVec4::zero() -> IVec4

    The zero vector.

    Interval

    pub(all) struct Interval {
    min : Float
    max : Float
    } derive(Eq)

    A one-dimensional range between two numbers, min at or below max.

    It is the one-dimensional case of an axis-aligned box, and what a timeline, an exposure or a depth range is. An empty interval — no numbers at all — is the one with min above max, which is how the union of nothing starts and how empty builds it; every operation here treats it as the identity the set operations expect.

    Values are immutable.
    impl Show for Interval

    Interval::approx

    fn Interval::approx(self : Interval, other : Interval, eps? : Float) -> Bool

    Whether the two intervals agree end for end to within eps.

    Interval::center

    fn Interval::center(self : Interval) -> Float

    The midpoint of the interval.

    Interval::contains

    fn Interval::contains(self : Interval, value : Float) -> Bool

    Whether value lies in the interval, ends included.

    Interval::empty

    fn Interval::empty() -> Interval

    The interval with no numbers in it, which every union starts from.

    Interval::equal

    fn Interval::equal(Interval, Interval) -> Bool

    Interval::expand

    fn Interval::expand(self : Interval, amount : Float) -> Interval

    The interval grown by amount at both ends; a negative amount shrinks it.

    Interval::extend_by

    fn Interval::extend_by(self : Interval, value : Float) -> Interval

    The interval grown to include value; the empty interval becomes [value,value].

    Interval::intersection

    fn Interval::intersection(self : Interval, other : Interval) -> Interval?

    The overlap of the two, or None when they do not meet.

    Interval::intersects

    fn Interval::intersects(self : Interval, other : Interval) -> Bool

    Whether the two intervals share at least one point.

    Interval::is_empty

    fn Interval::is_empty(self : Interval) -> Bool

    Whether the interval holds no numbers: min above max.

    Interval::is_finite

    fn Interval::is_finite(self : Interval) -> Bool

    Whether both ends are finite.

    Interval::new

    fn Interval::new(min : Float, max : Float) -> Interval

    The interval from min to max; the two are not reordered, so passing them the other way round gives the empty interval.

    Interval::not_equal

    fn Interval::not_equal(x : Interval, y : Interval) -> Bool

    Interval::output

    fn Interval::output(self : Interval, logger : &Logger) -> Unit

    Interval::size

    fn Interval::size(self : Interval) -> Float

    The distance from min to max, or zero for the empty interval.

    Interval::to_repr

    Interval::to_string

    fn Interval::to_string(self : Interval) -> String

    Interval::union

    fn Interval::union(self : Interval, other : Interval) -> Interval

    The smallest interval holding both; the empty interval is the identity.

    Line3

    pub(all) struct Line3 {
    point : Vec3
    direction : Vec3
    } derive(Eq)

    An infinite line in space: a point and a direction.

    A ray has a start and runs one way; a line has neither end, which is what a construction axis, a normal or the intersection of two planes is. The direction need not be a unit vector — the results are the same whatever its length — but a zero direction names no line, and every operation treats it as the single point point.

    Values are immutable.
    impl Show for Line3

    Line3::approx

    fn Line3::approx(self : Line3, other : Line3, eps? : Float) -> Bool

    Whether the two lines have the same point and direction to within eps.

    Line3::at

    fn Line3::at(self : Line3, t : Float) -> Vec3

    The point t of the direction from point; t may be any number.

    Line3::closest_point_to

    fn Line3::closest_point_to(self : Line3, other : Vec3) -> Vec3

    The point of the line closest to other.

    The perpendicular foot: the vector from it to other is perpendicular to the direction. A zero direction leaves the line a point, and the answer is that point.

    Line3::closest_point_to_line

    fn Line3::closest_point_to_line(self : Line3, other : Line3) -> Vec3

    The point of this line closest to the other line.

    For two lines that meet or are skew, the closest points are the pair joined by the common perpendicular; this is the one on this line. Parallel lines have a whole family of closest pairs and the foot from the other line's point is returned. A degenerate line — a zero direction — gives its point.

    Line3::distance_to_line

    fn Line3::distance_to_line(self : Line3, other : Line3) -> Float

    The distance between the two lines: zero when they meet.

    Line3::distance_to_point

    fn Line3::distance_to_point(self : Line3, other : Vec3) -> Float

    The distance from the line to the point.

    Line3::equal

    fn Line3::equal(Line3, Line3) -> Bool

    Line3::intersect_plane

    fn Line3::intersect_plane(self : Line3, plane : Plane) -> Vec3?

    Where the line cuts the plane, or None when it runs parallel to it.

    A line has no ends, so any crossing counts, on either side of point.

    Line3::is_finite

    fn Line3::is_finite(self : Line3) -> Bool

    Whether the point and the direction are finite.

    Line3::new

    fn Line3::new(point : Vec3, direction : Vec3) -> Line3

    The line through point in direction.

    Line3::not_equal

    fn Line3::not_equal(x : Line3, y : Line3) -> Bool

    Line3::output

    fn Line3::output(self : Line3, logger : &Logger) -> Unit

    Line3::to_repr

    Line3::to_string

    fn Line3::to_string(self : Line3) -> String

    Mat2

    pub(all) struct Mat2 {
    c0 : Vec2
    c1 : Vec2
    } derive(Eq)

    A 2×2 matrix, stored as two column vectors.

    Column-major, like every matrix here: c0 is the first column, so the matrix that maps x to (a, c) and y to (b, d) is c0 = (a, c), c1 = (b, d), and its x and y are the first and second rows. WGSL writes a mat2x2<f32> in the same order, so to_array goes straight into a buffer.

    Values are immutable.
    impl Add for Mat2
    impl Div for Mat2
    impl Mul for Mat2
    impl Neg for Mat2
    impl Show for Mat2
    impl Sub for Mat2

    Mat2::add

    fn Mat2::add(self : Mat2, other : Mat2) -> Mat2

    Mat2::approx

    fn Mat2::approx(self : Mat2, other : Mat2, eps? : Float) -> Bool

    Whether the two matrices agree to within eps element by element.

    Mat2::col

    fn Mat2::col(self : Mat2, index : Int) -> Vec2

    Column index, counted from zero.

    Mat2::determinant

    fn Mat2::determinant(self : Mat2) -> Float

    The signed area the matrix scales by: zero exactly when it is singular.

    Mat2::div

    fn Mat2::div(self : Mat2, other : Mat2) -> Mat2

    Mat2::equal

    fn Mat2::equal(Mat2, Mat2) -> Bool

    Mat2::from_angle

    fn Mat2::from_angle(radians : Float) -> Mat2

    The matrix that rotates counter-clockwise by radians.

    Mat2::from_array

    fn Mat2::from_array(values : FixedArray[Float]) -> Mat2

    The matrix from four values in column-major order: [c0.x, c0.y, c1.x, c1.y].

    This is the order a mat2x2<f32> sits in a GPU buffer in, so a matrix read back out of one goes in here unchanged. Aborts if the array does not hold four values.

    Mat2::from_cols

    fn Mat2::from_cols(c0 : Vec2, c1 : Vec2) -> Mat2

    The matrix with c0 and c1 as its columns.

    Mat2::from_rows

    fn Mat2::from_rows(r0 : Vec2, r1 : Vec2) -> Mat2

    The matrix with r0 and r1 as its rows.

    Mat2::from_scale

    fn Mat2::from_scale(s : Vec2) -> Mat2

    The matrix that scales x by s.x and y by s.y.

    Mat2::get

    fn Mat2::get(self : Mat2, row_index : Int, column_index : Int) -> Float

    The element at row and column, both counted from zero.

    Mat2::identity

    fn Mat2::identity() -> Mat2

    The identity: the matrix that changes nothing.

    Mat2::inverse

    fn Mat2::inverse(self : Mat2) -> Mat2?

    The matrix that undoes this one, or None when there is none.

    A singular matrix — one with a zero determinant — flattens the plane onto a line or a point, and nothing undoes that. None is the honest answer; the caller decides whether that is an error or a case to handle.

    Mat2::is_finite

    fn Mat2::is_finite(self : Mat2) -> Bool

    Whether every element is finite.

    Mat2::mul

    fn Mat2::mul(self : Mat2, other : Mat2) -> Mat2

    Mat2::mul_vec2

    fn Mat2::mul_vec2(self : Mat2, v : Vec2) -> Vec2

    The vector the matrix takes v to.

    Written as one expression per component rather than as the sum of two scaled columns: a Vec2 is a heap value in the native backend, so every intermediate column is an allocation, and there are two of them here for each result.

    Mat2::neg

    fn Mat2::neg(self : Mat2) -> Mat2

    Mat2::not_equal

    fn Mat2::not_equal(x : Mat2, y : Mat2) -> Bool

    Mat2::output

    fn Mat2::output(self : Mat2, logger : &Logger) -> Unit

    Mat2::row

    fn Mat2::row(self : Mat2, index : Int) -> Vec2

    Row index, counted from zero.

    Mat2::sub

    fn Mat2::sub(self : Mat2, other : Mat2) -> Mat2

    Mat2::to_angle

    fn Mat2::to_angle(self : Mat2) -> Float

    The angle, in radians, the matrix rotates x by.

    Only meaningful for a rotation matrix — one built by from_angle, or a linear part that happens to be one; on anything else it reads the direction of the first column.

    Mat2::to_array

    fn Mat2::to_array(self : Mat2) -> FixedArray[Float]

    The elements in column-major order, [c0.x, c0.y, c1.x, c1.y], ready for a buffer.

    Mat2::to_repr

    Mat2::to_string

    fn Mat2::to_string(self : Mat2) -> String

    Mat2::transpose

    fn Mat2::transpose(self : Mat2) -> Mat2

    The matrix with rows and columns exchanged.

    Mat2::zero

    fn Mat2::zero() -> Mat2

    The zero matrix: the one that sends everything to the origin.

    Mat3

    pub(all) struct Mat3 {
    c0 : Vec3
    c1 : Vec3
    c2 : Vec3
    } derive(Eq)

    A 3×3 matrix: a linear map of space, or the upper-left block of a 4×4 one.

    Column-major, as mat3x3<f32> is in a buffer: c0 is the first column, and the rows are read across the columns. Rotations, scalings and shears are linear maps, so they need no translation column and live here rather than in Affine3.

    Values are immutable.
    impl Add for Mat3
    impl Div for Mat3
    impl Mul for Mat3
    impl Neg for Mat3
    impl Show for Mat3
    impl Sub for Mat3

    Mat3::add

    fn Mat3::add(self : Mat3, other : Mat3) -> Mat3

    Mat3::approx

    fn Mat3::approx(self : Mat3, other : Mat3, eps? : Float) -> Bool

    Whether the two matrices agree to within eps element by element.

    Mat3::col

    fn Mat3::col(self : Mat3, index : Int) -> Vec3

    Column index, counted from zero.

    Mat3::determinant

    fn Mat3::determinant(self : Mat3) -> Float

    The signed volume the matrix scales by: zero exactly when it is singular.

    Mat3::div

    fn Mat3::div(self : Mat3, other : Mat3) -> Mat3

    Mat3::equal

    fn Mat3::equal(Mat3, Mat3) -> Bool

    Mat3::from_array

    fn Mat3::from_array(values : FixedArray[Float]) -> Mat3

    The matrix from nine values in column-major order.

    Aborts if the array does not hold nine values.

    Mat3::from_axis_angle

    fn Mat3::from_axis_angle(axis : Vec3, radians : Float) -> Mat3

    The rotation of radians about axis, by the right-hand rule.

    axis is normalized first; a zero axis has no direction to turn about and gives the identity.

    Mat3::from_cols

    fn Mat3::from_cols(c0 : Vec3, c1 : Vec3, c2 : Vec3) -> Mat3

    The matrix with the three vecs as its columns.

    Mat3::from_euler

    fn Mat3::from_euler(order : EulerOrder, angles : Vec3) -> Mat3

    The rotation the three angles describe, taken in order, read by axis.

    The components are the turns about x, y and z, in radians, wherever in the order those axes turn up; see Quat::from_euler, which also says which orders this reading cannot express. from_euler_turns takes the turns in the order the order names them and covers all twelve.

    Mat3::from_euler_turns

    fn Mat3::from_euler_turns(order : EulerOrder, turns : Turns) -> Mat3

    The rotation the three turns describe, in the order the order names them.

    Mat3::from_mat4

    fn Mat3::from_mat4(m : Mat4) -> Mat3

    The upper-left 3×3 block of a 4×4 matrix: the linear part, without the translation.

    Mat3::from_quat

    fn Mat3::from_quat(q : Quat) -> Mat3

    The rotation the quaternion holds.

    Mat3::from_rows

    fn Mat3::from_rows(r0 : Vec3, r1 : Vec3, r2 : Vec3) -> Mat3

    The matrix with the three vecs as its rows.

    Mat3::from_scale

    fn Mat3::from_scale(s : Vec3) -> Mat3

    The matrix that scales each axis by the matching component of s.

    Mat3::get

    fn Mat3::get(self : Mat3, row_index : Int, column_index : Int) -> Float

    The element at row and column, both counted from zero.

    Mat3::identity

    fn Mat3::identity() -> Mat3

    The identity: the matrix that changes nothing.

    Mat3::inverse

    fn Mat3::inverse(self : Mat3) -> Mat3?

    The matrix that undoes this one, or None when there is none.

    Mat3::is_finite

    fn Mat3::is_finite(self : Mat3) -> Bool

    Whether every element is finite.

    Mat3::mul

    fn Mat3::mul(self : Mat3, other : Mat3) -> Mat3

    Mat3::mul_vec3

    fn Mat3::mul_vec3(self : Mat3, v : Vec3) -> Vec3

    The vector the matrix takes v to.

    One expression per component, for the reason given at Mat2::mul_vec2: the three scaled columns a sum would build are three allocations on the native backend.

    Mat3::neg

    fn Mat3::neg(self : Mat3) -> Mat3

    Mat3::not_equal

    fn Mat3::not_equal(x : Mat3, y : Mat3) -> Bool

    Mat3::output

    fn Mat3::output(self : Mat3, logger : &Logger) -> Unit

    Mat3::row

    fn Mat3::row(self : Mat3, index : Int) -> Vec3

    Row index, counted from zero.

    Mat3::sub

    fn Mat3::sub(self : Mat3, other : Mat3) -> Mat3

    Mat3::to_array

    fn Mat3::to_array(self : Mat3) -> FixedArray[Float]

    The elements in column-major order, ready for a buffer.

    Mat3::to_repr

    Mat3::to_string

    fn Mat3::to_string(self : Mat3) -> String

    Mat3::transpose

    fn Mat3::transpose(self : Mat3) -> Mat3

    The matrix with rows and columns exchanged.

    This is also the inverse of a rotation matrix, which is the cheapest way to undo one.

    Mat3::zero

    fn Mat3::zero() -> Mat3

    The zero matrix: the one that sends everything to the origin.

    Mat4

    pub(all) struct Mat4 {
    c0 : Vec4
    c1 : Vec4
    c2 : Vec4
    c3 : Vec4
    } derive(Eq)

    A 4×4 matrix: everything a 3×3 matrix does, plus the translation a transformation of space needs.

    Column-major, so c3 is the translation column and to_array is exactly the sixteen floats a mat4x4<f32> occupies in a storage or uniform buffer. A point is a Vec4 with w = 1, a direction one with w = 0, and the fourth row of a projection matrix puts the perspective divide into w.

    Values are immutable.
    impl Add for Mat4
    impl Div for Mat4
    impl Mul for Mat4
    impl Neg for Mat4
    impl Show for Mat4
    impl Sub for Mat4

    Mat4::add

    fn Mat4::add(self : Mat4, other : Mat4) -> Mat4

    Mat4::approx

    fn Mat4::approx(self : Mat4, other : Mat4, eps? : Float) -> Bool

    Whether the two matrices agree to within eps element by element.

    Mat4::col

    fn Mat4::col(self : Mat4, index : Int) -> Vec4

    Column index, counted from zero.

    Mat4::determinant

    fn Mat4::determinant(self : Mat4) -> Float

    The signed hypervolume the matrix scales by: zero exactly when it is singular.

    Mat4::div

    fn Mat4::div(self : Mat4, other : Mat4) -> Mat4

    Mat4::equal

    fn Mat4::equal(Mat4, Mat4) -> Bool

    Mat4::from_array

    fn Mat4::from_array(values : FixedArray[Float]) -> Mat4

    The matrix from sixteen values in column-major order — the order a mat4x4<f32> sits in a buffer in.

    Aborts if the array does not hold sixteen values.

    Mat4::from_axis_angle

    fn Mat4::from_axis_angle(axis : Vec3, radians : Float) -> Mat4

    The rotation of radians about axis, by the right-hand rule.

    Mat4::from_cols

    fn Mat4::from_cols(c0 : Vec4, c1 : Vec4, c2 : Vec4, c3 : Vec4) -> Mat4

    The matrix with the four vecs as its columns.

    Mat4::from_euler

    fn Mat4::from_euler(order : EulerOrder, angles : Vec3) -> Mat4

    The rotation the three angles describe, taken in order, in radians, read by axis. See Quat::from_euler for how the components are read and which orders that reading cannot express.

    Mat4::from_euler_turns

    fn Mat4::from_euler_turns(order : EulerOrder, turns : Turns) -> Mat4

    The rotation the three turns describe, in the order the order names them.

    Mat4::from_mat3

    fn Mat4::from_mat3(m : Mat3) -> Mat4

    The matrix that does what the 3×3 matrix does to x, y and z, and leaves w alone.

    Mat4::from_quat

    fn Mat4::from_quat(q : Quat) -> Mat4

    The rotation the quaternion holds.

    Mat4::from_rows

    fn Mat4::from_rows(r0 : Vec4, r1 : Vec4, r2 : Vec4, r3 : Vec4) -> Mat4

    The matrix with the four vecs as its rows.

    Mat4::from_scale

    fn Mat4::from_scale(s : Vec3) -> Mat4

    The matrix that scales each axis by the matching component of s.

    Mat4::from_translation

    fn Mat4::from_translation(t : Vec3) -> Mat4

    The matrix that moves every point by t.

    Mat4::from_trs

    fn Mat4::from_trs(trs : Trs) -> Mat4

    The matrix that scales by scale, then rotates by rotation, then moves by translation.

    test {
    let scale : @moonmath.Vec3 = @moonmath.Vec3::splat(2.0)
    let offset = @moonmath.Vec3::new(1.0, 0.0, 0.0)
    let m = @moonmath.Mat4::from_trs(
    @moonmath.Trs::new(offset, @moonmath.Quat::identity(), scale),
    )
    // One, scaled by two, then moved one along x.
    assert_true(
    m
    .transform_point(@moonmath.Vec3::one())
    .approx(@moonmath.Vec3::new(3.0, 2.0, 2.0)),
    )
    }

    Mat4::frustum

    fn Mat4::frustum(left : Float, right : Float, bottom : Float, top : Float, near : Float, far : Float) -> Mat4

    A perspective projection from an explicit frustum with the depth range 0..1.

    The rectangle on the near plane becomes the full clip volume, so the six numbers are all a camera needs: left and bottom are negative, right and top positive for a centred view, and offsetting them is what a lens shift does.

    Mat4::frustum_gl

    fn Mat4::frustum_gl(left : Float, right : Float, bottom : Float, top : Float, near : Float, far : Float) -> Mat4

    A perspective projection from an explicit frustum with the depth range -1..1.

    Mat4::get

    fn Mat4::get(self : Mat4, row_index : Int, column_index : Int) -> Float

    The element at row and column, both counted from zero.

    Mat4::identity

    fn Mat4::identity() -> Mat4

    The identity: the matrix that changes nothing.

    Mat4::inverse

    fn Mat4::inverse(self : Mat4) -> Mat4?

    The matrix that undoes this one, or None when there is none.

    The usual use is the inverse of a camera's view-projection matrix, and the usual singular case is a projection built with far equal to near.

    Mat4::is_finite

    fn Mat4::is_finite(self : Mat4) -> Bool

    Whether every element is finite.

    Mat4::look_at

    fn Mat4::look_at(eye : Vec3, target : Vec3, up : Vec3) -> Mat4

    The view matrix of a camera at eye looking at target, with up telling it which way is up.

    Right-handed: the camera looks down its own -z, so the third column of the result is eye - target, normalized. up need not be perpendicular to the view direction, only not parallel to it; when it is parallel there is no answer and the identity comes back rather than a matrix full of NaNs.

    Mat4::mul

    fn Mat4::mul(self : Mat4, other : Mat4) -> Mat4

    Mat4::mul_vec4

    fn Mat4::mul_vec4(self : Mat4, v : Vec4) -> Vec4

    The vector the matrix takes v to, without any division by w.

    This is what a shader's matrix * vector does: the projection divide happens after clipping, not here. One expression per component, for the reason given at Mat2::mul_vec2.

    Mat4::neg

    fn Mat4::neg(self : Mat4) -> Mat4

    Mat4::not_equal

    fn Mat4::not_equal(x : Mat4, y : Mat4) -> Bool

    Mat4::orthographic

    fn Mat4::orthographic(left : Float, right : Float, bottom : Float, top : Float, near : Float, far : Float) -> Mat4

    An orthographic projection with the depth range 0..1: the box the six planes bound becomes the unit cube in clip space.

    Right-handed, so the camera looks down -z and near and far are distances in front of it, both positive. A box with left == right, bottom == top or near == far has no volume to map and gives the identity.

    Mat4::orthographic_gl

    fn Mat4::orthographic_gl(left : Float, right : Float, bottom : Float, top : Float, near : Float, far : Float) -> Mat4

    An orthographic projection with the depth range -1..1.

    Mat4::output

    fn Mat4::output(self : Mat4, logger : &Logger) -> Unit

    Mat4::perspective

    fn Mat4::perspective(fov_y : Float, aspect : Float, near : Float, far : Float) -> Mat4

    A perspective projection with the depth range 0..1: the near plane maps to zero and the far plane to one, which is what WebGPU clips against.

    fov_y is the vertical field of view in radians and aspect the width divided by the height. Right-handed: the camera looks down -z, so near and far are distances in front of it, both positive.

    test {
    let m = @moonmath.Mat4::perspective(@moonmath.half_pi, 1.0, 0.1, 100.0)
    // The near plane lands on depth 0 and the far plane on depth 1.
    let near = m.mul_vec4(@moonmath.Vec4::new(0.0, 0.0, -0.1, 1.0))
    assert_true(@moonmath.approx(near.z / near.w, 0.0))
    let far = m.mul_vec4(@moonmath.Vec4::new(0.0, 0.0, -100.0, 1.0))
    assert_true(@moonmath.approx(far.z / far.w, 1.0))
    }

    Mat4::perspective_gl

    fn Mat4::perspective_gl(fov_y : Float, aspect : Float, near : Float, far : Float) -> Mat4

    A perspective projection with the depth range -1..1, for a pipeline that clips like OpenGL.

    The same as perspective in every other respect; only the third row differs.

    Mat4::perspective_infinite

    fn Mat4::perspective_infinite(fov_y : Float, aspect : Float, near : Float) -> Mat4

    A perspective projection with the depth range 0..1 and no far plane.

    Precision is better spent near the eye: with no far plane to reach, the depth values crowd into the region where a depth buffer can still tell them apart, instead of being spread over a range whose far end nothing occupies.

    Mat4::perspective_infinite_gl

    fn Mat4::perspective_infinite_gl(fov_y : Float, aspect : Float, near : Float) -> Mat4

    A perspective projection with the depth range -1..1 and no far plane.

    Mat4::row

    fn Mat4::row(self : Mat4, index : Int) -> Vec4

    Row index, counted from zero.

    Mat4::sub

    fn Mat4::sub(self : Mat4, other : Mat4) -> Mat4

    Mat4::to_array

    fn Mat4::to_array(self : Mat4) -> FixedArray[Float]

    The elements in column-major order, the sixteen a buffer holds.

    Mat4::to_repr

    Mat4::to_string

    fn Mat4::to_string(self : Mat4) -> String

    Mat4::to_trs

    fn Mat4::to_trs(self : Mat4) -> Trs?

    The translation, rotation and scale the matrix was built from, or None when it cannot be read that way.

    A matrix with shear in it — the common case being a non-uniform scale applied after a rotation — is not a Trs and has no decomposition here; neither has one whose linear part is singular, whose rotation would be unrecoverable. When the determinant is negative the first component of scale carries the sign, because a mirrored basis is a rotation with one flipped axis.

    Mat4::transform_point

    fn Mat4::transform_point(self : Mat4, p : Vec3) -> Vec3

    The point the matrix takes p to, dividing by w when there is one.

    A projection matrix leaves the result in homogeneous coordinates; the point only exists after that division, so it happens here. A w of zero — a point on the horizon of a perspective projection — leaves the numerator as it is.

    Mat4::transform_vector

    fn Mat4::transform_vector(self : Mat4, v : Vec3) -> Vec3

    The direction the matrix takes v to, ignoring the translation column.

    Directions have no position, so the fourth row must not touch them: a translation leaves a direction alone, as it should.

    Mat4::transpose

    fn Mat4::transpose(self : Mat4) -> Mat4

    The matrix with rows and columns exchanged.

    Mat4::zero

    fn Mat4::zero() -> Mat4

    The zero matrix: the one that sends everything to the origin and loses w.

    Plane

    pub(all) struct Plane {
    normal : Vec3
    distance : Float
    } derive(Eq)

    An infinite plane in space: the set of points p with dot(normal, p) ==distance, where the normal is a unit vector.

    The offset form rather than point-and-normal, because the signed distance of a point is then a dot product and no subtraction, which is what a frustum does six times per box.

    Values are immutable.
    impl Show for Plane

    Plane::approx

    fn Plane::approx(self : Plane, other : Plane, eps? : Float) -> Bool

    Whether the two planes are the same to within eps.

    Plane::equal

    fn Plane::equal(Plane, Plane) -> Bool

    Plane::flip

    fn Plane::flip(self : Plane) -> Plane

    The same plane with the other side facing out.

    Plane::from_point_normal

    fn Plane::from_point_normal(point : Vec3, normal : Vec3) -> Plane?

    The plane through point with the given normal.

    Plane::from_points

    fn Plane::from_points(a : Vec3, b : Vec3, c : Vec3) -> Plane?

    The plane through the three points, or None when they are collinear.

    The normal faces the way the points wind, by the right-hand rule, so the winding order decides which side is positive.

    Plane::intersect_ray

    fn Plane::intersect_ray(self : Plane, ray : Ray3) -> Float?

    Where the ray meets the plane, as a t, or None when it runs parallel or the crossing is behind the origin.

    Plane::is_finite

    fn Plane::is_finite(self : Plane) -> Bool

    Whether the normal and the distance are finite.

    Plane::new

    fn Plane::new(normal : Vec3, distance : Float) -> Plane

    The plane whose normal is normal and whose signed distance from the origin is distance.

    normal is normalized, and distance scaled with it, so that signed_distance counts the same units as the coordinates. A zero normal names no plane and aborts: there is no answer to give back.

    Plane::not_equal

    fn Plane::not_equal(x : Plane, y : Plane) -> Bool

    Plane::output

    fn Plane::output(self : Plane, logger : &Logger) -> Unit

    Plane::project

    fn Plane::project(self : Plane, point : Vec3) -> Vec3

    The point of the plane closest to point: the point projected onto it.

    Plane::signed_distance

    fn Plane::signed_distance(self : Plane, point : Vec3) -> Float

    The distance from the plane to the point, positive on the side the normal points to.

    Plane::to_repr

    Plane::to_string

    fn Plane::to_string(self : Plane) -> String

    Quat

    pub(all) struct Quat {
    x : Float
    y : Float
    z : Float
    w : Float
    } derive(Eq)

    A rotation, as a unit quaternion.

    (x, y, z) is the axis of the rotation scaled by the sine of half its angle, and w the cosine of half the angle. Half angles, because a rotation acts on a vector from both sides: q v q⁻¹.

    A quaternion is four numbers where a matrix is nine, composes in fewer operations, and interpolates smoothly — which is why animation and physics carry rotations this way and convert to a matrix only when handing one to a shader.

    The rotations are the unit quaternions; the ones here do not enforce that on construction, because building one by summing or lerping intermediate values is legitimate and only the end result has to be normal. Anything that composes an arbitrary number of them — rotate, to_mat3, slerp — says when it assumes unit length.
    impl Div for Quat
    impl Mul for Quat
    impl Neg for Quat
    impl Show for Quat

    Quat::approx

    fn Quat::approx(self : Quat, other : Quat, eps? : Float) -> Bool

    Whether the two rotations are the same to within eps.

    q and -q are the same rotation, so the sign between them is not a difference; the comparison uses the absolute dot product.

    Quat::conjugate

    fn Quat::conjugate(self : Quat) -> Quat

    The conjugate: the quaternion with its axis part negated.

    The inverse rotation for a unit quaternion, and cheaper than inverse because it divides by nothing.

    Quat::div

    fn Quat::div(self : Quat, other : Quat) -> Quat

    Quat::dot

    fn Quat::dot(self : Quat, other : Quat) -> Float

    The dot product of the two, as four-component vectors.

    Its sign says whether the two rotations take the short way round: negative means the interpolations in this module would travel the long way, and interpolating with the negated quaternion instead gives the short one.

    Quat::equal

    fn Quat::equal(Quat, Quat) -> Bool

    Quat::from_axis_angle

    fn Quat::from_axis_angle(axis : Vec3, radians : Float) -> Quat

    The rotation of radians about axis, by the right-hand rule.

    axis is normalized first; a zero axis has no direction to turn about and gives the identity.

    test {
    let quarter = @moonmath.Quat::from_axis_angle(
    @moonmath.Vec3::unit_z(),
    @moonmath.half_pi,
    )
    assert_true(
    quarter.rotate(@moonmath.Vec3::unit_x()).approx(@moonmath.Vec3::unit_y()),
    )
    }

    Quat::from_euler

    fn Quat::from_euler(order : EulerOrder, angles : Vec3) -> Quat

    The rotation the three angles describe, taken in order, in radians.

    The components of angles are the turns about x, y and z, wherever in the order those axes turn up: for Zyx, which turns about z first, the third component is the first turn. That is the reading a caller expects from a vector named after axes, and it is exact for the six orders that name three different axes.

    The other six turn about one axis twice — Xyx is the family — and two turns about one axis cannot both be given by its single component. Passing one of those orders to this function aborts; from_euler_turns takes the three turns in order and covers all twelve.

    test {
    // Zyx turns about z first, so the third component is the first turn.
    let turn = @moonmath.Quat::from_euler(
    @moonmath.EulerOrder::Zyx,
    @moonmath.Vec3::new(0.0, 0.0, @moonmath.half_pi),
    )
    assert_true(
    turn.rotate(@moonmath.Vec3::unit_y()).approx(-@moonmath.Vec3::unit_x()),
    )
    }

    Quat::from_euler_turns

    fn Quat::from_euler_turns(order : EulerOrder, turns : Turns) -> Quat

    The rotation the three turns describe, in the order the order names them.

    The general form: it takes the first turn, the second and the third however many axes they use, so it covers all twelve orders. For the six that name three different axes it is the same rotation from_euler builds from a vector, with the turns already in order.

    Quat::from_mat3

    fn Quat::from_mat3(m : Mat3) -> Quat

    The rotation the matrix holds, for a matrix that is a rotation.

    A matrix with a scale in it gives the rotation underneath only when the scale is uniform; one that is not a rotation at all gives the nearest unit quaternion to its columns, which is still a usable answer for a matrix that drifted.

    Quat::from_mat4

    fn Quat::from_mat4(m : Mat4) -> Quat

    The rotation the 4×4 matrix holds, read from its upper-left block.

    Quat::from_rotation_arc

    fn Quat::from_rotation_arc(from : Vec3, to : Vec3) -> Quat

    The shortest rotation that turns from towards to, for two directions.

    The pair need not be normalized and need not be perpendicular to anything: the rotation takes the direction of from to the direction of to and leaves the plane they span alone. Opposed directions have no single shortest rotation — every half turn about a perpendicular axis works — and one is picked. A zero vector has no direction to turn and gives the identity.

    test {
    let turn = @moonmath.Quat::from_rotation_arc(
    @moonmath.Vec3::unit_x(),
    @moonmath.Vec3::unit_y(),
    )
    assert_true(
    turn.rotate(@moonmath.Vec3::unit_x()).approx(@moonmath.Vec3::unit_y()),
    )
    }

    Quat::identity

    fn Quat::identity() -> Quat

    The rotation that changes nothing.

    Quat::inverse

    fn Quat::inverse(self : Quat) -> Quat?

    The rotation run backwards: the quaternion that undoes this one.

    For a unit quaternion this is the conjugate; for any other it is the conjugate divided by the squared length, so that q * q.inverse() is the identity. None for the zero quaternion, which represents no rotation and has no inverse.

    Quat::is_finite

    fn Quat::is_finite(self : Quat) -> Bool

    Whether every component is finite.

    Quat::length

    fn Quat::length(self : Quat) -> Float

    The length of the quaternion as a four-component vector: one for a rotation.

    Quat::length_squared

    fn Quat::length_squared(self : Quat) -> Float

    The square of the length.

    Quat::mul

    fn Quat::mul(self : Quat, other : Quat) -> Quat

    Quat::neg

    fn Quat::neg(self : Quat) -> Quat

    Quat::new

    fn Quat::new(x : Float, y : Float, z : Float, w : Float) -> Quat

    The quaternion from its four components, in the order (x, y, z, w).

    Quat::nlerp

    fn Quat::nlerp(self : Quat, other : Quat, t : Float) -> Quat

    The rotation a fraction of the way from self to other along the straight line, normalized back onto the sphere.

    Cheaper than slerp and close to it for small angles; the rate is not quite constant, which a long animation would show.

    Quat::normalize

    fn Quat::normalize(self : Quat) -> Quat

    The quaternion scaled to length one, or the identity if it is zero.

    A rotation is always a unit quaternion; one built by summing or lerping others has to be brought back before it can rotate anything, and the zero quaternion — which summing can produce — falls back to the identity rather than to a division by zero.

    Quat::not_equal

    fn Quat::not_equal(x : Quat, y : Quat) -> Bool

    Quat::output

    fn Quat::output(self : Quat, logger : &Logger) -> Unit

    Quat::random

    fn Quat::random(u1 : Float, u2 : Float, u3 : Float) -> Quat

    A uniformly distributed random rotation.

    u1, u2 and u3 are independent uniform samples in [0, 1). The result is a unit quaternion spread evenly over the rotation group — the construction that makes it even is not obvious, and a quaternion built from three evenly sampled angles would not be.

    Quat::rotate

    fn Quat::rotate(self : Quat, v : Vec3) -> Vec3

    The vector turned by the rotation.

    Assumes unit length. Two cross products rather than q v q⁻¹, which is the same result with fewer multiplications.

    Quat::slerp

    fn Quat::slerp(self : Quat, other : Quat, t : Float) -> Quat

    The rotation a fraction of the way from self to other, at constant angular speed.

    Spherical interpolation: the intermediate rotations turn at a steady rate, which is what an animation between two orientations wants. The two are compared first, and the negated one is used when they point away from each other — q and -q are the same rotation, and the negation is the one that takes the short way.

    Quat::to_array

    fn Quat::to_array(self : Quat) -> FixedArray[Float]

    The four components in order, [x, y, z, w], for handing to a buffer.

    Quat::to_axis_angle

    fn Quat::to_axis_angle(self : Quat) -> (Vec3, Float)

    The angle and axis of the rotation, as (axis, radians) with the axis a unit vector and the angle in 0..pi.

    A rotation of zero has no axis to name; it comes back as (x, 0) — the first axis, with a zero angle, which rotates nothing either way.

    Quat::to_euler

    fn Quat::to_euler(self : Quat, order : EulerOrder) -> Vec3

    The turn about each axis that produces this rotation in order, in radians.

    The inverse of from_euler, and read the same way: the component of an axis is the turn about it, wherever in the order it comes. Aborts for the six orders that turn about one axis twice, whose two turns cannot both be written into one component; to_euler_turns returns them in order instead.

    test {
    let turn = @moonmath.Quat::from_axis_angle(
    @moonmath.Vec3::unit_x(),
    @moonmath.quarter_pi,
    )
    let angles = turn.to_euler(@moonmath.EulerOrder::Yxz)
    assert_true(@moonmath.approx(angles.x, @moonmath.quarter_pi))
    }

    Quat::to_euler_turns

    fn Quat::to_euler_turns(self : Quat, order : EulerOrder) -> Turns

    The three turns that produce this rotation in order, in radians, in the order the order names them.

    The inverse of from_euler_turns, for every one of the twelve orders. Feeding the result back gives the rotation again, except at the two angles where the first and third turns collapse into one — the gimbal lock every three-angle convention has. There the first turn comes back as zero and the whole of it is carried by the third.

    Quat::to_mat3

    fn Quat::to_mat3(self : Quat) -> Mat3

    The rotation as a 3×3 matrix.

    Assumes unit length; a quaternion that drifted scales the matrix by the square of its norm.

    Quat::to_mat4

    fn Quat::to_mat4(self : Quat) -> Mat4

    The rotation as a 4×4 matrix whose translation is zero.

    Quat::to_repr

    Quat::to_string

    fn Quat::to_string(self : Quat) -> String

    Ray2

    pub(all) struct Ray2 {
    origin : Vec2
    direction : Vec2
    } derive(Eq)

    A half-line in the plane: a start and a direction.

    origin + direction * t for t >= 0. The direction is not normalized here — a ray whose direction carries the speed of something travelling along it is as useful as a unit one, and t then reads as time.

    Values are immutable.
    impl Show for Ray2

    Ray2::approx

    fn Ray2::approx(self : Ray2, other : Ray2, eps? : Float) -> Bool

    Whether the two rays start at the same point and head the same way.

    Ray2::at

    fn Ray2::at(self : Ray2, t : Float) -> Vec2

    The point t along the ray.

    Ray2::equal

    fn Ray2::equal(Ray2, Ray2) -> Bool

    Ray2::intersect_aabb

    fn Ray2::intersect_aabb(self : Ray2, box : Aabb2) -> Float?

    Where the ray first meets the box, as a t, or None when it misses.

    A ray that starts inside the box meets its surface on the way out, and that crossing is the one reported — the nearest one at t >= 0, rather than "the one in front of the origin in the direction of travel".

    Ray2::intersect_aabb_interval

    fn Ray2::intersect_aabb_interval(self : Ray2, box : Aabb2) -> (Float, Float)?

    The stretch of the ray that lies inside the box, as a pair of t values.

    A ray that starts inside the box is already inside at t = 0, and the entry is then negative — the point was inside before the ray started, and the interval says from where. None when the ray misses the box.

    Ray2::intersect_circle

    fn Ray2::intersect_circle(self : Ray2, center : Vec2, radius : Float) -> Float?

    Where the ray first meets the circle, as a t, or None when it misses.

    A ray that starts inside the circle meets it on the way out.

    Ray2::intersect_rect

    fn Ray2::intersect_rect(self : Ray2, rect : Rect) -> Float?

    Where the ray first meets the rectangle, as a t, or None when it misses.

    Ray2::new

    fn Ray2::new(origin : Vec2, direction : Vec2) -> Ray2

    The ray starting at origin and heading along direction.

    Ray2::not_equal

    fn Ray2::not_equal(x : Ray2, y : Ray2) -> Bool

    Ray2::output

    fn Ray2::output(self : Ray2, logger : &Logger) -> Unit

    Ray2::to_repr

    Ray2::to_string

    fn Ray2::to_string(self : Ray2) -> String

    Ray3

    pub(all) struct Ray3 {
    origin : Vec3
    direction : Vec3
    } derive(Eq)

    A half-line in space: a start and a direction.

    What a picking click, a mouse cursor in a 3D scene and a photon all are. The tests here return the parameter t of the first surface crossing rather than the point, because the caller usually needs the distance and the point is one multiplication away.

    Values are immutable.
    impl Show for Ray3

    Ray3::approx

    fn Ray3::approx(self : Ray3, other : Ray3, eps? : Float) -> Bool

    Whether the two rays start at the same point and head the same way.

    Ray3::at

    fn Ray3::at(self : Ray3, t : Float) -> Vec3

    The point t along the ray.

    Ray3::equal

    fn Ray3::equal(Ray3, Ray3) -> Bool

    Ray3::intersect_aabb

    fn Ray3::intersect_aabb(self : Ray3, box : Aabb3) -> Float?

    Where the ray first meets the box, as a t, or None when it misses.

    A ray that starts inside the box reports its exit.

    Ray3::intersect_aabb_interval

    fn Ray3::intersect_aabb_interval(self : Ray3, box : Aabb3) -> (Float, Float)?

    The stretch of the ray that lies inside the box, as a pair of t values.

    A ray that starts inside has an entry before its origin. This is the slab test: for each axis the ray is inside the box over an interval, and the intersection of the three intervals is the stretch wanted. A ray parallel to a face never leaves that axis' slab, or never enters it.

    Ray3::intersect_plane

    fn Ray3::intersect_plane(self : Ray3, plane : Plane) -> Float?

    Where the ray meets the plane, as a t, or None when it runs parallel.

    A ray lying inside the plane meets it everywhere and nowhere in particular; that comes back as None too.

    Ray3::intersect_sphere

    fn Ray3::intersect_sphere(self : Ray3, sphere : Sphere) -> Float?

    Where the ray first meets the sphere, as a t, or None when it misses.

    A ray that starts inside the sphere reports its exit.

    Ray3::intersect_triangle

    fn Ray3::intersect_triangle(self : Ray3, triangle : Triangle) -> Float?

    Where the ray meets the triangle, as a t, or None when it misses.

    Möller–Trumbore: the ray is solved against the triangle's barycentric coordinates directly, without building the plane first, and a hit behind the origin or outside the triangle gives None.

    Ray3::new

    fn Ray3::new(origin : Vec3, direction : Vec3) -> Ray3

    The ray starting at origin and heading along direction.

    Ray3::not_equal

    fn Ray3::not_equal(x : Ray3, y : Ray3) -> Bool

    Ray3::output

    fn Ray3::output(self : Ray3, logger : &Logger) -> Unit

    Ray3::to_repr

    Ray3::to_string

    fn Ray3::to_string(self : Ray3) -> String

    Rect

    pub(all) struct Rect {
    origin : Vec2
    size : Vec2
    } derive(Eq)

    A rectangle given by its corner and its size: the shape a layout hands out.

    A layout thinks in "here, this big", not in two opposite corners, which is why this is not Aabb2 — the two describe the same set of points and answer different questions. origin is the corner with the smaller coordinates and size the extent from it, so a size with a negative component describes the same points as the equivalent positive one, with the corners swapped.

    Values are immutable.
    impl Show for Rect

    Rect::approx

    fn Rect::approx(self : Rect, other : Rect, eps? : Float) -> Bool

    Whether the two rectangles cover the same points to within eps.

    Rect::area

    fn Rect::area(self : Rect) -> Float

    The area the rectangle covers.

    Rect::center

    fn Rect::center(self : Rect) -> Vec2

    The point in the middle.

    Rect::contains

    fn Rect::contains(self : Rect, point : Vec2) -> Bool

    Whether the point is inside or on the edge.

    Rect::corners

    fn Rect::corners(self : Rect) -> FixedArray[Vec2]

    The four corners, starting at origin and going counter-clockwise.

    Rect::equal

    fn Rect::equal(Rect, Rect) -> Bool

    Rect::from_edges

    fn Rect::from_edges(left : Float, top : Float, right : Float, bottom : Float) -> Rect

    The rectangle spanning two opposite corners, in either order.

    Rect::from_xywh

    fn Rect::from_xywh(x : Float, y : Float, width : Float, height : Float) -> Rect

    The rectangle with its top-left corner at x, y and the given extent.

    The usual spelling for a layout node; which way is "top" is the caller's convention, as everywhere else here.

    Rect::height

    fn Rect::height(self : Rect) -> Float

    The extent along y, always non-negative.

    Rect::inset

    fn Rect::inset(self : Rect, amount : Float) -> Rect

    The same rectangle with every edge moved in by amount.

    A positive amount shrinks it, a negative one grows it; shrinking past the middle leaves an empty rectangle at the centre rather than a negative one.

    Rect::intersection

    fn Rect::intersection(self : Rect, other : Rect) -> Rect?

    The area the two share, or None when they share none.

    Rect::intersects

    fn Rect::intersects(self : Rect, other : Rect) -> Bool

    Whether the two rectangles share any area.

    Touching edges share no area and do not count; an empty rectangle intersects nothing.

    Rect::is_empty

    fn Rect::is_empty(self : Rect) -> Bool

    Whether the rectangle covers no area at all.

    A zero width or height is empty: it is a line or a point, and nothing inside it can be drawn or hit.

    Rect::max

    fn Rect::max(self : Rect) -> Vec2

    The corner with the larger coordinates.

    Rect::min

    fn Rect::min(self : Rect) -> Vec2

    The corner with the smaller coordinates.

    Rect::new

    fn Rect::new(origin : Vec2, size : Vec2) -> Rect

    The rectangle at origin extending size.

    Rect::not_equal

    fn Rect::not_equal(x : Rect, y : Rect) -> Bool

    Rect::output

    fn Rect::output(self : Rect, logger : &Logger) -> Unit

    Rect::to_aabb

    fn Rect::to_aabb(self : Rect) -> Aabb2

    The same points as an axis-aligned box.

    Rect::to_repr

    Rect::to_string

    fn Rect::to_string(self : Rect) -> String

    Rect::translate

    fn Rect::translate(self : Rect, offset : Vec2) -> Rect

    The same rectangle moved by offset.

    Rect::union

    fn Rect::union(self : Rect, other : Rect) -> Rect

    The smallest rectangle containing both.

    Rect::width

    fn Rect::width(self : Rect) -> Float

    The extent along x, always non-negative.

    Shear

    pub(all) struct Shear {
    xy : Float
    xz : Float
    yz : Float
    } derive(Eq)

    A shear: the three off-diagonal terms that slide one axis into another while leaving the origin and the axes themselves where they are.

    A shear is a linear map, so it is applied through to_mat3 or to_mat4. xy slides y into x, xz slides z into x, and yz slides z into y, matching the (row, column) terms of the upper triangle. The three together are the general upper-triangular linear map, which is what a skew, a slanted view of a page, or a shear deformation of a mesh is.

    Values are immutable.
    impl Show for Shear

    Shear::approx

    fn Shear::approx(self : Shear, other : Shear, eps? : Float) -> Bool

    Whether the three terms agree with other to within eps.

    Shear::equal

    fn Shear::equal(Shear, Shear) -> Bool

    Shear::is_finite

    fn Shear::is_finite(self : Shear) -> Bool

    Whether the three terms are finite.

    Shear::new

    fn Shear::new(xy : Float, xz : Float, yz : Float) -> Shear

    The shear from its three terms.

    Shear::not_equal

    fn Shear::not_equal(x : Shear, y : Shear) -> Bool

    Shear::output

    fn Shear::output(self : Shear, logger : &Logger) -> Unit

    Shear::to_mat3

    fn Shear::to_mat3(self : Shear) -> Mat3

    The shear as a linear map, ready to compose with a rotation or a scale.

    Shear::to_mat4

    fn Shear::to_mat4(self : Shear) -> Mat4

    The shear as a 4×4 matrix whose translation is zero.

    Shear::to_repr

    Shear::to_string

    fn Shear::to_string(self : Shear) -> String

    Shear::zero

    fn Shear::zero() -> Shear

    No shear: the identity map.

    Sphere

    pub(all) struct Sphere {
    center : Vec3
    radius : Float
    } derive(Eq)

    A sphere: a centre and a radius.

    Values are immutable.
    impl Show for Sphere

    Sphere::approx

    fn Sphere::approx(self : Sphere, other : Sphere, eps? : Float) -> Bool

    Whether the two spheres agree to within eps.

    Sphere::contains_point

    fn Sphere::contains_point(self : Sphere, point : Vec3) -> Bool

    Whether the point is inside or on the surface.

    Sphere::equal

    fn Sphere::equal(Sphere, Sphere) -> Bool

    Sphere::intersects_aabb

    fn Sphere::intersects_aabb(self : Sphere, box : Aabb3) -> Bool

    Whether the sphere and the box overlap or touch.

    The point of the box closest to the centre is the one that decides it: if that point is within the radius, the two meet. Testing the centre against the box alone would call a sphere near a corner a miss.

    Sphere::intersects_sphere

    fn Sphere::intersects_sphere(self : Sphere, other : Sphere) -> Bool

    Whether the two spheres overlap or touch.

    Sphere::new

    fn Sphere::new(center : Vec3, radius : Float) -> Sphere

    The sphere with the given centre and radius.

    Sphere::not_equal

    fn Sphere::not_equal(x : Sphere, y : Sphere) -> Bool

    Sphere::output

    fn Sphere::output(self : Sphere, logger : &Logger) -> Unit

    Sphere::random_inside

    fn Sphere::random_inside(self : Sphere, u1 : Float, u2 : Float, u3 : Float) -> Vec3

    A point uniformly distributed through the sphere.

    u1, u2 and u3 are independent uniform samples in [0, 1), as for Vec3::random_in_ball.

    Sphere::random_on_surface

    fn Sphere::random_on_surface(self : Sphere, u1 : Float, u2 : Float) -> Vec3

    A point uniformly distributed over the surface of the sphere.

    u1 and u2 are independent uniform samples in [0, 1), as for Vec3::random_on_sphere.

    Sphere::surface_area

    fn Sphere::surface_area(self : Sphere) -> Float

    The area of the surface.

    Sphere::to_repr

    Sphere::to_string

    fn Sphere::to_string(self : Sphere) -> String

    Sphere::volume

    fn Sphere::volume(self : Sphere) -> Float

    The volume it encloses.

    Triangle

    pub(all) struct Triangle {
    a : Vec3
    b : Vec3
    c : Vec3
    } derive(Eq)

    A triangle in space: three corners in winding order.

    The winding decides which side the normal faces, by the right-hand rule.

    Values are immutable.
    impl Show for Triangle

    Triangle::approx

    fn Triangle::approx(self : Triangle, other : Triangle, eps? : Float) -> Bool

    Whether the two triangles have the same corners to within eps.

    Triangle::area

    fn Triangle::area(self : Triangle) -> Float

    The area.

    Triangle::barycentric

    fn Triangle::barycentric(self : Triangle, point : Vec3) -> Vec3?

    The barycentric coordinates of a point with respect to the triangle.

    The three weights sum to one, and a point inside has all three in 0..1. The point is projected onto the triangle's plane first, so a point off the plane still has coordinates — they are those of the closest point on the plane. None when the triangle has no area to weight against.

    Triangle::equal

    fn Triangle::equal(Triangle, Triangle) -> Bool

    Triangle::intersect_ray

    fn Triangle::intersect_ray(self : Triangle, ray : Ray3) -> Float?

    Where the ray meets the triangle, as a t, or None when it misses.

    The same test as Ray3::intersect_triangle, written from this side for code that already has the triangle.

    Triangle::new

    fn Triangle::new(a : Vec3, b : Vec3, c : Vec3) -> Triangle

    The triangle with the three given corners.

    Triangle::normal

    fn Triangle::normal(self : Triangle) -> Vec3

    The unit normal, facing the way the corners wind.

    A triangle with no area — three collinear points — has no normal, and comes back as the zero vector rather than a NaN.

    Triangle::not_equal

    fn Triangle::not_equal(x : Triangle, y : Triangle) -> Bool

    Triangle::output

    fn Triangle::output(self : Triangle, logger : &Logger) -> Unit

    Triangle::to_repr

    Triangle::to_string

    fn Triangle::to_string(self : Triangle) -> String

    Trs

    pub(all) struct Trs {
    translation : Vec3
    rotation : Quat
    scale : Vec3
    } derive(Eq)

    A translation, a rotation and a scale, in that order of composition: a point is scaled, then turned, then moved.

    This is what Mat4::to_trs recovers from a matrix that was built the same way, and what a scene graph stores per node — the three pieces are what an animation edits, and a decomposition keeps them editable.
    impl Show for Trs
    impl Debug for Trs

    Trs::equal

    fn Trs::equal(Trs, Trs) -> Bool

    Trs::new

    fn Trs::new(translation : Vec3, rotation : Quat, scale : Vec3) -> Trs

    The three parts of a transformation.

    Trs::not_equal

    fn Trs::not_equal(x : Trs, y : Trs) -> Bool

    Trs::output

    fn Trs::output(self : Trs, logger : &Logger) -> Unit

    Trs::to_repr

    Trs::to_string

    fn Trs::to_string(self : Trs) -> String

    Turns

    pub(all) struct Turns {
    first : Float
    second : Float
    third : Float
    } derive(Eq)

    The three turns of a rotation, in the order the order names them.

    first is the turn about the first axis of the order, second about the second and third about the third — which for six of the twelve orders is the first axis again. A record rather than a vector because a vector's components are named after axes, and for those six orders the third turn is not about the third axis; naming the slots after their place in the order says exactly what they are.
    impl Show for Turns

    Turns::approx

    fn Turns::approx(self : Turns, other : Turns, eps? : Float) -> Bool

    Whether the two triples agree to within eps.

    Turns::equal

    fn Turns::equal(Turns, Turns) -> Bool

    Turns::new

    fn Turns::new(first : Float, second : Float, third : Float) -> Turns

    The three turns, in the order the order names them.

    Turns::not_equal

    fn Turns::not_equal(x : Turns, y : Turns) -> Bool

    Turns::output

    fn Turns::output(self : Turns, logger : &Logger) -> Unit

    Turns::to_repr

    Turns::to_string

    fn Turns::to_string(self : Turns) -> String

    UVec2

    pub(all) struct UVec2 {
    x : UInt
    y : UInt
    } derive(Eq, Hash)

    Two unsigned whole numbers: a size, a count, a pixel coordinate that cannot be negative.

    There is no Neg here for the obvious reason: a negative size is not a size. Subtraction is present and wraps, as UInt subtraction does.
    impl Add for UVec2
    impl Div for UVec2
    impl Mul for UVec2
    impl Show for UVec2
    impl Sub for UVec2

    UVec2::add

    fn UVec2::add(self : UVec2, other : UVec2) -> UVec2

    UVec2::as_vec2

    fn UVec2::as_vec2(self : UVec2) -> Vec2

    The same size as a floating-point vector, exactly.

    UVec2::clamp

    fn UVec2::clamp(self : UVec2, low : UVec2, high : UVec2) -> UVec2

    The pair with every component pinned between low and high, inclusive.

    UVec2::div

    fn UVec2::div(self : UVec2, other : UVec2) -> UVec2

    UVec2::dot

    fn UVec2::dot(self : UVec2, other : UVec2) -> UInt

    The dot product.

    UVec2::equal

    fn UVec2::equal(UVec2, UVec2) -> Bool

    UVec2::hash

    fn UVec2::hash(self : UVec2) -> Int

    UVec2::hash_combine

    fn UVec2::hash_combine(UVec2, Hasher) -> Unit

    UVec2::max

    fn UVec2::max(self : UVec2, other : UVec2) -> UVec2

    The component-wise larger of the two.

    UVec2::min

    fn UVec2::min(self : UVec2, other : UVec2) -> UVec2

    The component-wise smaller of the two.

    UVec2::mul

    fn UVec2::mul(self : UVec2, other : UVec2) -> UVec2

    UVec2::new

    fn UVec2::new(x : UInt, y : UInt) -> UVec2

    The pair (x, y).

    UVec2::not_equal

    fn UVec2::not_equal(x : UVec2, y : UVec2) -> Bool

    UVec2::one

    fn UVec2::one() -> UVec2

    One in both components.

    UVec2::output

    fn UVec2::output(self : UVec2, logger : &Logger) -> Unit

    UVec2::splat

    fn UVec2::splat(value : UInt) -> UVec2

    The same value in both components.

    UVec2::sub

    fn UVec2::sub(self : UVec2, other : UVec2) -> UVec2

    UVec2::to_array

    fn UVec2::to_array(self : UVec2) -> FixedArray[UInt]

    The components in order, [x, y].

    UVec2::to_repr

    UVec2::to_string

    fn UVec2::to_string(self : UVec2) -> String

    UVec2::zero

    fn UVec2::zero() -> UVec2

    Zero in both components: an empty size.

    UVec3

    pub(all) struct UVec3 {
    x : UInt
    y : UInt
    z : UInt
    } derive(Eq, Hash)

    Three unsigned whole numbers.
    impl Add for UVec3
    impl Div for UVec3
    impl Mul for UVec3
    impl Show for UVec3
    impl Sub for UVec3

    UVec3::add

    fn UVec3::add(self : UVec3, other : UVec3) -> UVec3

    UVec3::as_vec3

    fn UVec3::as_vec3(self : UVec3) -> Vec3

    The same values as a floating-point vector, exactly.

    UVec3::clamp

    fn UVec3::clamp(self : UVec3, low : UVec3, high : UVec3) -> UVec3

    The triple with every component pinned between low and high, inclusive.

    UVec3::div

    fn UVec3::div(self : UVec3, other : UVec3) -> UVec3

    UVec3::dot

    fn UVec3::dot(self : UVec3, other : UVec3) -> UInt

    The dot product.

    UVec3::equal

    fn UVec3::equal(UVec3, UVec3) -> Bool

    UVec3::hash

    fn UVec3::hash(self : UVec3) -> Int

    UVec3::hash_combine

    fn UVec3::hash_combine(UVec3, Hasher) -> Unit

    UVec3::max

    fn UVec3::max(self : UVec3, other : UVec3) -> UVec3

    The component-wise larger of the two.

    UVec3::min

    fn UVec3::min(self : UVec3, other : UVec3) -> UVec3

    The component-wise smaller of the two.

    UVec3::mul

    fn UVec3::mul(self : UVec3, other : UVec3) -> UVec3

    UVec3::new

    fn UVec3::new(x : UInt, y : UInt, z : UInt) -> UVec3

    The triple (x, y, z).

    UVec3::not_equal

    fn UVec3::not_equal(x : UVec3, y : UVec3) -> Bool

    UVec3::one

    fn UVec3::one() -> UVec3

    One in every component.

    UVec3::output

    fn UVec3::output(self : UVec3, logger : &Logger) -> Unit

    UVec3::splat

    fn UVec3::splat(value : UInt) -> UVec3

    The same value in all three components.

    UVec3::sub

    fn UVec3::sub(self : UVec3, other : UVec3) -> UVec3

    UVec3::to_array

    fn UVec3::to_array(self : UVec3) -> FixedArray[UInt]

    The components in order, [x, y, z].

    UVec3::to_repr

    UVec3::to_string

    fn UVec3::to_string(self : UVec3) -> String

    UVec3::zero

    fn UVec3::zero() -> UVec3

    Zero in every component.

    UVec4

    pub(all) struct UVec4 {
    x : UInt
    y : UInt
    z : UInt
    w : UInt
    } derive(Eq, Hash)

    Four unsigned whole numbers.
    impl Add for UVec4
    impl Div for UVec4
    impl Mul for UVec4
    impl Show for UVec4
    impl Sub for UVec4

    UVec4::add

    fn UVec4::add(self : UVec4, other : UVec4) -> UVec4

    UVec4::as_vec4

    fn UVec4::as_vec4(self : UVec4) -> Vec4

    The same values as a floating-point vector, exactly.

    UVec4::clamp

    fn UVec4::clamp(self : UVec4, low : UVec4, high : UVec4) -> UVec4

    The quadruple with every component pinned between low and high.

    UVec4::div

    fn UVec4::div(self : UVec4, other : UVec4) -> UVec4

    UVec4::dot

    fn UVec4::dot(self : UVec4, other : UVec4) -> UInt

    The dot product.

    UVec4::equal

    fn UVec4::equal(UVec4, UVec4) -> Bool

    UVec4::hash

    fn UVec4::hash(self : UVec4) -> Int

    UVec4::hash_combine

    fn UVec4::hash_combine(UVec4, Hasher) -> Unit

    UVec4::max

    fn UVec4::max(self : UVec4, other : UVec4) -> UVec4

    The component-wise larger of the two.

    UVec4::min

    fn UVec4::min(self : UVec4, other : UVec4) -> UVec4

    The component-wise smaller of the two.

    UVec4::mul

    fn UVec4::mul(self : UVec4, other : UVec4) -> UVec4

    UVec4::new

    fn UVec4::new(x : UInt, y : UInt, z : UInt, w : UInt) -> UVec4

    The quadruple (x, y, z, w).

    UVec4::not_equal

    fn UVec4::not_equal(x : UVec4, y : UVec4) -> Bool

    UVec4::one

    fn UVec4::one() -> UVec4

    One in every component.

    UVec4::output

    fn UVec4::output(self : UVec4, logger : &Logger) -> Unit

    UVec4::splat

    fn UVec4::splat(value : UInt) -> UVec4

    The same value in all four components.

    UVec4::sub

    fn UVec4::sub(self : UVec4, other : UVec4) -> UVec4

    UVec4::to_array

    fn UVec4::to_array(self : UVec4) -> FixedArray[UInt]

    The components in order, [x, y, z, w].

    UVec4::to_repr

    UVec4::to_string

    fn UVec4::to_string(self : UVec4) -> String

    UVec4::zero

    fn UVec4::zero() -> UVec4

    Zero in every component.

    Vec2

    pub(all) struct Vec2 {
    x : Float
    y : Float
    } derive(Eq)

    A point or a direction in the plane.

    Two scalars side by side, in the order the plane's axes are named: x runs east, y runs north. Nothing here knows whether +y points up the screen or down it — that is a convention of the canvas, not of the arithmetic.

    Values are immutable; every operation returns a new one.

    The operators take two vectors of the same type. There is no Vec2 * Float, because a value can only carry one Mul implementation here: scalar scaling is scale, its inverse divide.
    impl Add for Vec2
    impl Div for Vec2
    impl Mul for Vec2
    impl Neg for Vec2
    impl Show for Vec2
    impl Sub for Vec2

    Vec2::abs

    fn Vec2::abs(self : Vec2) -> Vec2

    The vector with every component replaced by its absolute value.

    Vec2::add

    fn Vec2::add(self : Vec2, other : Vec2) -> Vec2

    Vec2::angle_between

    fn Vec2::angle_between(self : Vec2, other : Vec2) -> Float

    The angle from self to other, in radians, positive counter-clockwise.

    Signed, because in the plane there are only two ways to turn and the sign says which. The result is in -pi..pi, and is zero when either vector has no direction.

    test {
    let x : @moonmath.Vec2 = @moonmath.Vec2::unit_x()
    let y = @moonmath.Vec2::unit_y()
    assert_true(@moonmath.approx(x.angle_between(y), @moonmath.half_pi))
    }

    Vec2::approx

    fn Vec2::approx(self : Vec2, other : Vec2, eps? : Float) -> Bool

    Whether the two vectors agree to within eps component by component.

    Without an epsilon this uses default_epsilon.

    Vec2::ceil

    fn Vec2::ceil(self : Vec2) -> Vec2

    The vector with every component rounded up to a whole number.

    Vec2::clamp

    fn Vec2::clamp(self : Vec2, low : Vec2, high : Vec2) -> Vec2

    The vector with every component pinned between low and high.

    Vec2::cross

    fn Vec2::cross(self : Vec2, other : Vec2) -> Float

    The 2D cross product: the signed area of the parallelogram the two span.

    Positive when other lies counter-clockwise of self, which is the sign a winding test reads.

    Vec2::distance

    fn Vec2::distance(self : Vec2, other : Vec2) -> Float

    The distance between the two points.

    Vec2::distance_squared

    fn Vec2::distance_squared(self : Vec2, other : Vec2) -> Float

    The square of the distance between the two points.

    Vec2::div

    fn Vec2::div(self : Vec2, other : Vec2) -> Vec2

    Vec2::divide

    fn Vec2::divide(self : Vec2, divisor : Float) -> Vec2

    The vector divided by divisor, component by component.

    Dividing by zero gives an infinity or a NaN, as the scalar division does; no check is made here.

    Vec2::dot

    fn Vec2::dot(self : Vec2, other : Vec2) -> Float

    The dot product: length * other.length * cos(angle) between them.

    Positive when they point the same way, negative when they oppose, zero when they are perpendicular. It is also what a shader's dot computes, so the two agree bit for bit when the operands do.

    Vec2::equal

    fn Vec2::equal(Vec2, Vec2) -> Bool

    Vec2::floor

    fn Vec2::floor(self : Vec2) -> Vec2

    The vector with every component rounded down to a whole number.

    Vec2::fract

    fn Vec2::fract(self : Vec2) -> Vec2

    The vector with every component replaced by its fractional part, x - floor(x).

    Vec2::is_finite

    fn Vec2::is_finite(self : Vec2) -> Bool

    Whether both components are finite — neither an infinity nor a NaN.

    Vec2::length

    fn Vec2::length(self : Vec2) -> Float

    The length of the vector: its distance from the origin.

    Vec2::length_squared

    fn Vec2::length_squared(self : Vec2) -> Float

    The square of the length.

    Comparing squared lengths orders vectors the same way length does without the square root, which is why range checks in hot loops use it.

    Vec2::lerp

    fn Vec2::lerp(self : Vec2, other : Vec2, t : Float) -> Vec2

    The point a fraction of the way from self to other.

    Vec2::max

    fn Vec2::max(self : Vec2, other : Vec2) -> Vec2

    The component-wise larger of the two.

    Vec2::min

    fn Vec2::min(self : Vec2, other : Vec2) -> Vec2

    The component-wise smaller of the two.

    Vec2::mul

    fn Vec2::mul(self : Vec2, other : Vec2) -> Vec2

    Vec2::neg

    fn Vec2::neg(self : Vec2) -> Vec2

    Vec2::new

    fn Vec2::new(x : Float, y : Float) -> Vec2

    The vector from x and y.

    Vec2::normalize

    fn Vec2::normalize(self : Vec2) -> Vec2

    The vector scaled to length one, pointing the way it did.

    The zero vector has no direction, and this returns zero rather than a NaN or a division by zero: Vec2::zero() is the named answer for "no direction". Callers that need to tell the case apart use try_normalize.

    Vec2::normalize_or

    fn Vec2::normalize_or(self : Vec2, fallback : Vec2) -> Vec2

    The vector scaled to length one, or fallback if it has no direction.

    Vec2::not_equal

    fn Vec2::not_equal(x : Vec2, y : Vec2) -> Bool

    Vec2::one

    fn Vec2::one() -> Vec2

    The vector with one in both components.

    Vec2::output

    fn Vec2::output(self : Vec2, logger : &Logger) -> Unit

    Vec2::perp

    fn Vec2::perp(self : Vec2) -> Vec2

    The vector turned a quarter turn counter-clockwise: (-y, x).

    Vec2::project

    fn Vec2::project(self : Vec2, onto : Vec2) -> Vec2

    The component of self along onto: the shadow it casts on that direction.

    onto need not be a unit vector; the result lies along it either way. A zero onto has no direction to project onto and gives zero.

    Vec2::reflect

    fn Vec2::reflect(self : Vec2, normal : Vec2) -> Vec2

    The direction self takes after bouncing off a surface with normal normal.

    self is the incoming direction, and normal points out of the surface against it — the result leaves at the mirror angle with the same length. A zero normal returns self unchanged.

    Vec2::refract

    fn Vec2::refract(self : Vec2, normal : Vec2, eta : Float) -> Vec2?

    The direction self takes after entering a medium with normal normal and index ratio eta, or None when it does not enter at all.

    self and normal are taken to be unit length, normal pointing against the incoming direction, and eta the ratio of the indices of refraction on the two sides. Past the critical angle the ray is reflected instead of refracted, and there is no refracted direction to return — that is the None, and it is the caller's job to reflect.

    Vec2::reject

    fn Vec2::reject(self : Vec2, onto : Vec2) -> Vec2

    The component of self perpendicular to onto: what is left after the projection is taken away.

    Vec2::round

    fn Vec2::round(self : Vec2) -> Vec2

    The vector with every component rounded to the nearest whole number, a half going up: 2.5 becomes 3 and -1.5 becomes -1. This is the rounding Float::round does, in both units and sign.

    Vec2::scale

    fn Vec2::scale(self : Vec2, factor : Float) -> Vec2

    The vector multiplied by factor.

    This is the scalar product the * operator cannot express, since * on two vectors is the component-wise one.

    Vec2::splat

    fn Vec2::splat(value : Float) -> Vec2

    The same value in both components.

    Vec2::sub

    fn Vec2::sub(self : Vec2, other : Vec2) -> Vec2

    Vec2::to_array

    fn Vec2::to_array(self : Vec2) -> FixedArray[Float]

    The components in order, [x, y], for handing to a buffer.

    Vec2::to_repr

    Vec2::to_string

    fn Vec2::to_string(self : Vec2) -> String

    Vec2::to_vec3

    fn Vec2::to_vec3(self : Vec2, z : Float) -> Vec3

    The same vector with z appended, for a point being lifted into 3D.

    Vec2::to_vec4

    fn Vec2::to_vec4(self : Vec2, z : Float, w : Float) -> Vec4

    The same vector with z and w appended.

    Vec2::try_normalize

    fn Vec2::try_normalize(self : Vec2) -> Vec2?

    The vector scaled to length one, or None if it has no direction.

    Vec2::unit_x

    fn Vec2::unit_x() -> Vec2

    The unit vector along +x.

    Vec2::unit_y

    fn Vec2::unit_y() -> Vec2

    The unit vector along +y.

    Vec2::yx

    fn Vec2::yx(self : Vec2) -> Vec2

    The components the other way round: (y, x).

    Vec2::zero

    fn Vec2::zero() -> Vec2

    The zero vector: the origin, and the one vector with no direction.

    Vec3

    pub(all) struct Vec3 {
    x : Float
    y : Float
    z : Float
    } derive(Eq)

    A point or a direction in space.

    Right-handed: x and y as in the plane, z towards the viewer in the usual camera setup, so that cross(x, y) == z.

    Values are immutable; every operation returns a new one.

    The operators take two vectors of the same type. Scalar scaling is scale and divide, not * and /, which belong to the component-wise product.
    impl Add for Vec3
    impl Div for Vec3
    impl Mul for Vec3
    impl Neg for Vec3
    impl Show for Vec3
    impl Sub for Vec3

    Vec3::abs

    fn Vec3::abs(self : Vec3) -> Vec3

    The vector with every component replaced by its absolute value.

    Vec3::add

    fn Vec3::add(self : Vec3, other : Vec3) -> Vec3

    Vec3::angle_between

    fn Vec3::angle_between(self : Vec3, other : Vec3) -> Float

    The angle between the two directions, in radians, in 0..pi.

    Unsigned: in space there is no counter-clockwise, so the answer is just how far apart they point. Computed from the normalized dot product, clamped so that rounding cannot push it out of the arc cosine's domain. Zero when either vector has no direction.

    test {
    let x : @moonmath.Vec3 = @moonmath.Vec3::unit_x()
    let y = @moonmath.Vec3::unit_y()
    assert_true(@moonmath.approx(x.angle_between(y), @moonmath.half_pi))
    assert_true(@moonmath.approx(x.angle_between(-x), @moonmath.pi))
    }

    Vec3::approx

    fn Vec3::approx(self : Vec3, other : Vec3, eps? : Float) -> Bool

    Whether the two vectors agree to within eps component by component.

    Vec3::ceil

    fn Vec3::ceil(self : Vec3) -> Vec3

    The vector with every component rounded up to a whole number.

    Vec3::clamp

    fn Vec3::clamp(self : Vec3, low : Vec3, high : Vec3) -> Vec3

    The vector with every component pinned between low and high.

    Vec3::cross

    fn Vec3::cross(self : Vec3, other : Vec3) -> Vec3

    The cross product: the vector perpendicular to both, right-handed.

    Its length is the area of the parallelogram they span, so the two are parallel exactly when it is zero. cross(x, y) is z.

    Vec3::distance

    fn Vec3::distance(self : Vec3, other : Vec3) -> Float

    The distance between the two points.

    Vec3::distance_squared

    fn Vec3::distance_squared(self : Vec3, other : Vec3) -> Float

    The square of the distance between the two points.

    Vec3::div

    fn Vec3::div(self : Vec3, other : Vec3) -> Vec3

    Vec3::divide

    fn Vec3::divide(self : Vec3, divisor : Float) -> Vec3

    The vector divided by divisor, component by component.

    Dividing by zero gives an infinity or a NaN, as the scalar division does.

    Vec3::dot

    fn Vec3::dot(self : Vec3, other : Vec3) -> Float

    The dot product: length * other.length * cos(angle) between them.

    Vec3::equal

    fn Vec3::equal(Vec3, Vec3) -> Bool

    Vec3::floor

    fn Vec3::floor(self : Vec3) -> Vec3

    The vector with every component rounded down to a whole number.

    Vec3::fract

    fn Vec3::fract(self : Vec3) -> Vec3

    The vector with every component replaced by its fractional part.

    Vec3::is_finite

    fn Vec3::is_finite(self : Vec3) -> Bool

    Whether every component is finite.

    Vec3::length

    fn Vec3::length(self : Vec3) -> Float

    The length of the vector: its distance from the origin.

    Vec3::length_squared

    fn Vec3::length_squared(self : Vec3) -> Float

    The square of the length, without the square root.

    Vec3::lerp

    fn Vec3::lerp(self : Vec3, other : Vec3, t : Float) -> Vec3

    The point a fraction of the way from self to other.

    Vec3::max

    fn Vec3::max(self : Vec3, other : Vec3) -> Vec3

    The component-wise larger of the two.

    Vec3::min

    fn Vec3::min(self : Vec3, other : Vec3) -> Vec3

    The component-wise smaller of the two.

    Vec3::mul

    fn Vec3::mul(self : Vec3, other : Vec3) -> Vec3

    Vec3::neg

    fn Vec3::neg(self : Vec3) -> Vec3

    Vec3::new

    fn Vec3::new(x : Float, y : Float, z : Float) -> Vec3

    The vector from its three components.

    Vec3::normalize

    fn Vec3::normalize(self : Vec3) -> Vec3

    The vector scaled to length one, pointing the way it did.

    The zero vector has no direction, and this returns Vec3::zero() rather than a NaN: the named answer is "no direction", not "undefined". Use try_normalize where the case needs telling apart.

    Vec3::normalize_or

    fn Vec3::normalize_or(self : Vec3, fallback : Vec3) -> Vec3

    The vector scaled to length one, or fallback if it has no direction.

    Vec3::not_equal

    fn Vec3::not_equal(x : Vec3, y : Vec3) -> Bool

    Vec3::one

    fn Vec3::one() -> Vec3

    The vector with one in every component.

    Vec3::output

    fn Vec3::output(self : Vec3, logger : &Logger) -> Unit

    Vec3::project

    fn Vec3::project(self : Vec3, onto : Vec3) -> Vec3

    The component of self along onto.

    onto need not be a unit vector. A zero onto gives zero.

    Vec3::random_in_ball

    fn Vec3::random_in_ball(u1 : Float, u2 : Float, u3 : Float) -> Vec3

    A point uniformly distributed through the unit ball.

    u1, u2 and u3 are independent uniform samples in [0, 1). The radius is the cube root of u3, not u3 itself: the volume grows as the cube of the radius, so a linear radius would pile the points near the centre.

    Vec3::random_on_sphere

    fn Vec3::random_on_sphere(u1 : Float, u2 : Float) -> Vec3

    A point uniformly distributed over the surface of the unit sphere.

    u1 and u2 are independent uniform samples in [0, 1). The height is sampled uniformly rather than the polar angle, which is what keeps the distribution even: sampling the angle uniformly would crowd the poles.

    Vec3::reflect

    fn Vec3::reflect(self : Vec3, normal : Vec3) -> Vec3

    The direction self takes after bouncing off a surface with normal normal.

    self is the incoming direction and normal points out of the surface against it. Both are taken to be unit length, as they are in a shader; the result has the same length as self.

    Vec3::refract

    fn Vec3::refract(self : Vec3, normal : Vec3, eta : Float) -> Vec3?

    The direction self takes after entering a medium with normal normal and index ratio eta, or None past the critical angle.

    self and normal are taken to be unit length, normal pointing against the incoming direction, and eta the ratio of the indices of refraction on the two sides. Past the critical angle the ray is reflected whole and there is no refracted direction: None says so, and reflecting is the caller's business.

    test {
    let down : @moonmath.Vec3 = @moonmath.Vec3::new(0.0, -1.0, 0.0)
    let up = @moonmath.Vec3::new(0.0, 1.0, 0.0)
    // Straight down onto a surface and straight on through.
    let through = down.refract(up, 0.5).unwrap()
    assert_true(through.approx(down))
    }

    Vec3::reject

    fn Vec3::reject(self : Vec3, onto : Vec3) -> Vec3

    The component of self perpendicular to onto.

    Vec3::round

    fn Vec3::round(self : Vec3) -> Vec3

    The vector with every component rounded to the nearest whole number, a half going up.

    Vec3::scale

    fn Vec3::scale(self : Vec3, factor : Float) -> Vec3

    The vector multiplied by factor.

    Vec3::splat

    fn Vec3::splat(value : Float) -> Vec3

    The same value in all three components.

    Vec3::sub

    fn Vec3::sub(self : Vec3, other : Vec3) -> Vec3

    Vec3::to_array

    fn Vec3::to_array(self : Vec3) -> FixedArray[Float]

    The components in order, [x, y, z], for handing to a buffer.

    Vec3::to_repr

    Vec3::to_string

    fn Vec3::to_string(self : Vec3) -> String

    Vec3::to_vec4

    fn Vec3::to_vec4(self : Vec3, w : Float) -> Vec4

    The same vector with w appended, for a point being lifted into 4D.

    Vec3::try_normalize

    fn Vec3::try_normalize(self : Vec3) -> Vec3?

    The vector scaled to length one, or None if it has no direction.

    Vec3::unit_x

    fn Vec3::unit_x() -> Vec3

    The unit vector along +x.

    Vec3::unit_y

    fn Vec3::unit_y() -> Vec3

    The unit vector along +y.

    Vec3::unit_z

    fn Vec3::unit_z() -> Vec3

    The unit vector along +z.

    Vec3::xy

    fn Vec3::xy(self : Vec3) -> Vec2

    The first two components: the vector projected onto the xy plane.

    Vec3::xz

    fn Vec3::xz(self : Vec3) -> Vec2

    The first and third components: the projection onto the xz plane.

    Vec3::xzy

    fn Vec3::xzy(self : Vec3) -> Vec3

    The components in the order (x, z, y), swapping the last two.

    Vec3::yxz

    fn Vec3::yxz(self : Vec3) -> Vec3

    The components in the order (y, x, z), swapping the first two.

    Vec3::yz

    fn Vec3::yz(self : Vec3) -> Vec2

    The last two components: the projection onto the yz plane.

    Vec3::yzx

    fn Vec3::yzx(self : Vec3) -> Vec3

    The components in the order (y, z, x), a third of a turn of the axes.

    Vec3::zero

    fn Vec3::zero() -> Vec3

    The zero vector: the origin, and the one vector with no direction.

    Vec3::zxy

    fn Vec3::zxy(self : Vec3) -> Vec3

    The components in the order (z, x, y), the other third of a turn.

    Vec3::zyx

    fn Vec3::zyx(self : Vec3) -> Vec3

    The components backwards: (z, y, x), which turns a lookup into its mirror.

    Vec4

    pub(all) struct Vec4 {
    x : Float
    y : Float
    z : Float
    w : Float
    } derive(Eq)

    Four components: a point in homogeneous coordinates, a colour with alpha, or four numbers that travel together.

    A Vec4 is what a Mat4 multiplies, and what a shader writes into a vertex buffer: to_array gives the four in the order the buffer wants them.

    Values are immutable; every operation returns a new one.
    impl Add for Vec4
    impl Div for Vec4
    impl Mul for Vec4
    impl Neg for Vec4
    impl Show for Vec4
    impl Sub for Vec4

    Vec4::abs

    fn Vec4::abs(self : Vec4) -> Vec4

    The vector with every component replaced by its absolute value.

    Vec4::add

    fn Vec4::add(self : Vec4, other : Vec4) -> Vec4

    Vec4::approx

    fn Vec4::approx(self : Vec4, other : Vec4, eps? : Float) -> Bool

    Whether the two vectors agree to within eps component by component.

    Vec4::ceil

    fn Vec4::ceil(self : Vec4) -> Vec4

    The vector with every component rounded up to a whole number.

    Vec4::clamp

    fn Vec4::clamp(self : Vec4, low : Vec4, high : Vec4) -> Vec4

    The vector with every component pinned between low and high.

    Vec4::distance

    fn Vec4::distance(self : Vec4, other : Vec4) -> Float

    The distance between the two points, in four dimensions.

    Vec4::distance_squared

    fn Vec4::distance_squared(self : Vec4, other : Vec4) -> Float

    The square of the distance between the two points.

    Vec4::div

    fn Vec4::div(self : Vec4, other : Vec4) -> Vec4

    Vec4::divide

    fn Vec4::divide(self : Vec4, divisor : Float) -> Vec4

    The vector divided by divisor, component by component.

    Vec4::dot

    fn Vec4::dot(self : Vec4, other : Vec4) -> Float

    The dot product, over all four components.

    In homogeneous coordinates this is what a plane's dot does with a point, and what the w component of a transformed vector carries.

    Vec4::equal

    fn Vec4::equal(Vec4, Vec4) -> Bool

    Vec4::floor

    fn Vec4::floor(self : Vec4) -> Vec4

    The vector with every component rounded down to a whole number.

    Vec4::fract

    fn Vec4::fract(self : Vec4) -> Vec4

    The vector with every component replaced by its fractional part.

    Vec4::is_finite

    fn Vec4::is_finite(self : Vec4) -> Bool

    Whether every component is finite.

    Vec4::length

    fn Vec4::length(self : Vec4) -> Float

    The length of the vector, in four dimensions.

    Vec4::length_squared

    fn Vec4::length_squared(self : Vec4) -> Float

    The square of the length, without the square root.

    Vec4::lerp

    fn Vec4::lerp(self : Vec4, other : Vec4, t : Float) -> Vec4

    The point a fraction of the way from self to other, all four components.

    Vec4::max

    fn Vec4::max(self : Vec4, other : Vec4) -> Vec4

    The component-wise larger of the two.

    Vec4::min

    fn Vec4::min(self : Vec4, other : Vec4) -> Vec4

    The component-wise smaller of the two.

    Vec4::mul

    fn Vec4::mul(self : Vec4, other : Vec4) -> Vec4

    Vec4::neg

    fn Vec4::neg(self : Vec4) -> Vec4

    Vec4::new

    fn Vec4::new(x : Float, y : Float, z : Float, w : Float) -> Vec4

    The vector from its four components.

    Vec4::normalize

    fn Vec4::normalize(self : Vec4) -> Vec4

    The vector scaled to length one, or Vec4::zero() if it has no direction.

    Vec4::normalize_or

    fn Vec4::normalize_or(self : Vec4, fallback : Vec4) -> Vec4

    The vector scaled to length one, or fallback if it has no direction.

    Vec4::not_equal

    fn Vec4::not_equal(x : Vec4, y : Vec4) -> Bool

    Vec4::one

    fn Vec4::one() -> Vec4

    The vector with one in every component.

    Vec4::output

    fn Vec4::output(self : Vec4, logger : &Logger) -> Unit

    Vec4::round

    fn Vec4::round(self : Vec4) -> Vec4

    The vector with every component rounded to the nearest whole number, a half going up.

    Vec4::scale

    fn Vec4::scale(self : Vec4, factor : Float) -> Vec4

    The vector multiplied by factor.

    This is how a homogeneous position is taken out of projective space: divide by w with divide, or scale by its reciprocal.

    Vec4::splat

    fn Vec4::splat(value : Float) -> Vec4

    The same value in all four components.

    Vec4::sub

    fn Vec4::sub(self : Vec4, other : Vec4) -> Vec4

    Vec4::to_array

    fn Vec4::to_array(self : Vec4) -> FixedArray[Float]

    The components in order, [x, y, z, w], for handing to a buffer.

    Vec4::to_repr

    Vec4::to_string

    fn Vec4::to_string(self : Vec4) -> String

    Vec4::try_normalize

    fn Vec4::try_normalize(self : Vec4) -> Vec4?

    The vector scaled to length one, or None if it has no direction.

    Vec4::unit_w

    fn Vec4::unit_w() -> Vec4

    The unit vector along +w.

    Vec4::unit_x

    fn Vec4::unit_x() -> Vec4

    The unit vector along +x.

    Vec4::unit_y

    fn Vec4::unit_y() -> Vec4

    The unit vector along +y.

    Vec4::unit_z

    fn Vec4::unit_z() -> Vec4

    The unit vector along +z.

    Vec4::wzyx

    fn Vec4::wzyx(self : Vec4) -> Vec4

    All four components backwards: (w, z, y, x).

    Vec4::xy

    fn Vec4::xy(self : Vec4) -> Vec2

    The first two components.

    Vec4::xyz

    fn Vec4::xyz(self : Vec4) -> Vec3

    The first three components: the position a homogeneous point names, before the division by w.

    Vec4::zero

    fn Vec4::zero() -> Vec4

    The zero vector.

    approx

    fn approx(a : Float, b : Float, eps? : Float) -> Bool

    Whether two numbers are the same to within eps.

    Without an epsilon the comparison uses default_epsilon, scaled up for values whose magnitude would swamp an absolute tolerance. Equality itself is exact — == on two numbers that took different paths to the same place rarely holds — so this is the test callers want for results of arithmetic.

    test {
    let a : Float = 0.1 + 0.2
    assert_true(@moonmath.approx(a, 0.3))
    assert_false(@moonmath.approx(a, 0.31))
    assert_true(@moonmath.approx(a, 0.31, eps=0.02))
    }

    approx_angle

    fn approx_angle(a : Float, b : Float, eps? : Float) -> Bool

    Whether two angles — in radians, wrapping at tau — point the same way.

    0 and tau are the same direction, and so are -pi and pi; comparing them as plain numbers says otherwise.

    clamp01

    fn clamp01(value : Float) -> Float

    value pinned to 0..1.

    default_epsilon

    let default_epsilon : Float

    The absolute tolerance approx falls back on when the caller gives none.

    It is a compromise: large enough to cover the rounding of a handful of operations at this precision, small enough that values a user would call different do not compare equal.

    half_pi

    let half_pi : Float

    Half a turn in radians: pi / 2. A quarter of tau.

    hsv_to_rgb

    fn hsv_to_rgb(hsv : Color3) -> Color3

    The colour a hue, saturation and value describe, read the way rgb_to_hsv writes them.

    inverse_lerp

    fn inverse_lerp(start : Float, end : Float, value : Float) -> Float

    Where value sits between start and end, as a fraction: the inverse of lerp.

    Returns zero when the two ends are equal — there is no fraction between them, and zero is the answer that keeps lerp from producing a NaN.

    lerp

    fn lerp(a : Float, b : Float, t : Float) -> Float

    The point t of the way from a to b: a at zero, b at one.

    t outside 0..1 extrapolates, which is what callers building animation curves usually want; clamp the result if it is not.
    let pi : Float

    The ratio of a circle's circumference to its diameter.

    quarter_pi

    let quarter_pi : Float

    A quarter turn in radians: pi / 4, the angle whose tangent is one.

    remap

    fn remap(value : Float, from_start : Float, from_end : Float, to_start : Float, to_end : Float) -> Float

    value taken from the range from_start..from_end and placed in to_start..to_end.

    It is lerp(to_start, to_end, inverse_lerp(from_start, from_end, value)): the value keeps its position within the range, and values outside the source range land outside the destination range by the same proportion.

    rgb_to_hsv

    fn rgb_to_hsv(color : Color3) -> Color3

    The hue, saturation and value of a colour, packed into a Color3 as r the hue, g the saturation and b the value.

    The hue is a fraction of a turn in 0..1 — a turn rather than degrees or radians, so it lines up with the rest of a graphics pipeline — and the other two are in 0..1. A grey has no hue and comes back as zero.

    smoothstep

    fn smoothstep(start : Float, end : Float, value : Float) -> Float

    A smooth step from zero at start to one at end, zero slope at both ends.

    The curve is 3t² - 2t³ over the normalised position, which is the cheapest easing whose first derivative vanishes at both ends — no visible corner where the animation starts or stops.

    A zero-width range has no slope to smooth: it steps at start.

    tau

    let tau : Float

    A full turn in radians: 2 * pi. The period of every angle here.

    to_degrees

    fn to_degrees(radians : Float) -> Float

    radians converted to degrees.

    to_radians

    fn to_radians(degrees : Float) -> Float

    degrees converted to radians.

    wrap

    fn wrap(value : Float, span : Float) -> Float

    value folded into 0..span.

    Angles and texture coordinates repeat, and a value that has run past the end of its range has to come back to the start rather than stop there. A span of zero or less has nothing to fold into and gives zero.

    test {
    assert_eq(@moonmath.wrap(7.5, 5.0), 2.5)
    assert_eq(@moonmath.wrap(-1.0, 5.0), 4.0)
    }

    wrap_range

    fn wrap_range(value : Float, start : Float, end : Float) -> Float

    value folded into start..end.

    wrap(value - start, end - start) + start, with the same half-open result.