A Go library for signed distance function shape generation. Read as 3D printing shape design.
⚠️ ARCHIVED ⚠️
Development has moved to https://github.com/soypat/gsdf.This project has been archived in favor of developing a more advanced solution. gsdf redesigns APIs to be vectorised speeding things up considerably and enabling usage of GPU which enables real-time visualization of shapes with infinite detail (no triangle rendering step required).
Most functionality in sdf has already been reimplemented at the time of archiving. Feel free to ask questions in the issue tracker!
sdf (originally sdfx)

A rewrite of the original Signed Distance Function CAD package sdfx for generating 2D and 3D geometry using Go.
Highlights
- GUI with real-time rendering using sdf3ui (or SDF Viewer).
- 3d and 2d objects modelled with signed distance functions (SDFs).
- Minimal and idiomatic API.
- Render objects as triangles or save to STL, 3MF(experimental) file format.
- End-to-end testing using image comparison.
mustandformpackages provide panicking and normal error handling basic shape generation APIs for different scenarios.- Dead-simple, single method
Rendererinterface. - Import mesh files: Edit STL and 3MF files as if they were native SDFs using
sdfexp.ImportModel - Tetrahedron mesher: Measure volume or create physics models of your shapes using
sdfexp.UniformTetrahedronMesh. Experimental feature. Example result image.
Examples
For real-world examples with images see examples directory README.See images of rendered shapes in render/testdata.
Here is a rendered bolt from one of the unit tests under form3_test.go 
Roadmap
- [x] Clean up thread API mess
- [ ] Add a 2D renderer and it's respective
Renderer2interface. - [ ] Make 3D renderer multicore
Comparison
deadsy/sdfx
Advantages of deadsy/sdfx:
- Widely used
- More helper functions
- Working 2D renderer
- Very fast rendering
deadsy/sdfx is over 2 times slower and has ~5 times more allocations.
- Minimal and idiomatic API
Rendererinterface is dead-simple, idiomatic Go and not limited to SDFs
Renderer3 interface has filled render package with technical debt.
- Has
SDFUnionandSDFDiffinterfaces for blending shapes easily
MinPoly redesign to allow for n-degree polynomials. Also returns a sensible "undefined output" MinFunc before dividing by zero.
- No
nilvalued SDFs
nil SDFs as "empty" objects. This can later cause panics during rendering well after the point of failure causing hard to debug issues.
- Well defined package organization.
sdf
- End-to-end tested.
- Error-free API under
must3andmust2packages for makers.
panic instead of returned errors.
- deadsy/sdfx only allows for Go-style error handling like the form3 and form2 packages.
- Sound use of
mathpackage for best precision and overflow prevention.
math.Hypot used for all length calculations. deadsy/sdfx does not use math.Hypot.
- Uses gonum's
spatialpackage
sdfx has own vector types with methods which hurt code legibility
- spatial types from gonum library with correct Triangle degeneracy calculation. deadsy/sdfx's Degenerate calculation is incorrect.
- Idiomatic
threadpackage. Define arbitrary threads with ease usingThreaderinterface.
deadsy/sdfx defines threads with strings i.e. "M16x2". sdf Defines threads with types corresponding to standards. i.e: thread.ISO{D:16, P:2}, which defines an M16x2 ISO thread.
Contributing
See CONTRIBUTING.Why was sdfx rewritten?
The originalsdfx package is amazing. I thank deadsy for putting all that great work into making an amazing tool I use daily. That said, there are some things that were not compatible with my needs:
Rendering speed
Here is a benchmark rendering a threaded bolt:$ go test -benchmem -run=^$ -bench ^(BenchmarkSDFXBolt|BenchmarkBolt)$ ./render
goos: linux
goarch: amd64
pkg: github.com/soypat/sdf/render
cpu: AMD Ryzen 5 3400G with Radeon Vega Graphics
BenchmarkSDFXBolt-8 6 196941244 ns/op 14700786 B/op 98261 allocs/op
BenchmarkBolt-8 13 87547265 ns/op 18136785 B/op 20754 allocs/op
PASS
ok github.com/soypat/sdf/render 4.390s
BenchmarkBolt-8 is this implementation of Octree. BenchmarkSDFXBolt-8 is the sdfx implementation of said algorithm.
Unwieldy API design
- https://github.com/deadsy/sdfx/issues/48 Vector API redesign
- https://github.com/deadsy/sdfx/issues/35 Better STL save functions.
- https://github.com/deadsy/sdfx/issues/50 Removing returned errors from shape generation functions
return bb.Min.Add(bb.Size().Mul(i.ToV3().DivScalar(float64(node.meshSize)).
Div(node.cellCounts.ToV3().DivScalar(float64(node.meshSize))))) // actual code from original sdfx.
A more pressing issue was the Renderer3 interface definition method, Render
type Renderer3 interface { // ... Render(s sdf.SDF3, meshCells int, output chan<- *Triangle3) }
This presented a few problems:
- Raises many questions about usage of the function Render- who closes the channel? Does this function block? Do I have to call it as a goroutine?
- To implement a renderer one needs to bake in concurrency which is a hard thing to get right from the start. This also means all rendering code besides having the responsibility of computing geometry, it also has to handle concurrency features of the language. This leads to rendering functions with dual responsibility- compute geometry and also handle the multi-core aspect of the computation making code harder to maintain in the long run
- Using a channel to send individual triangles is probably a bottleneck.
- I would liken
meshCellsto an implementation detail of the renderer used. This can be passed as an argument when instantiating the renderer used.
- Who's to say we have to limit ourselves to signed distance functions? With the new proposed
Rendererinterface this is no longer the case.