README
¶
sdf (originally sdfx)
A rewrite of the original CAD package sdfx
for generating 2D and 3D geometry using Go.
- Objects are modelled with 2d and 3d signed distance functions (SDFs).
- Objects are defined with Go code.
- Objects are rendered to an STL file to be viewed and/or 3d printed.
Highlights
- 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.
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
- Add a 2D renderer and it's respective
Renderer2
interface. - Make 3D renderer multicore
Comparison
deadsy/sdfx
Advantages of deadsy/sdfx:
- Widely used
- More helper functions
- Working 2D renderer
Advantages of drummonds/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. See Questionable API design.
- deadsy/sdfx
- Has
SDFUnion
andSDFDiff
interfaces for blending shapes easily - 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 legibility
Contributing
See CONTRIBUTING.
Why was sdfx rewritten?
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:
Performance
sdfx is needlessly slow. Here is a benchmark rendering a threaded bolt:
$ go test -benchmem -run=^$ -bench ^(BenchmarkSDFXBolt|BenchmarkBolt)$ ./render
goos: linux
goarch: amd64
pkg: github.com/drummonds/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/drummonds/sdf/render 4.390s
BenchmarkBolt-8
is this implementation of Octree. BenchmarkSDFXBolt-8
is the sdfx
implementation of said algorithm.
Questionable 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
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.
sdf
and sdfx
consolidation
None planned.
My understanding is the sdfx
author has a very different design goal to what I envision. See the bullet-list of issues at the start of Questionable API design.
Documentation
¶
Index ¶
- func GenerateMesh2D(s SDF2, grid V2i) (d2.Set, error)
- func Identity() m22
- func Identity2d() m33
- func Identity3d() m44
- func MirrorX() m33
- func MirrorXY() m44
- func MirrorXZ() m44
- func MirrorXeqY() m44
- func MirrorY() m33
- func MirrorYZ() m44
- func MulVertices2(v d2.Set, a m33)
- func MulVertices3(v d3.Set, a m44)
- func NormalExtrude(p r3.Vec) r2.Vec
- func R2FromI(a V2i) r2.Vecdeprecated
- func R3FromI(a V3i) r3.Vecdeprecated
- func Rotate(a float64) m22
- func Rotate2d(a float64) m33
- func Rotate3d(v r3.Vec, a float64) m44
- func RotateX(a float64) m44
- func RotateY(a float64) m44
- func RotateZ(a float64) m44
- func Scale2d(v r2.Vec) m33
- func Scale3d(v r3.Vec) m44
- func Translate2d(v r2.Vec) m33
- func Translate3D(v r3.Vec) m44
- type CutSDF2
- type ExtrudeFunc
- type MaxFunc
- type MinFunc
- type SDF2
- func Center2D(s SDF2) SDF2
- func CenterAndScale2D(s SDF2, k float64) SDF2
- func Cut2D(sdf SDF2, a, v r2.Vec) SDF2
- func Elongate2D(sdf SDF2, h r2.Vec) SDF2
- func LineOf2D(s SDF2, p0, p1 r2.Vec, pattern string) SDF2
- func Multi2D(s SDF2, positions d2.Set) SDF2
- func Offset2D(sdf SDF2, offset float64) SDF2
- func RotateCopy2D(sdf SDF2, n int) SDF2
- func RotateUnion2D(sdf SDF2, num int, step m33) SDF2
- func ScaleUniform2D(sdf SDF2, k float64) SDF2
- func Slice2D(sdf SDF3, a, n r3.Vec) SDF2
- func Transform2D(sdf SDF2, m m33) SDF2
- type SDF2Diff
- type SDF2Union
- type SDF3
- func Cut3D(sdf SDF3, a, n r3.Vec) SDF3
- func Elongate3D(sdf SDF3, h r3.Vec) SDF3
- func Extrude3D(sdf SDF2, height float64) SDF3
- func ExtrudeRounded3D(sdf SDF2, height, round float64) SDF3
- func LineOf3D(s SDF3, p0, p1 r3.Vec, pattern string) SDF3
- func Loft3D(sdf0, sdf1 SDF2, height, round float64) SDF3
- func Multi3D(s SDF3, positions d3.Set) SDF3
- func Offset3D(sdf SDF3, offset float64) SDF3
- func Orient3D(s SDF3, base r3.Vec, directions d3.Set) SDF3
- func Revolve3D(sdf SDF2, theta float64) SDF3
- func RotateCopy3D(sdf SDF3, num int) SDF3
- func ScaleExtrude3D(sdf SDF2, height float64, scale r2.Vec) SDF3
- func ScaleTwistExtrude3D(sdf SDF2, height, twist float64, scale r2.Vec) SDF3
- func ScaleUniform3D(sdf SDF3, k float64) SDF3
- func Shell3D(sdf SDF3, thickness float64) SDF3
- func Transform3D(sdf SDF3, matrix m44) SDF3
- func TwistExtrude3D(sdf SDF2, height, twist float64) SDF3
- type SDF3Diff
- type SDF3Union
- type ScaleUniformSDF2
- type TransformSDF2
- type V2i
- func R2ToI(a r2.Vec) V2ideprecated
- type V3i
- func R3ToI(a r3.Vec) V3ideprecated
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GenerateMesh2D ¶
GenerateMesh2D generates a set of internal mesh points for an SDF2.
func MirrorXY ¶
func MirrorXY() m44
MirrorXY returns a 4x4 matrix with mirroring across the XY plane.
func MirrorXZ ¶
func MirrorXZ() m44
MirrorXZ returns a 4x4 matrix with mirroring across the XZ plane.
func MirrorXeqY ¶
func MirrorXeqY() m44
MirrorXeqY returns a 4x4 matrix with mirroring across the X == Y plane.
func MirrorYZ ¶
func MirrorYZ() m44
MirrorYZ returns a 4x4 matrix with mirroring across the YZ plane.
func MulVertices2 ¶
MulVertices multiples a set of V2 vertices by a rotate/translate matrix.
func MulVertices3 ¶
MulVertices multiples a set of r3.Vec vertices by a rotate/translate matrix.
func NormalExtrude ¶
NormalExtrude returns an extrusion function.
func Rotate ¶
func Rotate(a float64) m22
Rotate returns an orthographic 2x2 rotation matrix (right hand rule).
func Rotate2d ¶
func Rotate2d(a float64) m33
Rotate2d returns an orthographic 3x3 rotation matrix (right hand rule).
func RotateX ¶
func RotateX(a float64) m44
RotateX returns a 4x4 matrix with rotation about the X axis.
func RotateY ¶
func RotateY(a float64) m44
RotateY returns a 4x4 matrix with rotation about the Y axis.
func RotateZ ¶
func RotateZ(a float64) m44
RotateZ returns a 4x4 matrix with rotation about the Z axis.
func Scale2d ¶
Scale2d returns a 3x3 scaling matrix. Scaling does not preserve distance. See: ScaleUniform2D().
Types ¶
type CutSDF2 ¶
type CutSDF2 struct {
// contains filtered or unexported fields
}
CutSDF2 is an SDF2 made by cutting across an existing SDF2.
func (*CutSDF2) BoundingBox ¶
BoundingBox returns the bounding box for the cut SDF2.
type ExtrudeFunc ¶
ExtrudeFunc maps r3.Vec to V2 - the point used to evaluate the SDF2.
func ScaleExtrude ¶
func ScaleExtrude(height float64, scale r2.Vec) ExtrudeFunc
ScaleExtrude returns an extrusion functions that scales with z.
func ScaleTwistExtrude ¶
func ScaleTwistExtrude(height, twist float64, scale r2.Vec) ExtrudeFunc
ScaleTwistExtrude returns an extrusion function that scales and twists with z.
func TwistExtrude ¶
func TwistExtrude(height, twist float64) ExtrudeFunc
TwistExtrude returns an extrusion function that twists with z.
type MinFunc ¶
MinFunc is a minimum functions for SDF blending.
func MinChamfer ¶
MinChamfer returns a minimum function that makes a 45-degree chamfered edge (the diagonal of a square of size <r>). TODO: why the holes in the rendering?
type SDF2 ¶
SDF2 is the interface to a 2d signed distance function object.
func CenterAndScale2D ¶
CenterAndScale2D centers the origin of an SDF2 on it's bounding box, and then scales it. Distance is correct with scaling.
func Cut2D ¶
Cut2D cuts the SDF2 along a line from a in direction v. The SDF2 to the right of the line remains.
func Elongate2D ¶
Elongate2D returns the elongation of an SDF2.
func RotateCopy2D ¶
RotateCopy2D rotates and copies an SDF2 n times in a full circle.
func RotateUnion2D ¶
RotateUnion2D returns a union of rotated SDF2s.
func ScaleUniform2D ¶
ScaleUniform2D scales an SDF2 by k on each axis. Distance is correct with scaling.
func Slice2D ¶
Slice2D returns an SDF2 created from a planar slice through an SDF3. a is point on slicing plane, n is normal to slicing plane
func Transform2D ¶
Transform2D applies a transformation matrix to an SDF2. Distance is *not* preserved with scaling.
type SDF2Diff ¶
func Difference2D ¶
Difference2D returns the difference of two SDF2 objects, s0 - s1.
func Intersect2D ¶
Intersect2D returns the intersection of two SDF2s.
type SDF3 ¶
SDF3 is the interface to a 3d signed distance function object.
func Cut3D ¶
Cut3D cuts an SDF3 along a plane passing through a with normal n. The SDF3 on the same side as the normal remains.
func Elongate3D ¶
Elongate3D returns the elongation of an SDF3.
func ExtrudeRounded3D ¶
ExtrudeRounded3D extrudes an SDF2 to an SDF3 with rounded edges.
func Revolve3D ¶
Revolve3D returns an SDF3 for a solid of revolution. theta is in radians. For a full revolution call
Revolve3D(s0, 2*math.Pi)
func RotateCopy3D ¶
RotateCopy3D rotates and creates N copies of an SDF3 about the z-axis. num is the number of copies.
func ScaleExtrude3D ¶
ScaleExtrude3D extrudes an SDF2 and scales it over the height of the extrusion.
func ScaleTwistExtrude3D ¶
ScaleTwistExtrude3D extrudes an SDF2 and scales and twists it over the height of the extrusion.
func ScaleUniform3D ¶
ScaleUniform3D uniformly scales an SDF3 on all axes.
func Transform3D ¶
Transform3D applies a transformation matrix to an SDF3.
func TwistExtrude3D ¶
TwistExtrude3D extrudes an SDF2 while rotating by twist radians over the height of the extrusion.
type SDF3Diff ¶
func Difference3D ¶
Difference3D returns the difference of two SDF3s, s0 - s1. Difference3D will panic if one any of the arguments is nil.
func Intersect3D ¶
Intersect3D returns the intersection of two SDF3s. Intersect3D will panic if any of the arguments are nil.
type SDF3Union ¶
func RotateUnion3D ¶
RotateUnion3D creates a union of SDF3s rotated about the z-axis. num is the number of copies.
type ScaleUniformSDF2 ¶
type ScaleUniformSDF2 struct {
// contains filtered or unexported fields
}
ScaleUniformSDF2 scales another SDF2 on each axis.
func (*ScaleUniformSDF2) BoundingBox ¶
func (s *ScaleUniformSDF2) BoundingBox() d2.Box
BoundingBox returns the bounding box of an SDF2 with uniform scaling.
type TransformSDF2 ¶
type TransformSDF2 struct {
// contains filtered or unexported fields
}
TransformSDF2 transorms an SDF2 with rotation, translation and scaling.
func (*TransformSDF2) BoundingBox ¶
func (s *TransformSDF2) BoundingBox() d2.Box
BoundingBox returns the bounding box of a transformed SDF2.
type V2i ¶
type V2i [2]int
V2i is a 2D integer vector.