Any mathematical space, one library.
Geometry libraries hardcode their space: CGAL's kernels, Eigen's linear algebra, GLM's vectors all assume flat Euclidean R^N wired into every type. Need geodesics on a sphere, distances in hyperbolic space, mesh operations on some other manifold? That's a different, specialized library each time — or a parallel hand-written stack duplicating the one you already have.
Spatium doesn't hardcode a space. It has a concept hierarchy — Set → TopologicalSpace → MetricSpace → NormedSpace → InnerProductSpace → Manifold → RiemannianManifold → Surface — and any type satisfying a concept's requirements gets the whole library for free:
struct FlatTorus { /* distance(), exp_map(), log_map(), project()... */ };
static_assert(spatium::RiemannianManifold<FlatTorus>);
// Mesh<FlatTorus>, subdivision, geodesics, morphisms — all work automatically.One exception to "all work automatically", stated here rather than left to
be discovered: spatial acceleration is still flat. spatial/'s BVH
bounds with axis-aligned boxes, so ray casting and nearest-neighbour
queries are accelerated in Euclidean space and unaccelerated off it. The
operations still give correct answers on any space; they just walk
everything. A ball tree over geodesic balls is the fix and is an open item
in the roadmap, not an oversight.
C++23, in large part header-only — three deliberate exceptions exist where real complexity made that the wrong tradeoff, not an oversight: the Vulkan viewer needs genuine C linkage, the periodic-table data backs a single compiled translation unit, and the physics/mechanics research track plus optional CUDA/ipc-toolkit integrations sit outside the header-only spine on purpose. See Architecture for the honest breakdown, not a marketing gloss.
- Kerr black hole — full 4-coordinate geodesic integration, GPU-rendered (CUDA) at 1920x1080 (video)
- A donut, declaratively — built entirely from torus()/offset()/scatter(), see the getting-started guide (build-up video)
The donut is also where the scene DSL's argument is easiest to check. A scene is described as spaces rather than as meshes, and the description stays a small inspectable graph: 34 nodes describing 2,021,984 objects. Geometry that would be 64,654,768 vertices if every object carried its own copy is stored as 39,272 — about 1646x — and the frame renders in under a gigabyte. That is not a trick in the renderer; it is what having described the scene as spaces buys.
More in gallery/.
- Space hierarchy as concepts — Set, TopologicalSpace, MetricSpace, NormedSpace, InnerProductSpace, Manifold, RiemannianManifold, Surface
- Concrete spaces — Euclidean<N>, Sphere<N>, Hyperbolic<N>, ParametricSurface, ImplicitSurface
- Geometric primitives & operations — Line/Ray/Segment/Hyperplane/Triangle/Polygon/Circle/Disk/Box/Simplex; intersection (Moller-Trumbore, slab method, analytical ray-quadric), distance, boolean ops, clipping
- Mesh & geodesics — Mesh<Surface>, subdivision with surface projection, LOD chains, geodesic distance (Dijkstra + heat method), geodesic Voronoi, discrete exterior calculus
- Morphisms — typed maps between spaces with pipe composition: point | scale | shift | project
- Declarative scene DSL (io::build) —torus()/offset()/scatter()/compose()build a flat, inspectableTrace, not a tree of opaque closures; analytic until the last mile (offset surfaces and area-weighted placement are real function composition, no mesh anywhere until something actually needs triangles). Getting-started tutorial:docs/getting-started-dsl.md, runnable inexamples/donut_demo.cpp
- Arbitrary precision — Boost.Multiprecision (Real50, Real100, any digit count), same generic algorithms; optional, -DSPATIUM_BOOST=ON
- Physics & relativity research track — geometric-mechanics integrators (symplectic, Lie-group, variational), metric-agnostic geodesic integration (Schwarzschild/Kerr), and RSC — a trained dispatcher that picks which method/precision to use per problem, not hand-tuned; see Roadmap
- N-dimensional, zero-cost — templated on dimension and scalar type, concepts checked at compile time, no virtual dispatch
#include <spatium/spatium.hpp>
#include <print>
using namespace spatium;
using namespace spatium::geometry;
int main() {
// Geometry — clean factory syntax
auto t = tri(Vec3{0, 0, 0}, Vec3{1, 0, 0}, Vec3{0, 1, 0});
std::println("area = {:.4f}, normal = {}", t.area(), t.normal());
// Intersection via pipe
auto r = *ray(Vec3{0.25, 0.25, 5}, Vec3{0, 0, -1});
if (auto hit = r | t)
std::println("hit at {}", *hit);
// Morphism pipeline
auto scale = morph<E3, E3>([](const Vec3& p) { return p * 2.0; });
auto proj = morph<E3, E2>([](const Vec3& p) -> Vec2 { return {p[0], p[1]}; });
auto result = pt<E3>(Vec3{1, 2, 3}) | scale | proj;
std::println("{}", result); // P(2, 4)
// Sphere geodesics
S2 sphere;
auto north = pt<S2>(Vec3{0, 0, 1});
auto east = pt<S2>(Vec3{1, 0, 0});
auto tangent = north.log(east, sphere);
auto midpoint = north.exp(tangent, 0.5, sphere);
std::println("geodesic midpoint: {}", midpoint);
// Mesh subdivision
auto mesh = mesh::icosahedron(sphere);
auto refined = mesh::subdivide(mesh, sphere, 3);
std::println("{}", refined); // Mesh{V=642 F=1280 E≈1920}
}Requires C++23 (GCC 15+ or Clang 19+), CMake 3.28+, Catch2 v3 for tests.
# With Nix (recommended)
nix develop
cmake --preset default
cmake --build --preset default
ctest --preset default
# Without Nix
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
ninja -C buildCMakePresets.json has presets beyond default for common configurations — release (Eigen, for RSC training-heavy work), modules (the C++23 modules build path), noeigen, vulkan-dev, cuda, and two benchmark-harness presets. cmake --list-presets shows all of them.
* Defaults to ON only when Spatium is the top-level CMake project (built
standalone, as above). Pulled in via add_subdirectory() or FetchContent
from another project, these four default to OFF instead, so a downstream
consumer gets just Spatium::sdk without forcing a Vulkan/Catch2/example
build it never asked for -- see "Using in Your Project" below.
include(FetchContent)
FetchContent_Declare(spatium
GIT_REPOSITORY https://github.com/Vaniell0/spatium.git
GIT_TAG v1.0.0
)
FetchContent_MakeAvailable(spatium)
target_link_libraries(your_target PRIVATE Spatium::sdk)This pulls in only the header-only Spatium::sdk interface target -- the
Vulkan viewer, examples, tests, and RSC tools all default OFF when Spatium
isn't the top-level CMake project, so nothing beyond Spatium::sdk and its
one required dependency (Boost headers, for Real50/Real100) gets built.
See examples/external-consumer/ for a
complete, independently-buildable project using exactly this snippet.
find_package(Spatium REQUIRED)
target_link_libraries(your_target PRIVATE Spatium::sdk)Any struct with the right methods satisfies the concepts automatically:
struct FlatTorus {
using ScalarType = double;
using PointType = Vec<double, 2>;
using TangentVector = Vec<double, 2>;
static constexpr std::size_t dimension = 2;
static constexpr bool is_complete = true;
bool contains(const PointType& p) const { /* ... */ }
ScalarType distance(const PointType& a, const PointType& b) const { /* ... */ }
PointType exp_map(const PointType& p, const TangentVector& v, ScalarType t) const { /* ... */ }
TangentVector log_map(const PointType& p, const PointType& q) const { /* ... */ }
ScalarType metric_at(const PointType& p, const TangentVector& u, const TangentVector& v) const { /* ... */ }
PointType project(const PointType& p) const { /* ... */ }
TangentVector normal(const PointType& p) const { /* ... */ }
};
static_assert(spatium::RiemannianManifold<FlatTorus>);
static_assert(spatium::Surface<FlatTorus>);
// Mesh<FlatTorus>, subdivision, morphisms — all work automatically.#include <spatium/core/precision.hpp>
using namespace spatium;
// 50-digit precision
Euclidean<3, Real50> space;
Vec<Real50, 3> a{Real50{0}, Real50{0}, Real50{0}};
Vec<Real50, 3> b{Real50{3}, Real50{4}, Real50{0}};
auto d = space.distance(a, b); // 5.000...000 (50 digits)- Architecture — concept hierarchy, design decisions, the real dependency graph
- Conventions — namespace/subdivision/error-handling rules, and the known violations being fixed
- API Reference — all types, methods, concepts
- Quick Start Guide — getting started
- Getting Started: The Declarative Scene DSL — zero-barrier-to-entry, build a donut in three declarative steps
- Extending Spatium — defining custom spaces and primitives
- Roadmap — what's done, what's planned, project history
- Concept-Driven Physics — how physics/mechanics/fits the concept hierarchy
include/spatium/
core/ concepts, error, verify, precision
algebra/ Vec, Matrix, Quaternion, Complex, Dual (autodiff), calculus,
ODE solvers, linear solve, polynomial solvers, Eigen interop
algebra/groups/ SO3, SE3
spaces/ Euclidean, Sphere, Hyperbolic, ParametricSurface, ImplicitSurface
geometry/ primitives, intersection, distance, boolean ops, ray_surface (Quadric)
mesh/ Mesh, subdivision, LOD, topology, geodesic, voronoi, DEC
spatial/ BVH (SAH build, ray_cast, nearest, query_box)
discrete/ FiniteSet, GeometricSet
render/ supersample_pixel(), camera, write_image, parallel_for_rows
io/ Table, SVG, OBJ, STL
physics/ periodic-table element data (the one compiled TU)
physics/atomic/ atom/orbital models, Bohr model, SVG rendering
physics/mechanics/ integrators, symplectic/Lie-group/variational structure, contact
physics/relativity/ Schwarzschild/Kerr geodesic integration, accretion disks
viewer/ Vulkan app (multi-mesh, point clouds, ImGui)
point.hpp, morphism.hpp, spatium.hpp
rsc/ RSC — trained dispatcher on top of Spatium (7 domains, see docs/ROADMAP.md)
tests/, examples/, benchmarks/
nix run .#primitives # unified primitives + BVH raycast, interactive Vulkan
nix run .#tumbling # Dzhanibekov-effect rigid-body tumble (LGVI), frame sequenceMore demos exist in examples/ — analytical ray tracing, a Schwarzschild/Kerr GR raytracer, an Ellis wormhole flythrough, and others; cmake --list-presets and nix flake show list every buildable target.
Apache License 2.0 — see LICENSE. Contributing guide: CONTRIBUTING.md.
Project history: docs/ROADMAP.md.