fixarray
Fixed-extent vectors and matrices — the same mathematics as an ndarray::NDArray, with the shape moved into the type, for the small-and-hot regime (a renderer's transforms, a physics solver's contact frames, a filter's small state). Header-only; import fixarray (it auto-links ndarray for its element concepts). The heavy shape-generic numerics on the dynamic NDArray (LU/QR/SVD/eigen) live in the sibling linalg module.
The type
An NDArray carries its shape at runtime and its elements on the heap. That is what makes it general — and, for a 3-D direction or a 4×4 transform, it is the whole cost: an allocation and an indirection to move sixteen floats.
fixarray::Fixed<T, Dims...> is the same idea with the shape in the type. The elements live inline, so the value is trivially copyable and nothing allocates; the loops have compile-time trip counts and auto-vectorize. These are the types for high-performance work — transforms, contact frames, small filter state — where the same matrix is built and consumed millions of times a second. Everything else matches NDArray: the element types, the (row, column) index, numpy's vocabulary, and the answers.
using namespace cheatah::fixarray;
vec3f up{0.0F, 1.0F, 0.0F}; // 12 bytes, no allocation
mat4f mvp = projection * view; // 64 bytes — exactly a push constant
vec3f n = normalize(cross(up, dir));Aliases: vec2f…vec4f, vec2d…vec4d, mat2f…mat4f, mat2d…mat4d; Vec<T,N> and Mat<T,R,C> for anything else. Rank 1 (vector) and rank 2 (matrix).
From cheatah
import fixarray and use it from a .purr program — construct with the call form and operate with the free functions:
import io
import fixarray
fn length2(v: fixarray.Fixed<f32, 3>) -> f32 { # module-qualified param/return
return fixarray.dot(v, v)
}
fn main() {
let a = fixarray.vec3f(1.0, 2.0, 3.0) # construct (deduced type)
let b: fixarray.vec3f = fixarray.vec3f(4.0, 5.0, 6.0) # or annotate the type
io.print(fixarray.dot(a, b)) # 32
let bytes: fixarray.Vec<u8, 3> = fixarray.Vec<u8, 3>(1, 2, 3) # narrow element (1 byte each)
io.print(length2(a)) # 14
}
main()Aliases (vec3f, mat4f, …) and the explicit fixarray.Fixed<T, Dims…> / fixarray.Vec<T, N> spellings all work as let/field/parameter/return annotations. A parameter of a fixarray type binds by reference, so pass a named vector (an lvalue) to your own functions. import fixarray alone pulls in ndarray transitively.
A matrix is stored column-major, unlike NDArray. The indexing you write does not change, but data() hands back columns. That is deliberate and measured: m * v becomes a sum of scaled columns — contiguous and vertical — instead of four horizontal dot products behind a shuffle network, and the buffer is already in the order GPU APIs expect, so uploading a transform is a copy rather than a transpose.
Speed
Benchmarked against GLM across the complete overlap of the two APIs — 160 pairs: every vector and matrix operation (arithmetic, dot, cross, length, normalize, distance, reflect, min/max/clamp, mix, step/smoothstep, matmul, transpose, determinant, inverse, matrixCompMult, outerProduct, inverseTranspose), at sizes 2/3/4, in both float and double (fixed_glm_bench.cpp). Both sides compile in the same translation unit with the same flags, so neither gets an instruction set the other lacks — and the benchmark verifies the two produce identical results before it times either, so a fast wrong answer can never masquerade as a win.
Fixed is faster than or at parity with GLM on every operation, and slower on none — the exact tally is in the generated table below, because a count restated in prose is a count that drifts the moment someone re-measures. A representative sample:
operation |
| GLM | ||
|---|---|---|---|---|
| 0.66 ns ±0.01 | 1.78 ns ±0.05 | 2.69× | faster |
| 3.38 ns ±0.04 | 5.75 ns ±0.14 | 1.70× | faster |
| 0.67 ns ±0.01 | 1.36 ns ±0.06 | 2.03× | faster |
| 12.15 ns ±0.31 | 16.83 ns ±0.36 | 1.38× | faster |
| 0.44 ns ±0.01 | 0.82 ns ±0.01 | 1.85× | faster |
| 0.85 ns ±0.02 | 0.99 ns ±0.02 | 1.17× | parity — gap 0.14 ns |
Read the verdict column, not the ratio. A row can be ahead on ratio and still be called parity, because a difference counts here only when it clears both 1.15× and an absolute 0.25 ns — about one cycle. Below that floor the harness's own DoNotOptimize scaffolding is a larger effect than the code being measured, and on 4-element vectors most operations live there. That floor is also why this tally is smaller than it once was: the earlier 37 faster / 123 parity count came from a best-of-N minimum with each library's cases timed in one consecutive block. Re-measured with interleaved repetitions and medians, a batch of operations moved from "faster" to "parity". None moved to "slower" — the claim that matters survived the stricter method.
The bench_gate.sh hard gate keeps it that way: it fails the build if any pair regresses past GLM (tolerant by ratio and an absolute floor, with a confirmation re-run so sub-nanosecond noise never flakes it).
Choices that earn it, each a comment in fixarray.hpp: a matrix is stored column-major, so m * v is a sum of contiguous columns rather than horizontal dot products behind a shuffle network; dot sums pairwise and, at width ≥ 4, packs the products into one SIMD multiply (also lowering the rounding error to O(log n)); normalize takes one reciprocal then multiplies; min/max/abs/clamp are branchless always-writes that lower to minps/maxps; and every elementwise op builds its result in one pass through from_indices (no default-zero then overwrite). No intrinsics — the code is shaped so the compiler vectorizes it, no library to link at all.
Full GLM comparison — all 160 operations (ns, lower is better; ± is the IQR over 9 interleaved repetitions)
Vectors
operation | type |
| GLM | ratio | |
|---|---|---|---|---|---|
| vec3d | 0.48 ±0.03 | 0.85 ±0.04 | 0.57× | 🟢 faster |
| vec3f | 0.46 ±0.03 | 0.83 ±0.04 | 0.56× | 🟢 faster |
| vec4d | 0.47 ±0.03 | 0.79 ±0.07 | 0.59× | 🟢 faster |
| vec4f | 0.45 ±0.04 | 0.86 ±0.05 | 0.53× | 🟢 faster |
| vec2d | 0.46 ±0.02 | 0.47 ±0.01 | 0.98× | ⬜ parity |
| vec2f | 0.35 ±0.05 | 0.23 ±0.01 | 1.53× | ⬜ parity |
| vec3d | 0.46 ±0.02 | 0.46 ±0.02 | 1.02× | ⬜ parity |
| vec3f | 0.39 ±0.07 | 0.46 ±0.03 | 0.85× | ⬜ parity |
| vec4d | 0.46 ±0.02 | 0.48 ±0.03 | 0.98× | ⬜ parity |
| vec4f | 0.47 ±0.02 | 0.47 ±0.03 | 0.99× | ⬜ parity |
| vec3d | 0.46 ±0.03 | 0.47 ±0.03 | 0.96× | ⬜ parity |
| vec3f | 0.51 ±0.03 | 0.52 ±0.02 | 0.97× | ⬜ parity |
| vec4d | 0.47 ±0.02 | 0.46 ±0.03 | 1.01× | ⬜ parity |
| vec4f | 0.45 ±0.02 | 0.45 ±0.02 | 1.00× | ⬜ parity |
| vec3d | 0.71 ±0.04 | 0.70 ±0.06 | 1.01× | ⬜ parity |
| vec3f | 0.79 ±0.03 | 0.81 ±0.08 | 0.98× | ⬜ parity |
| vec3d | 0.69 ±0.05 | 0.85 ±0.06 | 0.81× | ⬜ parity |
| vec3f | 0.73 ±0.03 | 0.81 ±0.01 | 0.90× | ⬜ parity |
| vec4d | 1.17 ±0.05 | 1.19 ±0.05 | 0.98× | ⬜ parity |
| vec4f | 1.36 ±0.05 | 1.38 ±0.06 | 0.99× | ⬜ parity |
| vec3d | 1.36 ±0.03 | 1.39 ±0.08 | 0.98× | ⬜ parity |
| vec3f | 0.82 ±0.00 | 0.90 ±0.03 | 0.92× | ⬜ parity |
| vec4d | 1.23 ±0.08 | 1.23 ±0.07 | 1.01× | ⬜ parity |
| vec4f | 1.31 ±0.03 | 1.31 ±0.08 | 1.00× | ⬜ parity |
| vec2d | 0.45 ±0.03 | 0.47 ±0.02 | 0.97× | ⬜ parity |
| vec2f | 0.24 ±0.01 | 0.23 ±0.01 | 1.02× | ⬜ parity |
| vec3d | 0.45 ±0.02 | 0.46 ±0.01 | 0.98× | ⬜ parity |
| vec3f | 0.47 ±0.01 | 0.45 ±0.02 | 1.03× | ⬜ parity |
| vec4d | 0.47 ±0.02 | 0.46 ±0.03 | 1.02× | ⬜ parity |
| vec4f | 0.46 ±0.02 | 0.46 ±0.02 | 1.00× | ⬜ parity |
| vec2d | 0.47 ±0.01 | 0.46 ±0.01 | 1.03× | ⬜ parity |
| vec2f | 0.40 ±0.01 | 0.37 ±0.02 | 1.09× | ⬜ parity |
| vec3d | 0.58 ±0.02 | 0.47 ±0.06 | 1.24× | ⬜ parity |
| vec3f | 0.59 ±0.03 | 0.54 ±0.02 | 1.10× | ⬜ parity |
| vec4d | 0.69 ±0.04 | 0.70 ±0.06 | 0.98× | ⬜ parity |
| vec4f | 0.91 ±0.05 | 1.05 ±0.07 | 0.86× | ⬜ parity |
| vec2d | 0.46 ±0.02 | 0.46 ±0.01 | 1.00× | ⬜ parity |
| vec2f | 0.31 ±0.02 | 0.32 ±0.01 | 0.97× | ⬜ parity |
| vec3d | 0.49 ±0.03 | 0.44 ±0.02 | 1.10× | ⬜ parity |
| vec3f | 0.47 ±0.03 | 0.46 ±0.03 | 1.01× | ⬜ parity |
| vec4d | 0.53 ±0.03 | 0.53 ±0.02 | 1.00× | ⬜ parity |
| vec4f | 0.62 ±0.03 | 0.70 ±0.03 | 0.90× | ⬜ parity |
| vec2d | 1.38 ±0.09 | 1.36 ±0.05 | 1.01× | ⬜ parity |
| vec2f | 0.68 ±0.04 | 0.68 ±0.02 | 1.00× | ⬜ parity |
| vec3d | 1.41 ±0.06 | 1.39 ±0.04 | 1.01× | ⬜ parity |
| vec3f | 0.73 ±0.03 | 0.70 ±0.03 | 1.05× | ⬜ parity |
| vec4d | 1.36 ±0.04 | 1.41 ±0.03 | 0.96× | ⬜ parity |
| vec4f | 0.77 ±0.03 | 0.84 ±0.03 | 0.91× | ⬜ parity |
| vec3d | 0.46 ±0.03 | 0.46 ±0.02 | 1.01× | ⬜ parity |
| vec3f | 0.40 ±0.06 | 0.39 ±0.06 | 1.03× | ⬜ parity |
| vec4d | 0.45 ±0.01 | 0.45 ±0.00 | 1.00× | ⬜ parity |
| vec4f | 0.46 ±0.02 | 0.48 ±0.03 | 0.95× | ⬜ parity |
| vec3d | 0.47 ±0.01 | 0.47 ±0.02 | 1.02× | ⬜ parity |
| vec3f | 0.43 ±0.02 | 0.45 ±0.05 | 0.97× | ⬜ parity |
| vec4d | 0.47 ±0.03 | 0.47 ±0.02 | 1.00× | ⬜ parity |
| vec4f | 0.46 ±0.02 | 0.47 ±0.03 | 0.98× | ⬜ parity |
| vec3d | 0.64 ±0.06 | 0.58 ±0.13 | 1.10× | ⬜ parity |
| vec3f | 0.63 ±0.06 | 0.60 ±0.07 | 1.06× | ⬜ parity |
| vec4d | 0.59 ±0.03 | 0.69 ±0.06 | 0.85× | ⬜ parity |
| vec4f | 0.56 ±0.02 | 0.58 ±0.04 | 0.97× | ⬜ parity |
| vec2d | 0.46 ±0.01 | 0.46 ±0.01 | 1.00× | ⬜ parity |
| vec2f | 0.25 ±0.02 | 0.23 ±0.01 | 1.08× | ⬜ parity |
| vec3d | 0.47 ±0.03 | 0.46 ±0.02 | 1.02× | ⬜ parity |
| vec3f | 0.46 ±0.02 | 0.47 ±0.01 | 0.96× | ⬜ parity |
| vec4d | 0.48 ±0.05 | 0.46 ±0.01 | 1.04× | ⬜ parity |
| vec4f | 0.46 ±0.02 | 0.46 ±0.02 | 1.00× | ⬜ parity |
| vec2d | 0.47 ±0.02 | 0.45 ±0.01 | 1.03× | ⬜ parity |
| vec2f | 0.23 ±0.01 | 0.24 ±0.01 | 0.99× | ⬜ parity |
| vec3d | 0.46 ±0.02 | 0.45 ±0.01 | 1.01× | ⬜ parity |
| vec3f | 0.46 ±0.01 | 0.47 ±0.03 | 0.98× | ⬜ parity |
| vec4d | 0.48 ±0.03 | 0.45 ±0.02 | 1.07× | ⬜ parity |
| vec4f | 0.46 ±0.01 | 0.46 ±0.01 | 1.00× | ⬜ parity |
| vec2d | 2.38 ±0.07 | 2.35 ±0.04 | 1.01× | ⬜ parity |
| vec2f | 1.39 ±0.03 | 1.37 ±0.08 | 1.02× | ⬜ parity |
| vec3d | 2.39 ±0.11 | 2.33 ±0.10 | 1.02× | ⬜ parity |
| vec3f | 1.47 ±0.07 | 1.42 ±0.06 | 1.04× | ⬜ parity |
| vec4d | 2.46 ±0.15 | 2.36 ±0.17 | 1.04× | ⬜ parity |
| vec4f | 1.50 ±0.06 | 1.46 ±0.03 | 1.03× | ⬜ parity |
| vec3d | 2.92 ±0.10 | 2.89 ±0.15 | 1.01× | ⬜ parity |
| vec3f | 2.68 ±0.15 | 2.59 ±0.07 | 1.03× | ⬜ parity |
| vec4d | 3.41 ±0.15 | 3.48 ±0.06 | 0.98× | ⬜ parity |
| vec4f | 3.45 ±0.08 | 3.80 ±0.08 | 0.91× | ⬜ parity |
| vec3d | 0.99 ±0.04 | 1.19 ±0.03 | 0.83× | ⬜ parity |
| vec3f | 0.92 ±0.05 | 1.28 ±0.03 | 0.72× | 🟢 faster |
| vec4d | 0.97 ±0.04 | 1.63 ±0.12 | 0.60× | 🟢 faster |
| vec4f | 0.76 ±0.03 | 1.83 ±0.11 | 0.42× | 🟢 faster |
| vec3d | 1.33 ±0.02 | 1.16 ±0.05 | 1.15× | ⬜ parity |
| vec3f | 1.29 ±0.04 | 1.21 ±0.05 | 1.06× | ⬜ parity |
| vec4d | 1.32 ±0.11 | 1.18 ±0.05 | 1.12× | ⬜ parity |
| vec4f | 1.41 ±0.06 | 1.28 ±0.05 | 1.10× | ⬜ parity |
| vec3d | 0.48 ±0.02 | 0.49 ±0.01 | 0.98× | ⬜ parity |
| vec3f | 0.46 ±0.01 | 0.47 ±0.02 | 0.99× | ⬜ parity |
| vec4d | 0.47 ±0.03 | 0.47 ±0.05 | 1.00× | ⬜ parity |
| vec4f | 0.47 ±0.00 | 0.72 ±0.05 | 0.65× | ⬜ parity |
| vec2d | 0.47 ±0.02 | 0.46 ±0.01 | 1.02× | ⬜ parity |
| vec2f | 0.24 ±0.01 | 0.23 ±0.01 | 1.02× | ⬜ parity |
| vec3d | 0.47 ±0.01 | 0.45 ±0.02 | 1.03× | ⬜ parity |
| vec3f | 0.43 ±0.04 | 0.38 ±0.08 | 1.14× | ⬜ parity |
| vec4d | 0.47 ±0.01 | 0.45 ±0.01 | 1.04× | ⬜ parity |
| vec4f | 0.48 ±0.01 | 0.46 ±0.04 | 1.05× | ⬜ parity |
Matrices
operation | type |
| GLM | ratio | |
|---|---|---|---|---|---|
| mat2d | 0.46 ±0.02 | 0.47 ±0.02 | 0.99× | ⬜ parity |
| mat2f | 0.46 ±0.02 | 0.45 ±0.01 | 1.03× | ⬜ parity |
| mat3d | 0.95 ±0.14 | 0.92 ±0.03 | 1.03× | ⬜ parity |
| mat3f | 0.69 ±0.04 | 1.15 ±0.02 | 0.60× | 🟢 faster |
| mat4d | 1.60 ±0.19 | 1.48 ±0.10 | 1.08× | ⬜ parity |
| mat4f | 0.72 ±0.04 | 1.41 ±0.04 | 0.51× | 🟢 faster |
| mat3d | 0.96 ±0.07 | 0.92 ±0.06 | 1.04× | ⬜ parity |
| mat3f | 0.70 ±0.02 | 1.15 ±0.03 | 0.61× | 🟢 faster |
| mat4d | 1.52 ±0.13 | 1.63 ±0.18 | 0.93× | ⬜ parity |
| mat4f | 0.76 ±0.16 | 1.36 ±0.09 | 0.56× | 🟢 faster |
| mat2d | 0.42 ±0.02 | 0.42 ±0.02 | 1.01× | ⬜ parity |
| mat2f | 0.46 ±0.02 | 0.46 ±0.02 | 1.01× | ⬜ parity |
| mat3d | 1.16 ±0.04 | 1.16 ±0.10 | 1.00× | ⬜ parity |
| mat3f | 1.16 ±0.04 | 1.13 ±0.07 | 1.03× | ⬜ parity |
| mat4d | 3.83 ±0.12 | 3.75 ±0.20 | 1.02× | ⬜ parity |
| mat4f | 3.94 ±0.14 | 3.64 ±0.28 | 1.08× | ⬜ parity |
| mat2d | 0.46 ±0.02 | 0.46 ±0.01 | 0.99× | ⬜ parity |
| mat2f | 0.23 ±0.01 | 0.23 ±0.01 | 0.99× | ⬜ parity |
| mat3d | 1.13 ±0.03 | 1.39 ±0.03 | 0.81× | 🟢 faster |
| mat3f | 0.92 ±0.02 | 0.91 ±0.02 | 1.01× | ⬜ parity |
| mat4d | 1.13 ±0.02 | 2.47 ±0.14 | 0.46× | 🟢 faster |
| mat4f | 0.68 ±0.04 | 1.86 ±0.08 | 0.37× | 🟢 faster |
| mat2d | 0.98 ±0.04 | 0.94 ±0.04 | 1.04× | ⬜ parity |
| mat2f | 1.04 ±0.02 | 0.94 ±0.01 | 1.10× | ⬜ parity |
| mat3d | 3.63 ±0.13 | 3.80 ±0.11 | 0.95× | ⬜ parity |
| mat3f | 3.61 ±0.16 | 3.80 ±0.16 | 0.95× | ⬜ parity |
| mat4d | 12.82 ±0.66 | 17.51 ±0.70 | 0.73× | 🟢 faster |
| mat4f | 11.65 ±0.39 | 15.11 ±0.89 | 0.77× | 🟢 faster |
| mat3d | 4.62 ±0.38 | 4.77 ±0.29 | 0.97× | ⬜ parity |
| mat3f | 4.50 ±0.36 | 4.06 ±0.16 | 1.11× | ⬜ parity |
| mat4d | 14.15 ±1.15 | 15.23 ±0.37 | 0.93× | ⬜ parity |
| mat4f | 11.82 ±1.52 | 14.60 ±0.63 | 0.81× | 🟢 faster |
| mat2d | 0.94 ±0.06 | 0.91 ±0.01 | 1.03× | ⬜ parity |
| mat2f | 0.92 ±0.02 | 0.94 ±0.05 | 0.98× | ⬜ parity |
| mat3d | 3.43 ±0.08 | 3.44 ±0.13 | 1.00× | ⬜ parity |
| mat3f | 3.71 ±0.07 | 3.74 ±0.12 | 0.99× | ⬜ parity |
| mat4d | 5.95 ±0.27 | 6.34 ±0.76 | 0.94× | ⬜ parity |
| mat4f | 3.53 ±0.10 | 6.04 ±0.34 | 0.59× | 🟢 faster |
| mat2d | 0.50 ±0.05 | 0.49 ±0.01 | 1.02× | ⬜ parity |
| mat2f | 0.47 ±0.08 | 0.48 ±0.06 | 0.98× | ⬜ parity |
| mat3d | 1.19 ±0.15 | 1.03 ±0.07 | 1.16× | ⬜ parity |
| mat3f | 1.02 ±0.11 | 1.00 ±0.05 | 1.03× | ⬜ parity |
| mat4d | 1.53 ±0.13 | 1.51 ±0.15 | 1.01× | ⬜ parity |
| mat4f | 1.46 ±0.08 | 1.46 ±0.06 | 1.00× | ⬜ parity |
| mat2d | 0.50 ±0.02 | 0.47 ±0.01 | 1.05× | ⬜ parity |
| mat2f | 0.47 ±0.05 | 0.46 ±0.03 | 1.02× | ⬜ parity |
| mat3d | 0.91 ±0.03 | 0.91 ±0.03 | 1.00× | ⬜ parity |
| mat3f | 0.70 ±0.05 | 0.91 ±0.02 | 0.76× | ⬜ parity |
| mat4d | 1.18 ±0.02 | 1.19 ±0.07 | 0.99× | ⬜ parity |
| mat4f | 0.69 ±0.04 | 1.41 ±0.05 | 0.49× | 🟢 faster |
| mat3d | 0.93 ±0.08 | 0.93 ±0.02 | 1.01× | ⬜ parity |
| mat3f | 0.95 ±0.08 | 0.94 ±0.05 | 1.01× | ⬜ parity |
| mat4d | 1.29 ±0.10 | 1.29 ±0.07 | 1.00× | ⬜ parity |
| mat4f | 0.77 ±0.05 | 1.32 ±0.08 | 0.59× | 🟢 faster |
| mat2d | 0.68 ±0.01 | 0.68 ±0.02 | 0.99× | ⬜ parity |
| mat2f | 0.47 ±0.02 | 0.48 ±0.03 | 0.98× | ⬜ parity |
| mat3d | 1.37 ±0.05 | 1.38 ±0.06 | 0.99× | ⬜ parity |
| mat3f | 1.17 ±0.03 | 1.15 ±0.10 | 1.01× | ⬜ parity |
| mat4d | 2.08 ±0.07 | 2.02 ±0.06 | 1.03× | ⬜ parity |
| mat4f | 2.09 ±0.15 | 2.08 ±0.11 | 1.00× | ⬜ parity |
20 faster, 140 at parity, 0 slower across 160 operations.
Classes
Fixed— A fixed-extent, inline-stored array — an cheatah::ndarray::NDArray whose shape lives in the type.
Functions
Inner product of two vectors — Σ aᵢbᵢ, the same quantity dot(const NDArray&, const NDArray&) computes, without the allocation.
Summed pairwise, so it is both faster and more accurate than a left-to-right accumulation.
T | the element type. |
N | the length. |
a, b | the vectors. |
the inner product.
O(N).
none.
Fixarray.DotAndCrossCross product of two 3-vectors — the vector perpendicular to both, right-handed.
T | the element type. |
a, b | the vectors. |
a × b.
O(1).
none.
Fixarray.DotAndCrossThe squared Euclidean length of a vector — dot(v, v).
Prefer it to norm when only comparing lengths: it avoids the square root.
T | the element type. |
N | the length. |
v | the vector. |
Σ vᵢ².
O(N).
none.
for a complex element this is the bilinear Σ vᵢ² (matching dot), not the Hermitian Σ |vᵢ|² — it is the squared Euclidean LENGTH only for real elements.
Fixarray.NormAndNormalizeThe Euclidean length of a vector.
T | the element type; floating-point, since the result is a root. |
N | the length. |
v | the vector. |
sqrt(Σ vᵢ²).
O(N).
none.
Fixarray.NormAndNormalizeThe unit vector pointing the same way as v.
T | the element type; floating-point. |
N | the length. |
v | the vector; must not be the zero vector. |
v / norm(v).
std::domain_error | when |
O(N).
none.
Fixarray.NormAndNormalizeThe transpose of a matrix — rows become columns.
T | the element type. |
R | the rows. |
C | the columns. |
m | the matrix. |
the C×R transpose.
O(R·C).
none.
Fixarray.TransposeAndTraceThe trace of a square matrix — the sum of its diagonal.
T | the element type. |
N | the dimension. |
m | the matrix. |
Σ mᵢᵢ.
O(N).
none.
Fixarray.TransposeAndTraceMatrix product — the same A·B matmul(const NDArray&, const NDArray&) computes, with the shapes checked by the compiler rather than at runtime.
T | the element type. |
R | the rows of |
K | the shared dimension. |
C | the columns of |
a, b | the matrices. |
the R×C product.
O(R·K·C).
none.
Fixarray.Matmuloperator* · 2 overloads
Matrix product, spelled a * b.
T | the element type. |
R | the rows of |
K | the shared dimension. |
C | the columns of |
a, b | the matrices. |
matmul(a, b).
O(R·K·C).
none.
Fixarray.Matmuldeterminant · 3 overloads
The determinant of a 2×2 matrix, in closed form.
T | the element type. |
m | the matrix. |
det(m).
O(1).
none.
Fixarray.DeterminantAndInverseinverse · 3 overloads
The inverse of a 2×2 matrix, in closed form.
T | the element type; floating-point, since the inverse divides. |
m | the matrix; must be non-singular. |
m⁻¹.
std::domain_error | when |
O(1).
none.
Fixarray.DeterminantAndInverseThe Euclidean distance between two points — norm(a - b).
T | the element type; floating-point, since the result is a root. |
N | the dimension. |
a, b | the points. |
‖a − b‖.
O(N).
none.
Fixarray.GeometryThe squared distance between two points — squared_norm(a - b).
Prefer it to distance when only comparing distances: it skips the square root.
T | the element type. |
N | the dimension. |
a, b | the points. |
‖a − b‖².
O(N).
none.
Fixarray.GeometryReflect an incident vector about a surface normal — I − 2 (N·I) N, the GLSL reflect.
normal is assumed unit length, as GLSL requires.
T | the element type. |
N | the dimension. |
incident | the incoming vector. |
normal | the unit surface normal. |
the reflected vector.
O(N).
none.
Fixarray.GeometryRefract an incident vector through a surface — the GLSL refract.
incident and normal are assumed unit length. On total internal reflection (a negative radicand) the result is the zero vector, exactly as GLSL specifies.
T | the element type; floating-point. |
N | the dimension. |
incident | the unit incoming vector. |
normal | the unit surface normal. |
eta | the ratio of indices of refraction (source over destination). |
the refracted vector, or the zero vector under total internal reflection.
O(N).
none.
Fixarray.GeometryVec< T, N > faceforward(const Vec< T, N > &n, const Vec< T, N > &incident, const Vec< T, N > &reference)
source#
Orient a normal to face a viewer — the GLSL faceforward: return n when it already points against the incident direction, -n otherwise.
Used to keep a surface normal on the camera's side.
T | the element type. |
N | the dimension. |
n | the normal to orient. |
incident | the incident vector. |
reference | the reference normal the result is oriented against. |
n or -n.
O(N).
none.
Fixarray.GeometryThe absolute value of every element.
T | the element type; a real number, so |
Dims | the extents. |
x | the array. |
|x| elementwise.
O(size).
none.
Fixarray.CommonUnaryThe sign of every element: -1, 0, or +1.
T | the element type; a real number. |
Dims | the extents. |
x | the array. |
the elementwise sign.
O(size).
none.
Fixarray.CommonUnarymin · 2 overloads
The smaller of each corresponding pair of elements.
T | the element type; a real number. |
Dims | the extents. |
a, b | the arrays. |
min(aᵢ, bᵢ) elementwise.
O(size).
none.
Fixarray.MinMaxClampmax · 2 overloads
The larger of each corresponding pair of elements.
T | the element type; a real number. |
Dims | the extents. |
a, b | the arrays. |
max(aᵢ, bᵢ) elementwise.
O(size).
none.
Fixarray.MinMaxClampclamp · 2 overloads
Constrain every element to [lo, hi] — the GLSL clamp with scalar bounds, the common case of pinning a colour to [0, 1].
T | the element type; a real number. |
Dims | the extents. |
x | the array. |
lo | the lower bound. |
hi | the upper bound. |
O(size).
none.
Fixarray.MinMaxClampmix · 2 overloads
Linear interpolation — the GLSL mix: a (1 − t) + b t, with a scalar blend t (0 gives a, 1 gives b).
Composed from the operators, so it inherits their vectorization.
T | the element type; floating-point. |
Dims | the extents. |
a, b | the endpoints. |
t | the blend factor. |
the interpolated array.
O(size).
none.
Fixarray.MixStepA step at edge — the GLSL step: 0 where an element is below edge, 1 at or above.
T | the element type; a real number. |
Dims | the extents. |
edge | the threshold. |
x | the array. |
xᵢ < edge ? 0 : 1 elementwise.
O(size).
none.
Fixarray.MixStepA smooth Hermite transition from 0 to 1 across [edge0, edge1] — the GLSL smoothstep, with everything below edge0 giving 0 and everything above edge1 giving 1.
T | the element type; floating-point. |
Dims | the extents. |
edge0 | the lower edge. |
edge1 | the upper edge. |
x | the array. |
the smoothstepped array.
O(size).
none.
Fixarray.MixStepThe elementwise (Hadamard) product — the GLSL matrixCompMult.
Named apart from operator* precisely because * is the matrix product; this multiplies corresponding entries.
T | the element type. |
R | the rows. |
C | the columns. |
a, b | the matrices. |
the elementwise product.
O(R·C).
none.
Fixarray.MatrixExtrasThe outer product of a column and a row — the GLSL outerProduct: an R×C matrix whose (i, j) entry is c[i] · r[j].
A rank-one update, the workhorse of a covariance accumulation.
T | the element type. |
R | the length of |
C | the length of |
c | the column vector. |
r | the row vector. |
the R×C outer product.
O(R·C).
none.
Fixarray.MatrixExtrasThe inverse transpose of a matrix — transpose(inverse(m)), the GLSL inverseTranspose.
This is the matrix that carries normals correctly under a non-uniform transform, so lighting stays right.
T | the element type; floating-point. |
N | the dimension. |
m | the matrix; must be non-singular. |
(m⁻¹)ᵀ.
std::domain_error | when |
O(1) at the fixed sizes.
none.
Fixarray.MatrixExtrasExtract one row of a matrix as a vector.
T | the element type. |
R | the rows. |
C | the columns. |
Ix | the index type: an integer, or a scoped |
m | the matrix. |
i | the row, |
the C-vector of that row.
O(C).
none.
Fixarray.NamedRowsAndColumnsExtract one column of a matrix as a vector — a basis vector of the transform.
This is the axis a scoped enum was made to name: column(view, Axis::Right).
T | the element type. |
R | the rows. |
C | the columns. |
Ix | the index type: an integer, or a scoped |
m | the matrix. |
j | the column, |
the R-vector of that column.
O(R).
none.
Fixarray.NamedRowsAndColumnsRender v the way an NDArray renders — numpy-style nested brackets, each element through the SHARED scalar formatter (so i8/u8 elements print as NUMBERS, f32/f64 plainly, and a complex as a+bj).
A vector is [a, b, c]; a matrix is [[…], […]] in reading (row, column) order — regardless of the column-major storage. This is what io.print/io.str/str() show.
v | the value to format. |
the bracketed text.
O(Fixed::size).
the result string.
Stream v (the nested-bracket to_string form), so a Fixed is directly Streamable — a cheatah io.print(v) / io.str(v) finds this by ADL, exactly as it does for an NDArray or a primitive.
os | the stream. |
v | the value. |
os.
O(Fixed::size).
the intermediate string.
Constants & variables
The product of a pack of extents — a fixed array's element count, 1 for an empty pack.
Dims | the extents. |
Types
A fixed-extent vector of N elements.
T | the element type. |
N | the length. |
A fixed-extent matrix of R rows and C columns — column-major storage, mathematical (row, col) indexing (see the file doc).
T | the element type. |
R | the rows. |
C | the columns. |
A 2-D vector of float.
A 3-D vector of float — a direction, a position, a colour.
A 4-D vector of float — a homogeneous point, an RGBA colour.
A 2-D vector of double.
A 3-D vector of double.
A 4-D vector of double.
A 2×2 matrix of float.
A 3×3 matrix of float — a rotation, or a normal matrix.
A 4×4 matrix of float — a transform; exactly the 64 bytes of a push constant.
A 2×2 matrix of double.
A 3×3 matrix of double.
A 4×4 matrix of double.
