A rewrite of the original CAD package sdfx
for generating 2D and 3D geometry using Go.
- 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.
must
andform
packages provide panicking and normal error handling basic shape generation APIs for different scenarios.- Dead-simple, single method
Renderer
interface. - 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.
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
- Clean up thread API mess
- Add a 2D renderer and it's respective
Renderer2
interface. - Make 3D renderer multicore
Advantages of deadsy/sdfx:
- Widely used
- More helper functions
- Working 2D renderer
Advantages of soypat/sdf:
- Very fast rendering
deadsy/sdfx
is over 2 times slower and has ~5 times more allocations.
- Minimal and idiomatic API
Renderer
interface is dead-simple, idiomatic Go and not limited to SDFs- deadsy/sdfx
Renderer3
interface has filledrender
package with technical debt.
- deadsy/sdfx
- Has
SDFUnion
andSDFDiff
interfaces for blending shapes easilyMinPoly
redesign to allow for n-degree polynomials. Also returns a sensible "undefined output"MinFunc
before dividing by zero.
- No
nil
valued SDFs- deadsy/sdfx internally makes use of
nil
SDFs as "empty" objects. This can later cause panics during rendering well after the point of failure causing hard to debug issues.
- deadsy/sdfx internally makes use of
- Well defined package organization.
- deadsy/sdfx dumps helper and utility functions in
sdf
- deadsy/sdfx dumps helper and utility functions in
- End-to-end tested.
- Ensures functioning renderer and SDF functions using image comparison preventing accidental changes.
- Error-free API under
must3
andmust2
packages for makers.- For simple projects these packages allow for streamlined error handling process using
panic
instead of returned errors. - deadsy/sdfx only allows for Go-style error handling like the
form3
andform2
packages.
- For simple projects these packages allow for streamlined error handling process using
- Sound use of
math
package for best precision and overflow prevention.math.Hypot
used for all length calculations.deadsy/sdfx
does not usemath.Hypot
.
- Uses gonum's
spatial
packagesdfx
has own vector types with methods which hurt code legibilityspatial
types from gonum library with correct Triangle degeneracy calculation.deadsy/sdfx
's Degenerate calculation is incorrect.
- Idiomatic
thread
package. Define arbitrary threads with ease usingThreader
interface.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.
See CONTRIBUTING.
The original sdfx
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:
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.
- deadsy/sdfx#48 Vector API redesign
- deadsy/sdfx#35 Better STL save functions.
- deadsy/sdfx#50 Removing returned errors from shape generation functions
The vector math functions are methods which yield hard to follow operations. i.e:
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
meshCells
to 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
Renderer
interface this is no longer the case.
None planned.
Gopher rendition by Juliette Whittingslow.
Gopher design authored by Renée French
is licensed by the Creative Commons Attribution 3.0 licensed.