A high-performance C++20 differentiable geometry kernel for constraint-based CAD applications.
GeomCore is a production-grade signed distance field (SDF) engine with automatic differentiation support. It provides a clean operator-based API for building complex geometric models with parametric constraints.
- 17 Built-in SDF Operators: Primitives, transforms, booleans, and advanced chart operators
- Automatic Differentiation: Templated operators with Ceres Jet support for exact gradients
- IR-Based Architecture: Declarative JSON graph representation compiled to efficient executables
- Constraint Solving: Integration with Ceres optimizer for distance, tangent, and angle constraints
- High-Performance Meshing: Dual contouring with QEF solver (<5s for 100³ grids)
- Python Bindings: Full API exposure via pybind11
- Language: C++20
- Build System: CMake 3.22+
- Dependencies: Eigen3, Ceres Solver, pybind11, nlohmann/json
- Testing: GoogleTest
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -jcd build
ctest --output-on-failure# Create a simple box
./build/modelspace_cli mesh examples/simple_box.json output.obj
# Check documentation
./build/modelspace_cli --helpimport modelspace
# Build IR programmatically
ir = {
"nodes": {
"box1": {
"op": "sdf.box",
"params": {"width": 100, "height": 50, "depth": 20}
}
},
"outputs": ["box1"]
}
# Evaluate SDF at a point
field = modelspace.compile_ir(ir)
distance = field.evaluate([10.0, 0.0, 0.0])
gradient = field.gradient([10.0, 0.0, 0.0])
print(f"Distance: {distance}, Gradient: {gradient}")| Operator | Parameters | Description |
|---|---|---|
sdf.sphere |
radius |
Sphere centered at origin |
sdf.box |
width, height, depth |
Axis-aligned box |
sdf.cylinder |
radius, height |
Z-aligned cylinder |
sdf.cone |
bottom_radius, top_radius, height |
Tapered cylinder |
sdf.torus |
major_radius, minor_radius |
Donut shape |
sdf.capsule |
radius, height |
Pill shape |
sdf.halfspace |
normal, distance |
Infinite plane |
| Operator | Parameters | Description |
|---|---|---|
transform.translate |
x, y, z |
Translation |
transform.rotate |
axis, angle |
Axis-angle rotation (radians) |
transform.scale |
sx, sy, sz |
Non-uniform scaling |
| Operator | Parameters | Description |
|---|---|---|
boolean.union |
- | Sharp union (min) |
boolean.intersect |
- | Sharp intersection (max) |
boolean.subtract |
- | Sharp subtraction |
boolean.smooth_union |
k |
Differentiable blend |
| Operator | Parameters | Description |
|---|---|---|
chart.bspline |
control_points, knots, degree |
B-spline curve |
chart.extrude |
curve, direction, distance |
Linear extrusion |
chart.revolve |
curve, axis, angle |
Surface of revolution |
| Operator | Parameters | Description |
|---|---|---|
field.offset |
distance |
Shell/thicken operation |
{
"nodes": {
"cyl1": {
"op": "sdf.cylinder",
"params": {"radius": 10.0, "height": 30.0},
"inputs": []
},
"trans1": {
"op": "transform.translate",
"params": {"x": 5.0, "y": 0.0, "z": 0.0},
"inputs": ["cyl1"]
},
"sphere1": {
"op": "sdf.sphere",
"params": {"radius": 15.0},
"inputs": []
},
"union1": {
"op": "boolean.smooth_union",
"params": {"k": 2.0},
"inputs": ["trans1", "sphere1"]
}
},
"outputs": ["union1"],
"constraints": []
}Define geometric constraints and let the solver find optimal parameters:
{
"nodes": { ... },
"constraints": [
{
"id": "C1",
"type": "distance",
"targets": ["node1", "node2"],
"value": 10.0,
"weight": 1.0,
"samples": [
{"node": "node1", "point": [0, 0, 0]},
{"node": "node2", "point": [0, 0, 0]}
]
}
]
}Constraint types:
distance: Maintain distance between geometriestangent: Make surfaces tangentangle: Maintain angle between surfaces
| Operation | Metric |
|---|---|
| SDF Evaluation | ~20ns per point |
| Gradient Computation | ~50ns per point (AD) |
| Dual Contouring (100³) | <5 seconds |
| Constraint Solve (10 params) | <100ms |
#include <modelspace/core/compiler.hpp>
#include <modelspace/meshing/dual_contour.hpp>
// Load IR from JSON
auto ir = ms::load_ir_from_file("model.json");
// Compile to field evaluator
auto field = ms::compile_ir(ir);
// Evaluate at point
ms::Vec3 point{10.0, 0.0, 0.0};
double distance = field->value(point);
ms::Vec3 gradient = field->grad(point);
// Generate mesh
ms::DualContouring dc;
auto mesh = dc.contour(field.get(), /*grid_size=*/100, /*bounds=*/{-50, 50});import modelspace
# Load IR
ir = modelspace.load_ir("model.json")
# Compile
field = modelspace.compile_ir(ir)
# Evaluate
distance = field.evaluate([10.0, 0.0, 0.0])
gradient = field.gradient([10.0, 0.0, 0.0])
# Mesh generation
mesh = modelspace.mesh(field, grid_size=100)
modelspace.save_mesh(mesh, "output.obj")Ubuntu/Debian:
sudo apt-get install cmake libeigen3-dev libceres-dev \
libgtest-dev pybind11-dev nlohmann-json3-devmacOS:
brew install cmake eigen ceres-solver pybind11 nlohmann-jsonWindows (vcpkg):
vcpkg install eigen3 ceres pybind11 nlohmann-json# Configure
cmake -B build -DCMAKE_BUILD_TYPE=Release
# Build
cmake --build build -j$(nproc)
# Test
cd build && ctest
# Install
sudo cmake --install buildBUILD_PYTHON_BINDINGS: Enable Python module (default: ON)BUILD_CLI: Build command-line tool (default: ON)BUILD_TESTS: Build test suite (default: ON)USE_AUTOMATIC_DIFF: Enable AD (default: ON)
See examples/ directory for complete examples:
simple_box.json- Basic box primitivecsg_operations.json- Boolean operations democonstraint_solving.cpp- C++ constraint solver usagepython_api.py- Python API tour
- CAD Software: Parametric modeling with constraints
- Generative Design: Optimization-driven geometry
- Game Engines: Procedural level generation
- Robotics: Path planning with implicit surfaces
- Manufacturing: Design validation and analysis
GeomCore
├── Core
│ ├── IR (JSON graph representation)
│ ├── OpRegistry (operator plugin system)
│ └── Compiler (IR → executable field)
├── Operators
│ ├── SDFs (7 primitives)
│ ├── Transforms (3 types)
│ ├── Booleans (4 types)
│ └── Charts (3 types)
├── Constraints
│ ├── Cost functions (AD-enabled)
│ ├── Ceres integration
│ └── Sampling strategies
└── Meshing
├── Dual contouring
├── QEF solver
└── Provenance tracking
Create custom operators by implementing the OpFactory interface:
#include <modelspace/core/op.hpp>
struct MyOpEval : ms::FieldEval {
double value(const ms::Vec3& x) const override {
// Your SDF implementation
return /* distance */;
}
ms::Vec3 grad(const ms::Vec3& x) const override {
// Analytic gradient
return /* gradient vector */;
}
};
struct MyOpFactory : ms::OpFactory {
const char* name() const override { return "custom.myop"; }
std::unique_ptr<ms::FieldEval> instantiate(
const nlohmann::json& params,
const std::vector<std::shared_ptr<ms::FieldEval>>& inputs,
const ms::OpContext& ctx) const override {
return std::make_unique<MyOpEval>(/* ... */);
}
};
// Register at startup
ms::Registry::instance().register_factory(std::make_unique<MyOpFactory>());MIT License - see LICENSE file for details
Contributions welcome! See CONTRIBUTING.md for guidelines.
- Issues: https://github.com/YOUR_USERNAME/geomcore/issues
- Docs: https://geomcore.readthedocs.io
- Email: support@example.com
If you use GeomCore in academic work, please cite:
@software{modelspace_kernel,
title={GeomCore: A Differentiable Geometry Engine},
author={Your Name},
year={2025},
url={https://github.com/YOUR_USERNAME/geomcore}
}