Vecmat 0.2.3
C math and linear algebra library for 2D/3D graphics, physics, and science.
Loading...
Searching...
No Matches
Vecmat

A simple math and linear algebra library in C for 2D/3D graphics, machine learning, physics, and science.

Vecmat is a heartfelt ❤ love letter ️to the C programming language — with emphasis the elegance, simplicity and readability of the language, even for scenarios where other languages might seem more suited. Performance is important but second to usability and elegance.

Philosophy

Elegance, simplicity, and readability matter more than squeezing every cycle.

Goals

  • One common, easy-to-read API that is self-explanatory.
  • Put usability first, then performance. Default functions take and return values by copy so call sites stay simple.
  • Keep the public API stable. Speedups live behind the same names.
  • Work well in graphics engines, simulations, and games, not only tiny demos.
  • Stay portable C11, easy to pull in with CMake (FetchContent or find_package).
  • Grow SIMD amd MMA without forcing apps to pass ISA flags.

Features

  • Default interfaces use value types and obvious names (vector3, matrix4, quaternion).
  • The real work lives in _ptr functions (pointers in, pointers out). Those are what SIMD/MMA backends implement.
  • You can access components as .x/.y/.z or as m11, m21, ... or as a flat .v[] array.
  • Performance is not ignored; it is layered under a stable, comfortable API.
  • BSD 3-Clause License — great for individuals, organizations, and companies.
  • Includes a unit testing and benchmarking framework unitest.h
  • Exceptions in tests are handled using a custom handler except.h and you can use it for whatever it's only 24 lines of code.

Precision chosen at build time

  • Default: float and int32_t.
  • Optional: double (VECMAT_USE_F64), and int width 8 / 16 / 32.

Math types

  • Float vectors: 2D, 3D, 4D (vector2 / vector3 / vector4).
  • Integer vectors: same sizes (vector2i / vector3i / vector4i).
  • Float and integer matrices: 2x2, 3x3, 4x4.
  • Quaternions for rotation.
  • Easing functions for animation-style interpolation.

Features to Avoid

  • No SSE and no NEON on purpose. The library jumps to AVX / AVX2 / AVX-512 and ARM SVE / SVE2.

Two ways to call everything

  • By-value helpers for everyday code.
  • _ptr kernels for hot paths and SIMD.

Precision chosen at build time

  • Default: float and int32_t.
  • Optional: double (VECMAT_USE_F64), and int width 8 / 16 / 32.

Documentation

Generate local docs using doxygen

cd doc && doxygen Doxyfile

History

This library started as a quick replacement to the mathc library by Felipe Ferreira da Silva but evolved into somewhat larger scope. While Felipe's library worked well for some simple stuff, the mathc API wasn't ideal for larger like graphics engines, simulations, and games. I found the math API was somewhat incomplete, and not straightforward as expected.

I decided to build my own library with the main goal of common API, easy to use, and self-explanatory interfaces. While mathc put performance first making the API uncomfortable, vecmat puts usage and API first; that means that all default interfaces are copy and type names are expected.

SIMD and MMA

Selection order: SVE2 -> SVE -> AVX-512F -> AVX2 -> AVX -> Scalar

CMake flag Default Effect
-DVECMAT_RUNTIME_DISPATCH=ON ON for x86-64 and AArch64 Build extra ISA TUs and bind public names at runtime
-DVECMAT_ENABLE_AVX=ON ON on x86-64 Compile AVX kernels (-mavx / /arch:AVX)
-DVECMAT_ENABLE_AVX2=ON ON on x86-64 Compile AVX2 kernels (-mavx2 / /arch:AVX2)
-DVECMAT_ENABLE_AVX512=ON ON on x86-64 Compile AVX-512F kernels (-mavx512f / /arch:AVX512)
-DVECMAT_ENABLE_SVE=ON ON on AArch64 Compile SVE kernels (-march=armv8-a+sve)
-DVECMAT_ENABLE_SVE2=ON ON on AArch64 Compile SVE kernels (-march=armv8-a+sve2)

