Programmatic 3D modeling kernel — from CSG to BRep to mesh, with Python bindings

JefSCAD

Rust, Python bindings, CSG, BRep, NURBS, numerical meshing


The Problem

I like OpenSCAD. Defining solids programmatically is the right way to do parametric 3D modeling. But OpenSCAD has two fundamental problems:

  1. Floating-point errors create thin sections. When combining or subtracting solids, floating point inaccuracies produce tiny slivers along boolean operation boundaries. They're invisible on screen but ruin 3D prints.

  2. Immediate meshing loses fidelity. Objects are meshed at creation time. A cylinder becomes a faceted polygon immediately — stretch it into an ellipse and the facets stretch unevenly, revealing the fixed resolution.

There's a third desire: OpenSCAD's scripting language is limited. Real parametric modeling should live inside a real programming language.

JefSCAD is my answer: a geometry kernel that keeps solids as mathematical objects until the final export step, embedded in Python with a Rust backend.


The Architecture: Three Levels

The system operates through three successive representations:

CSG (tree) ──→ BRep (topology + geometry) ──→ Mesh (triangles)

1. Constructive Solid Geometry (CSG)

A tree structure where:

The tree is pure mathematical description — no meshing happens here. This means you can combine solids and the result stays exact until you explicitly compile it.

Each node has a geometry hash for caching and a provenance ID for tracing which part of the tree produced which piece of the final solid.

2. Boundary Representation (BRep)

The BRep separates topology from geometry:

TopologyGeometry
Solid → ShellsSurface (parametric: u,v → R³)
Shell → FacesCurve (parametric: t → R³)
Face → Loops → CoedgesP-Curve (t → u,v for trimming)
Coedge → Edge → VertexPoint (x,y,z)

A shell is a set of faces forming a closed manifold. A solid is an outer shell plus optional inner shells defining voids.

Faces reference parametric surfaces (plane, cylinder, sphere, NURBS). Edges reference parametric curves (line, arc, NURBS). Coedges carry p-curves that trim the face boundary in the surface's (u,v) parameter space.

This separation means: high-precision STEP export (using the exact surface/curve definitions), separate control over mesh resolution per face, and the ability to refine meshes locally without re-evaluating the entire boolean tree.

3. Mesh

An unstructured triangle mesh generated from the BRep:


Numerical Meshing Module

This is what connects JefSCAD to the GPU Fluid Dynamics project. The meshing module exports solver-ready spatial data:


Ideal Usage

import jefscad

# Start with primitives
cyl = jefscad.cylinder(r=2, h=3)
ball = jefscad.sphere(r=2.5)
capped_sphere = jefscad.union(cyl, ball.translate(0, 0, 3))

# Compile to BRep for precision / STEP export
ctx = jefscad.new_context()
brep = capped_sphere.compile_to_brep(ctx)
brep.save_step("my_thing.step")

# Generate a mesh for STL / OBJ / rendering
mesh = capped_sphere.create_mesh(ctx)
mesh.save_stl("my_thing.stl")
mesh.save_obj("my_thing.obj")

Current Status

JefSCAD is an active project. The CSG tree structure, BRep data model, and mesh generation strategy are fully specified. Current work focuses on:

The geometry kernel design is informed by my background in computational physics and finite element methods — the BRep data model draws directly from techniques used in FEM mesh generation.