Categorygithub.com/soypat/sdf
modulepackage
0.0.0-20240812015519-6596a10e8743
Repository: https://github.com/soypat/sdf.git
Documentation: pkg.go.dev

# README

Go Report Card GoDoc

⚠️ 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.
  • must and form 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.

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 renderedBolt

Roadmap

  • Clean up thread API mess
  • 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 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 filled render package with technical debt.
  • Has SDFUnion and SDFDiff interfaces for blending shapes easily
    • MinPoly 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.
  • Well defined package organization.
    • deadsy/sdfx dumps helper and utility functions in sdf
  • End-to-end tested.
    • Ensures functioning renderer and SDF functions using image comparison preventing accidental changes.
  • Error-free API under must3 and must2 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 and form2 packages.
  • Sound use of math package for best precision and overflow prevention.
    • math.Hypot used for all length calculations. deadsy/sdfx does not use math.Hypot.
  • Uses gonum's spatial package
    • 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 thread package. Define arbitrary threads with ease using Threader 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.

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:

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

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:

  1. 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?

  2. 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

  3. Using a channel to send individual triangles is probably a bottleneck.

  4. I would liken meshCells to an implementation detail of the renderer used. This can be passed as an argument when instantiating the renderer used.

  5. 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.

Logo work

Gopher rendition by Juliette Whittingslow.
Gopher design authored by Renée French is licensed by the Creative Commons Attribution 3.0 licensed.

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Functions

Array2D returns an XY grid array of an existing SDF2.
Array3D returns an XYZ array of a given SDF3.
Center2D centers the origin of an SDF2 on it's bounding box.
CenterAndScale2D centers the origin of an SDF2 on it's bounding box, and then scales it.
Cut2D cuts the SDF2 along a line from a in direction v.
Cut3D cuts an SDF3 along a plane passing through a with normal n.
Difference2D returns the difference of two SDF2 objects, s0 - s1.
Difference3D returns the difference of two SDF3s, s0 - s1.
Elongate2D returns the elongation of an SDF2.
Elongate3D returns the elongation of an SDF3.
Extrude3D does a linear extrude on an SDF3.
ExtrudeRounded3D extrudes an SDF2 to an SDF3 with rounded edges.
Intersect2D returns the intersection of two SDF2s.
Intersect3D returns the intersection of two SDF3s.
LineOf2D returns a union of 2D objects positioned along a line from p0 to p1.
LineOf3D returns a union of 3D objects positioned along a line from p0 to p1.
Loft3D extrudes an SDF3 that transitions between two SDF2 shapes.
MaxPoly creates a n-degree polynomial MaxFunc with parameter k that controls radius of the smoothing function.
MinExp returns a minimum function with exponential smoothing (k = 32).
MinPoly creates a n-degree polynomial MinFunc with parameter k that controls radius of the smoothing function.
MinPow returns a minimum function (k = 8).
MinRound returns a minimum function that uses a quarter-circle to join the two objects smoothly.
MirrorX returns a 3x3 matrix with mirroring across the X axis.
MirrorXeqY returns a 4x4 matrix with mirroring across the X == Y plane.
MirrorXY returns a 4x4 matrix with mirroring across the XY plane.
MirrorXZ returns a 4x4 matrix with mirroring across the XZ plane.
MirrorY returns a 3x3 matrix with mirroring across the Y axis.
MirrorYZ returns a 4x4 matrix with mirroring across the YZ plane.
Multi2D creates a union of an SDF2 at a set of 2D positions.
Multi3D creates a union of an SDF3 at translated positions.
NormalExtrude returns an extrusion function.
Offset2D returns an SDF2 that offsets the distance function of another SDF2.
Offset3D returns an SDF3 that offsets the distance function of another SDF3.
Orient3D creates a union of an SDF3 at oriented directions.
R2FromI temporary home for this function.
R2ToI temporary home for this function.
R3FromI temporary home for this function.
R3ToI temporary home for this function.
Revolve3D returns an SDF3 for a solid of revolution.
Rotate returns an orthographic 2x2 rotation matrix (right hand rule).
Rotate2D returns an orthographic 3x3 rotation matrix (right hand rule).
Rotate3D returns an orthographic 4x4 rotation matrix (right hand rule).
RotateCopy2D rotates and copies an SDF2 n times in a full circle.
RotateCopy3D rotates and creates N copies of an SDF3 about the z-axis.
RotateUnion2D returns a union of rotated SDF2s.
RotateUnion3D creates a union of SDF3s rotated about the z-axis.
RotateX returns a 4x4 matrix with rotation about the X axis.
RotateY returns a 4x4 matrix with rotation about the Y axis.
RotateZ returns a 4x4 matrix with rotation about the Z axis.
Scale2D returns a 3x3 scaling matrix.
Scale3D returns a 4x4 scaling matrix.
ScaleExtrude returns an extrusion functions that scales with z.
ScaleExtrude3D extrudes an SDF2 and scales it over the height of the extrusion.
ScaleTwistExtrude returns an extrusion function that scales and twists with z.
ScaleTwistExtrude3D extrudes an SDF2 and scales and twists it over the height of the extrusion.
ScaleUniform2D scales an SDF2 by k on each axis.
ScaleUniform3D uniformly scales an SDF3 on all axes.
Shell3D returns an SDF3 that shells the surface of an existing SDF3.
Slice2D returns an SDF2 created from a planar slice through an SDF3.
Transform2D applies a transformation matrix to an SDF2.
Transform3D applies a transformation matrix to an SDF3.
Translate2D returns a 3x3 translation matrix.
Translate3D returns a 4x4 translation matrix.
TwistExtrude returns an extrusion function that twists with z.
TwistExtrude3D extrudes an SDF2 while rotating by twist radians over the height of the extrusion.
Union2D returns the union of multiple SDF2 objects.
Union3D returns the union of multiple SDF3 objects.

# Structs

CutSDF2 is an SDF2 made by cutting across an existing SDF2.
ScaleUniformSDF2 scales another SDF2 on each axis.
TransformSDF2 transorms an SDF2 with rotation, translation and scaling.

# Interfaces

SDF2 is the interface to a 2d signed distance function object.
No description provided by the author
No description provided by the author
SDF3 is the interface to a 3d signed distance function object.
No description provided by the author
No description provided by the author

# Type aliases

ExtrudeFunc maps r3.Vec to V2 - the point used to evaluate the SDF2.
MaxFunc is a maximum function for SDF blending.
MinFunc is a minimum functions for SDF blending.
V2i is a 2D integer vector.
V3i is a 3D integer vector.