The Problem
Generating 3D printable meshes from signed distance functions (SDFs) is mathematically well-understood but practically tedious: you need to define shapes, combine them with boolean operations, and run a meshing algorithm. This repo packages that into a compact Python API so a user can define a shape in a few lines and get an STL file out.
What This Does
sdf is a Python library for constructive solid geometry (CSG). The core API lives in sdf/d2.py and sdf/d3.py — 2D and 3D primitives (sphere, box, cylinder, torus) that support & (intersection), | (union), and - (difference) operators. sdf/dn.py implements the boolean operations, sdf/ease.py provides 35 easing functions for transitions, and sdf/mesh.py handles mesh generation and bounding-box estimation.
The repo includes 11 runnable examples in examples/, each producing a mesh file. The canonical example in the README is 5 lines of code: define a sphere, subtract three oriented cylinders, save to out.stl.
How It Is Wired
Execution starts at main in examples/mesh.py:24, which reaches 16 functions. That script builds a hollowed box with cross-hatch ribs and saves it. The primary user path is simpler: call f.save('out.stl') on any SDF object. save lives in sdf/d3.py, which is the hub — 87 functions defined there, called from 3 files, and sdf/__init__.py re-exports it to 15 importing modules.
The call graph shows the real hot spots. slab is called from 7 places, _normalize from 6, shell and sdf from 5 each. The f function (a closure wrapper) calls _length 26 times and _vec 20 times — these are the vector math primitives everything builds on. sdf/__init__.py is a hub with 15 dependents; changing it has high blast radius.
File-by-file map: sdf/d3.py owns the 3D primitives and the SDF3 class, sdf/d2.py the 2D equivalents, sdf/dn.py the boolean operations, sdf/core.py the marching-cubes mesher and bounds estimation, sdf/mesh.py mesh I/O and transformations, sdf/text.py text and image-to-SDF conversion (reads image files), sdf/stl.py binary STL writing, sdf/progress.py progress reporting. docs/render.py is the only file that runs an external command (a Go program docs/render.go).
One structural concern: sdf/__init__.py, sdf/d3.py, and sdf/core.py form a circular import cycle. It works today, but it makes refactoring harder — changing any of those files requires understanding the mutual dependencies.
How To Use It
The README documents the install path:
git clone https://github.com/moses-y/sdf.git
cd sdf
virtualenv env
. env/bin/activate
pip install -e .
Dependencies (numpy, scipy, matplotlib, meshio, Pillow, scikit-image) install automatically via setup.py. Verify with:
python examples/example.py
That generates out.stl in the repo root. The library writes binary STL natively; other formats (OBJ, PLY, VTK) work via meshio based on the output file extension.
Real-World Use
A practical scenario: generating a custom enclosure for a PCB. Define the outer box, subtract cylinder cutouts for standoffs and a USB port, add a lid with a lip:
from sdf import *
box_body = rounded_box((60, 40, 20), 2)
standoffs = cylinder(0.15, 1).repeat((50, 30, 0))
usb_cutout = box((12, 6, 3)).translate((30, 0, 0))
f = box_body - standoffs - usb_cutout
f.save('enclosure.stl')
The result is a printable STL, ready for slicing.
Code Health & Issues
Static analysis (measured, not opinion) found 11 findings: 7 high, 4 medium. The high-severity items:
- Import cycle —
sdf/__init__.py,sdf/d3.py,sdf/core.pyparticipate in a circular dependency. Fix: extract shared types or defer imports. - Duplicated code — 14 repeated 6-line blocks across
sdf/d2.pyandsdf/d3.py. Fix: extract shared helpers.
Medium-severity: sdf/__init__.py is a hub module (15 dependents, high churn risk); sdf/text.py opens files without a context manager; sdf/core.py and sdf/dn.py use broad exception handling.
SDLC gaps: no test suite, no CI configuration, no lockfile. The repo has a license (MIT) and no committed secrets. For a library with this much geometry math, the missing tests are the most serious gap — a regression in vector math would produce silently broken meshes.
The Bottom Line
This is a clean, well-documented implementation of SDF-based CSG with a genuinely simple API. The core design is sound, but the circular imports and complete absence of tests make it risky to modify without careful manual verification. Use it for prototyping and small production jobs; add tests before building anything critical on top of it.