How to check for features:

printf("compiled=%s runtime=%s selected=%s\n",
vm_cpu_features_t vm_cpu_compiled_features(void)
Definition cpu.c:130
const char * vm_cpu_name(vm_cpu_features_t features)
Definition cpu.c:193
void vm_cpu_init(void)
Definition dispatch.c:77
vm_cpu_features_t vm_cpu_runtime_features(void)
Definition cpu.c:151
vm_cpu_features_t vm_cpu_selected_features(void)
Definition cpu.c:175

CPU Feature Support

  • AVX supported
  • AVX2 (FMA3) supported
  • AVX-512F (AVX-512 FMA) supported
  • AVX10 (FMA3) work in progress
  • AVX10.1 (Xeon 6) coming in 2027
  • AVX10.2 (Xeon 7) tbd
  • SVE (ARMv8.2-A+) supported
  • SVE2 (ARMv9) supported

MMA Support

  • WMMA / MMA (NVIDIA/CUDA) work in progress
  • MFMA / WMMA (AMD/ROCm) work in progress
  • AMX (4th-7th generation Intel Xeon) coming in 2027
  • SME / SME2 (ARMv9.2-A+) tbd

At this moment we have no plans to support NEON.

Relevant Resources

CMake Integration

Source using FetchContent

if(NOT TARGET vecmat::vecmat)
include(FetchContent)
FetchContent_Declare(vecmat
GIT_REPOSITORY https://github.com/alkavan/vecmat.git
GIT_TAG v0.1.0
)
FetchContent_MakeAvailable(vecmat)
endif()
target_link_libraries(my_app PRIVATE vecmat::vecmat)

Installed Package

find_package(vecmat 0.1 CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE vecmat::vecmat)

System integration / Out-of-source build and installation

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug \
-DVECMAT_BUILD_TESTS=ON \
-DCMAKE_INSTALL_PREFIX="$HOME/.local"
cmake --build build -j
cmake --install build

Note: Use -DVECMAT_INSTALL=ON only when install rules were turned off or vecmat isn't top-level — and you still want cmake --install to install it.

Scalar precision flags

vm_float_t and vm_int_t are selected at compile time. Pass the matching CMake options when configuring Vecmat. The options become public compile definitions on vecmat::vecmat and vecmat::vecmat_static, so anything that links the library sees the same typedefs.

Defaults (no flags): vm_float_t is float, vm_int_t is int32_t.

CMake flag Header macro Effect
-DVECMAT_USE_F64=ON VECMAT_USE_F64 vm_float_t is double
-DVECMAT_USE_INT8=ON VECMAT_USE_INT8 vm_int_t is int8_t
-DVECMAT_USE_INT16=ON VECMAT_USE_INT16 vm_int_t is int16_t
-DVECMAT_USE_INT32=ON VECMAT_USE_INT32 vm_int_t is int32_t

The integer flags are mutually exclusive. CMake will error if more than one is ON. VECMAT_USE_F64 can be combined with any one integer flag.

Configure from the command line:

cmake -S . -B build \
-DVECMAT_USE_F64=ON \
-DVECMAT_USE_INT16=ON \
-DVECMAT_BUILD_TESTS=ON

With FetchContent, set the cache variables before FetchContent_MakeAvailable:

set(VECMAT_USE_F64 ON CACHE BOOL "" FORCE)
set(VECMAT_USE_INT16 ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(vecmat)

Without CMake, define the same macros yourself (compiler flag or before the library include):

cc -DVECMAT_USE_F64 -DVECMAT_USE_INT16 ...
#define VECMAT_USE_F64
#define VECMAT_USE_INT16
#include <vecmat.h>

The library and every translation unit that includes vecmat.h must use the same set of macros, or the types will not match at link time.

Contributing

We don't have any complicated rules for contributing (for now), we only expect people to comply with the project Philosophy and Goals.

Artificial Intelligence Guidelines and Transparency

  1. AI use: Use of AI is neither prohibited nor encouraged. You may use AI only if you follow all the guidelines in this section.
  2. Disclosure: If you add AI-generated material to a contribution or derivative work, say so clearly — for example in the pull request, commit message, or nearby comments. Note which parts were AI-generated or heavily AI-assisted. Everyday autocomplete or small wording help does not need a notice.
  3. Responsibility: When you contribute or share a derivative, you take responsibility that the work has enough original human authorship, and that any AI-generated parts don't violate someone else's terms or the project [LICENSE](LICENSE).
  4. AI training: If you train an AI system on this code, it is recommended to give it the whole project, including in-code comments and any generated documentation that exists.

Usage and Examples

Vectors and matrices are plain C structs. Components are available as named fields (.x / .y / .z / .w, or m11, m21, …) and as a flat .v[] array. Prefer the value constructors for everyday code.

Individual element access

p.x = 1.0f; // same as p.v[0]
p.v[1] = 2.0f; // same as p.y
printf("%f\n", p.z);
vm_float_t v[VECMAT_VEC3_SIZE]
Definition vecmat.h:141
vm_float_t z
Definition vecmat.h:139
vm_float_t x
Definition vecmat.h:137
matrix3 mat;
mat.v[0] = 1.0f; // same as mat.m11 (column-major)
printf("%f\n", mat.m21); // same as mat.v[1]
vm_float_t v[VECMAT_MAT3_SIZE]
Definition vecmat.h:238
vm_float_t m21
Definition vecmat.h:229

Initializing a vector

vector3 p = vec3(1.0f, 2.0f, 3.0f);
vector2 q = vec2(4.0f, 5.0f);
vector3i grid = vec3i(8, 16, 24);
vector3 origin = vec3_zero();
vector3 ones = vec3_one();
vector3 fill = vec3_splat(0.5f);
vector3 named = { .x = 1.0f, .y = 0.0f, .z = 0.0f };
vector4 homog = { .v = {1.0f, 2.0f, 3.0f, 1.0f} };
vec3_assign_xyz(&p, 0.0f, 1.0f, 0.0f);
vector3 lifted = vec3_from_vec2(q, 0.0f);
vector3 vec3_one(void)
Returns a vector3 with all components set to 1.0f.
Definition vector3.c:22
vector3 vec3_from_vec2(vector2 v, vm_float_t z)
Builds a vector3 from a vector2 and z.
Definition vector3.c:642
vector3 vec3_zero(void)
Returns a zero-initialized vector3.
Definition vector3.c:12
vector2 vec2(vm_float_t x, vm_float_t y)
Constructs a vector2 from x and y.
Definition vecmat.c:220
vector3 vec3_splat(vm_float_t s)
Returns a vector with every component set to s.
Definition vector3.c:628
vector3 vec3(vm_float_t x, vm_float_t y, vm_float_t z)
Constructs a vector3 from x, y, and z.
Definition vecmat.c:363
void vec3_assign_xyz(vector3 *dest, vm_float_t x, vm_float_t y, vm_float_t z)
Assigns x, y, and z to dest.
Definition vecmat.c:389
vector3i vec3i(vm_int_t x, vm_int_t y, vm_int_t z)
Constructs a vector3i from x, y, and z.
Definition vecmat.c:432

The same pattern exists for vector2 / vector4 and the integer types (vecN_zero, vecN_one, vecN_splat, plus vec2i / vec3i).

Initializing a matrix

matrix3 ident = {
.m11 = 1.0f, .m21 = 0.0f, .m31 = 0.0f,
.m12 = 0.0f, .m22 = 1.0f, .m32 = 0.0f,
.m13 = 0.0f, .m23 = 0.0f, .m33 = 1.0f
};
matrix3 also = { .v = {1,0,0, 0,1,0, 0,0,1} };

Accessing matrix elements

Accessing elements by name

float determinant(const matrix3 *mat) {
float det =
mat->m11 * (mat->m22 * mat->m33 - mat->m23 * mat->m32) // First term
- mat->m12 * (mat->m21 * mat->m33 - mat->m23 * mat->m31) // Second term (negative)
+ mat->m13 * (mat->m21 * mat->m32 - mat->m22 * mat->m31); // Third term
return det;
}
vm_float_t m13
Definition vecmat.h:234
vm_float_t m32
Definition vecmat.h:233
vm_float_t m23
Definition vecmat.h:235
vm_float_t m12
Definition vecmat.h:231
vm_float_t m33
Definition vecmat.h:236
vm_float_t m11
Definition vecmat.h:228
vm_float_t m31
Definition vecmat.h:230
vm_float_t m22
Definition vecmat.h:232

Accessing elements by index

matrix3 mat;
for (int i = 0; i < 9; i++) {
mat.v[i] *= 2.0f; // Scale all elements by 2
}

Implementing Common Vector And Matrix Operations

Vector Operations Examples

A function for general linear transformation to the vector:

void transform(vector3 *out, const matrix3 *mat, const vector3 *vec) {
out->x = mat->m11 * vec->x + mat->m12 * vec->y + mat->m13 * vec->z;
out->y = mat->m21 * vec->x + mat->m22 * vec->y + mat->m23 * vec->z;
out->z = mat->m31 * vec->x + mat->m32 * vec->y + mat->m33 * vec->z;
}
vm_float_t y
Definition vecmat.h:138

A function to translate a vector by adding a translation offset:

void translate(vector3 *out, const vector3 *vec, const vector3 *translation) {
out->x = vec->x + translation->x;
out->y = vec->y + translation->y;
out->z = vec->z + translation->z;
}

Matrix Operations Examples

You can write a function to multiply two matrix3 instances.
Using the array access makes it easier to implement with nested loops:

void multiply(matrix3 *result, const matrix3 *a, const matrix3 *b) {
for (int c = 0; c < 3; c++) { /* columns of result / of B */
for (int r = 0; r < 3; r++) { /* rows of result / of A */
float sum = 0.0f;
for (int k = 0; k < 3; k++) {
sum += a->v[k * 3 + r] * b->v[c * 3 + k]; /* column-major */
}
result->v[c * 3 + r] = sum;
}
}
}

This creates a matrix4 that can apply rotation/scaling (from matrix3) followed by translation:

void affine_matrix(matrix4 *out, const matrix3 *linear, const vector3 *translation) {
// Copy the 3x3 linear part (columns 1-3)
out->m11 = linear->m11; out->m21 = linear->m21; out->m31 = linear->m31; out->m41 = 0.0f;
out->m12 = linear->m12; out->m22 = linear->m22; out->m32 = linear->m32; out->m42 = 0.0f;
out->m13 = linear->m13; out->m23 = linear->m23; out->m33 = linear->m33; out->m43 = 0.0f;
// Set translation in the fourth column
out->m14 = translation->x;
out->m24 = translation->y;
out->m34 = translation->z;
out->m44 = 1.0f;
}
vm_float_t m44
Definition vecmat.h:269
vm_float_t m12
Definition vecmat.h:258
vm_float_t m41
Definition vecmat.h:257
vm_float_t m13
Definition vecmat.h:262
vm_float_t m33
Definition vecmat.h:264
vm_float_t m42
Definition vecmat.h:261
vm_float_t m23
Definition vecmat.h:263
vm_float_t m21
Definition vecmat.h:255
vm_float_t m43
Definition vecmat.h:265
vm_float_t m22
Definition vecmat.h:259
vm_float_t m32
Definition vecmat.h:260
vm_float_t m24
Definition vecmat.h:267
vm_float_t m11
Definition vecmat.h:254
vm_float_t m31
Definition vecmat.h:256
vm_float_t m14
Definition vecmat.h:266
vm_float_t m34
Definition vecmat.h:268