├── .gitignore ├── .wip ├── gears.go ├── kdtree.go ├── quadtree.go └── spline.go ├── LICENSE ├── Makefile ├── README.md ├── docs ├── ROADMAP.md └── gallery │ ├── axoloti.png │ ├── bezier_bowl.png │ ├── bezier_shape.png │ ├── camshaft.png │ ├── cc16a.png │ ├── cc16b_0.png │ ├── cc16b_1.png │ ├── cc18b.png │ ├── cc18c.png │ ├── core_box.png │ ├── extrude1.png │ ├── extrude2.png │ ├── gear.png │ ├── geneva1.png │ ├── geneva2.png │ ├── gyroid.png │ ├── head.png │ ├── icosahedron.png │ ├── msquare.png │ ├── nutsandbolts.png │ ├── text.png │ ├── voronoi.png │ └── wheel.png ├── examples ├── 3dp_nutbolt │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── angle │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── arrow │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── axochord │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── axoloti │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── beehive │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── benchmark │ ├── Makefile │ └── main.go ├── bezier │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── birdhouse │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── bjj │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── bolt_container │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── bucky │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── camshaft │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── cap │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── carburetor │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── challenge │ ├── Makefile │ ├── SHA1SUM │ ├── cc16.go │ ├── cc18.go │ └── main.go ├── cylinder_head │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── dc2test │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── delta │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── devo │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── draincover │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── drone │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── dust_collection │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── eurorack │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── extrusion │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── fidget │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── finial │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── flask │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── gas_cap │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── gears │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── geneva │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── gridfinity │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── gyroid │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── hole_patterns │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── holes │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── hollowing_stl │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── inlet_hood │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── joko │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── keycap │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── loadcell │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── maestro │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── maixgo │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── mcg │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── mesh_test │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── midget │ ├── Makefile │ ├── SHA1SUM │ ├── crankcase.go │ ├── cylinder.go │ └── main.go ├── monkey_hat │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── msquare │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── nordic │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── nutcover │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── nutsandbolts │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── offset_box │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── opengate │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── panel_box │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── phone │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── pico_cnc │ ├── Makefile │ ├── SHA1SUM │ ├── main.go │ └── penholder.go ├── pillar_holder │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── pipe_connectors │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── pool │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── pottery_wheel │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── ringnut_tool │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── rpi │ ├── Makefile │ ├── SHA1SUM │ ├── main.go │ └── stand │ │ └── stand.go ├── servo │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── simple_stl │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── spiral │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── sprue │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── square_flange │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── tabbox │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── tacho_bracket │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── tapers │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── test │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── text │ ├── Makefile │ ├── SHA1SUM │ └── main.go └── voronoi │ ├── Makefile │ ├── SHA1SUM │ └── main.go ├── files ├── bottle.stl ├── cmr10.ttf ├── monkey.stl └── teapot.stl ├── go.mod ├── go.sum ├── mk └── example.mk ├── obj ├── angle.go ├── arrow.go ├── bolt.go ├── chamfer.go ├── draincover.go ├── drone.go ├── finger.go ├── gear.go ├── geneva.go ├── gridfinity.go ├── hex.go ├── hole.go ├── keyway.go ├── knurl.go ├── nut.go ├── panel.go ├── panelbox.go ├── pipe.go ├── servo.go ├── spring.go ├── standoff.go ├── stl.go ├── tab.go ├── trp.go └── washer.go ├── render ├── 3mf.go ├── dc │ ├── dc3v1.go │ ├── dc3v2.go │ ├── leastsquares.go │ └── utils.go ├── dc2.go ├── delaunay.go ├── dxf.go ├── march2.go ├── march2x.go ├── march3.go ├── march3x.go ├── png.go ├── render.go ├── stl.go ├── stl_test.go ├── svg.go └── utils.go ├── sdf ├── benchmark.go ├── bezier.go ├── box2.go ├── box2_test.go ├── box3.go ├── cache2.go ├── cams.go ├── flange.go ├── gyroid.go ├── line.go ├── line_test.go ├── matrix.go ├── mesh2.go ├── mesh2_test.go ├── mesh3.go ├── mesh3_test.go ├── poly.go ├── quadratic.go ├── rack.go ├── screw.go ├── sdf2.go ├── sdf3.go ├── sdf_test.go ├── spiral.go ├── spline.go ├── text.go ├── triangle2.go ├── triangle3.go ├── triangle3_test.go ├── utils.go └── voxel.go ├── tools ├── ply │ ├── __init__.py │ ├── cpp.py │ ├── ctokens.py │ ├── lex.py │ ├── yacc.py │ └── ygen.py ├── sha1tool.py └── svg2bezier └── vec ├── conv └── conv.go ├── p2 └── p2.go ├── v2 ├── v2.go └── v2_test.go ├── v2i ├── v2i.go └── v2i_test.go ├── v3 ├── v3.go └── v3_test.go └── v3i ├── v3i.go └── v3i_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | tools/md5tool/md5tool 2 | tools/parser.out 3 | tools/parsetab.py 4 | *.pyc 5 | examples/*/*.stl 6 | !examples/monkey_hat/monkey.stl 7 | examples/*/*.dxf 8 | examples/*/*.png 9 | examples/*/*.svg 10 | -------------------------------------------------------------------------------- /.wip/kdtree.go: -------------------------------------------------------------------------------- 1 | /* 2 | K-Dimensional Trees 3 | */ 4 | 5 | package sdf 6 | 7 | import ( 8 | "sort" 9 | ) 10 | 11 | type ByX []V2 12 | 13 | func (a ByX) Len() int { return len(a) } 14 | func (a ByX) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 15 | func (a ByX) Less(i, j int) bool { return a[i].X < a[j].X } 16 | 17 | type ByY []V2 18 | 19 | func (a ByY) Len() int { return len(a) } 20 | func (a ByY) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 21 | func (a ByY) Less(i, j int) bool { return a[i].Y < a[j].Y } 22 | 23 | type KdTree2 struct { 24 | root *KdNode2 25 | } 26 | 27 | type KdNode2 struct { 28 | n V2 29 | left *KdNode2 30 | right *KdNode2 31 | } 32 | 33 | func NewKdTree2(points []V2) *KdTree2 { 34 | t := KdTree2{} 35 | 36 | sort.Sort(ByX(points)) 37 | return &t 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2019 Jason T. Harris 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | DIRS = $(wildcard ./examples/*/.) 3 | 4 | all clean hash: 5 | for dir in $(DIRS); do \ 6 | $(MAKE) -C $$dir $@ || exit 1; \ 7 | done 8 | 9 | test: 10 | cd sdf; go test; cd .. 11 | for dir in $(DIRS); do \ 12 | $(MAKE) -C $$dir $@ || exit 1; \ 13 | done 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/deadsy/sdfx)](https://goreportcard.com/report/github.com/deadsy/sdfx) 3 | [![GoDoc](https://godoc.org/github.com/deadsy/sdfx?status.svg)](https://godoc.org/github.com/deadsy/sdfx/sdf) 4 | 5 | # sdfx 6 | 7 | A simple CAD package written in Go (https://golang.org/) 8 | 9 | * Objects are modelled with 2d and 3d signed distance functions (SDFs). 10 | * Objects are defined with Go code. 11 | * Objects are rendered to an STL/3MF file to be viewed and/or 3d printed. 12 | 13 | ## How To 14 | 1. See the examples. 15 | 2. Write some Go code to define your own object. 16 | 3. Build and run the Go code. 17 | 4. Preview the output in an 3d file viewer (e.g. http://www.meshlab.net/) 18 | 5. Print the STL/3MF file if you like it enough. 19 | 20 | [SDF Viewer Go](https://github.com/Yeicor/sdf-viewer-go) or [SDFX-UI](https://github.com/Yeicor/sdfx-ui) allow faster development iterations, replacing steps 3 and 4 until the final build. 21 | 22 | ## Why? 23 | * SDFs make CSG easy. 24 | * As a language Golang > OpenSCAD. 25 | * SDFs can easily do filleting and chamfering (hard to do with OpenSCAD). 26 | * SDFs are hackable to try out oddball ideas. 27 | 28 | ## Development 29 | * [Roadmap](docs/ROADMAP.md) 30 | 31 | ## Gallery 32 | 33 | ![wheel](docs/gallery/wheel.png "Pottery Wheel Casting Pattern") 34 | ![core_box](docs/gallery/core_box.png "Pottery Wheel Core Box") 35 | ![cylinder_head](docs/gallery/head.png "Cylinder Head") 36 | ![msquare](docs/gallery/msquare.png "M-Square Casting Pattern") 37 | ![axoloti](docs/gallery/axoloti.png "Axoloti Mount Kit") 38 | ![text](docs/gallery/text.png "TrueType font rendering") 39 | ![gyroid](docs/gallery/gyroid.png "Gyroid Surface") 40 | ![icosahedron](docs/gallery/icosahedron.png "Icosahedron") 41 | ![cc16a](docs/gallery/cc16a.png "Reddit CAD Challenge 16A") 42 | ![cc16b](docs/gallery/cc16b_0.png "Reddit CAD Challenge 16B") 43 | ![cc18b](docs/gallery/cc18b.png "Reddit CAD Challenge 18B") 44 | ![cc18c](docs/gallery/cc18c.png "Reddit CAD Challenge 18C") 45 | ![gear](docs/gallery/gear.png "Involute Gear") 46 | ![camshaft](docs/gallery/camshaft.png "Wallaby Camshaft") 47 | ![geneva](docs/gallery/geneva1.png "Geneva Mechanism") 48 | ![nutsandbolts](docs/gallery/nutsandbolts.png "Nuts and Bolts") 49 | ![extrude1](docs/gallery/extrude1.png "Twisted Extrusions") 50 | ![extrude2](docs/gallery/extrude2.png "Scaled and Twisted Extrusions") 51 | ![bezier1](docs/gallery/bezier_bowl.png "Bowl made with Bezier Curves") 52 | ![bezier2](docs/gallery/bezier_shape.png "Extruded Bezier Curves") 53 | ![voronoi](docs/gallery/voronoi.png "2D Points Distance Field") 54 | -------------------------------------------------------------------------------- /docs/ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | 1. Add 3d bezier surfaces. 4 | 5 | 2. Fix the distance function for non-linear extrusions. E.g. screws. 6 | 7 | 3. Generate smaller/better STL files. 8 | See issue #6 9 | 10 | 4. Add the ability to extrude a 2d SDF along a curve. 11 | 12 | 5. Add faster evaluation of the SDF for 2d polygons. 13 | 14 | 6. Add faster evaluation of the SDF for 3d polygons (triangle meshes). 15 | See issue #14. 16 | 17 | 18 | # General 19 | 20 | 1. Make the public API of all packages small. 21 | Don't make public symbols that do not need to be. 22 | 23 | 2. Add more error returns. 24 | All SDF generating functions should return an error, typically used to indicate bad parameters. 25 | These errors should be propagated to the ultimate caller. 26 | 27 | 3. Panics should be used to indicate fundamental code problems - not just bad parameters. 28 | 29 | -------------------------------------------------------------------------------- /docs/gallery/axoloti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/axoloti.png -------------------------------------------------------------------------------- /docs/gallery/bezier_bowl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/bezier_bowl.png -------------------------------------------------------------------------------- /docs/gallery/bezier_shape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/bezier_shape.png -------------------------------------------------------------------------------- /docs/gallery/camshaft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/camshaft.png -------------------------------------------------------------------------------- /docs/gallery/cc16a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/cc16a.png -------------------------------------------------------------------------------- /docs/gallery/cc16b_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/cc16b_0.png -------------------------------------------------------------------------------- /docs/gallery/cc16b_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/cc16b_1.png -------------------------------------------------------------------------------- /docs/gallery/cc18b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/cc18b.png -------------------------------------------------------------------------------- /docs/gallery/cc18c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/cc18c.png -------------------------------------------------------------------------------- /docs/gallery/core_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/core_box.png -------------------------------------------------------------------------------- /docs/gallery/extrude1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/extrude1.png -------------------------------------------------------------------------------- /docs/gallery/extrude2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/extrude2.png -------------------------------------------------------------------------------- /docs/gallery/gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/gear.png -------------------------------------------------------------------------------- /docs/gallery/geneva1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/geneva1.png -------------------------------------------------------------------------------- /docs/gallery/geneva2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/geneva2.png -------------------------------------------------------------------------------- /docs/gallery/gyroid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/gyroid.png -------------------------------------------------------------------------------- /docs/gallery/head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/head.png -------------------------------------------------------------------------------- /docs/gallery/icosahedron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/icosahedron.png -------------------------------------------------------------------------------- /docs/gallery/msquare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/msquare.png -------------------------------------------------------------------------------- /docs/gallery/nutsandbolts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/nutsandbolts.png -------------------------------------------------------------------------------- /docs/gallery/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/text.png -------------------------------------------------------------------------------- /docs/gallery/voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/voronoi.png -------------------------------------------------------------------------------- /docs/gallery/wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/docs/gallery/wheel.png -------------------------------------------------------------------------------- /examples/3dp_nutbolt/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/3dp_nutbolt/SHA1SUM: -------------------------------------------------------------------------------- 1 | 62730f847b96546e44f172cf6ff10418d2e7c768 metric_bolt.stl 2 | b6b872684fb42428e41a7968ca44ce766ddcd431 inch_nut.stl 3 | 7692568d9d23c14b2ccd87671c32a90ed240520b metric_nut.stl 4 | fb6b9a563e035acf30595bf26123c3aaff7ed8db inch_bolt.stl 5 | -------------------------------------------------------------------------------- /examples/3dp_nutbolt/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | 3D Printable Nuts and Bolts 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | ) 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | // Tolerance: Measured in mm. Typically 0.0 to 0.4. Larger is looser. 22 | // Smaller is tighter. Heuristically it could be set to some fraction 23 | // of an FDM nozzle size. It's worth experimenting to find out a good 24 | // value for the specific application and printer. 25 | // const mmTolerance = 0.4 // a bit loose 26 | // const mmTolerance = 0.2 // very tight 27 | // const mmTolerance = 0.3 // good plastic to plastic fit 28 | const mmTolerance = 0.3 29 | const inchTolerance = mmTolerance / sdf.MillimetresPerInch 30 | 31 | // Quality: The long axis of the model is rendered with n cells. A larger 32 | // value will take longer to generate, give a better resolution and a 33 | // larger STL file size. 34 | const quality = 200 35 | 36 | //----------------------------------------------------------------------------- 37 | // inch example 38 | 39 | func inch() error { 40 | // bolt 41 | boltParms := obj.BoltParms{ 42 | Thread: "unc_5/8", 43 | Style: "knurl", 44 | Tolerance: inchTolerance, 45 | TotalLength: 2.0, 46 | ShankLength: 0.5, 47 | } 48 | bolt, err := obj.Bolt(&boltParms) 49 | if err != nil { 50 | return err 51 | } 52 | bolt = sdf.ScaleUniform3D(bolt, sdf.MillimetresPerInch) 53 | render.ToSTL(bolt, "inch_bolt.stl", render.NewMarchingCubesUniform(quality)) 54 | 55 | // nut 56 | nutParms := obj.NutParms{ 57 | Thread: "unc_5/8", 58 | Style: "knurl", 59 | Tolerance: inchTolerance, 60 | } 61 | nut, err := obj.Nut(&nutParms) 62 | if err != nil { 63 | return err 64 | } 65 | nut = sdf.ScaleUniform3D(nut, sdf.MillimetresPerInch) 66 | render.ToSTL(nut, "inch_nut.stl", render.NewMarchingCubesUniform(quality)) 67 | 68 | return nil 69 | } 70 | 71 | //----------------------------------------------------------------------------- 72 | // metric example 73 | 74 | func metric() error { 75 | // bolt 76 | boltParms := obj.BoltParms{ 77 | Thread: "M16x2", 78 | Style: "hex", 79 | Tolerance: mmTolerance, 80 | TotalLength: 50.0, 81 | ShankLength: 10.0, 82 | } 83 | bolt, err := obj.Bolt(&boltParms) 84 | if err != nil { 85 | return err 86 | } 87 | render.ToSTL(bolt, "metric_bolt.stl", render.NewMarchingCubesUniform(quality)) 88 | 89 | // nut 90 | nutParms := obj.NutParms{ 91 | Thread: "M16x2", 92 | Style: "hex", 93 | Tolerance: mmTolerance, 94 | } 95 | nut, err := obj.Nut(&nutParms) 96 | if err != nil { 97 | return err 98 | } 99 | render.ToSTL(nut, "metric_nut.stl", render.NewMarchingCubesUniform(quality)) 100 | 101 | return nil 102 | } 103 | 104 | //----------------------------------------------------------------------------- 105 | 106 | func main() { 107 | err := inch() 108 | if err != nil { 109 | fmt.Printf("%s\n", err) 110 | } 111 | 112 | err = metric() 113 | if err != nil { 114 | fmt.Printf("%s\n", err) 115 | } 116 | } 117 | 118 | //----------------------------------------------------------------------------- 119 | -------------------------------------------------------------------------------- /examples/angle/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/angle/SHA1SUM: -------------------------------------------------------------------------------- 1 | 6bb50c749489c6acad9d340abd616e3c3477fd2b angle.stl 2 | -------------------------------------------------------------------------------- /examples/angle/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | 5 | */ 6 | //----------------------------------------------------------------------------- 7 | 8 | package main 9 | 10 | import ( 11 | "log" 12 | 13 | "github.com/deadsy/sdfx/obj" 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | // material shrinkage 21 | const shrink = 1.0 / 0.999 // PLA ~0.1% 22 | //const shrink = 1.0/0.995; // ABS ~0.5% 23 | 24 | //----------------------------------------------------------------------------- 25 | 26 | func main() { 27 | 28 | const l = 1.25 * sdf.MillimetresPerInch 29 | const t = 0.125 * sdf.MillimetresPerInch 30 | const r = 0.125 * sdf.MillimetresPerInch 31 | 32 | k := obj.AngleParms{ 33 | X: obj.AngleLeg{l, t}, 34 | Y: obj.AngleLeg{l, t}, 35 | RootRadius: r, 36 | Length: 12 * sdf.MillimetresPerInch, 37 | } 38 | 39 | s, err := obj.Angle3D(&k) 40 | if err != nil { 41 | log.Fatalf("error: %s", err) 42 | } 43 | s = sdf.ScaleUniform3D(s, shrink) 44 | render.ToSTL(s, "angle.stl", render.NewMarchingCubesOctree(300)) 45 | } 46 | 47 | //----------------------------------------------------------------------------- 48 | -------------------------------------------------------------------------------- /examples/arrow/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/arrow/SHA1SUM: -------------------------------------------------------------------------------- 1 | ae5d00f9a29b0277a92650a8f8ed27e55e11c425 axes3.stl 2 | c68ef109b8e87c2f6d070b24434b2f32fc7d163c axes1.stl 3 | 921c2e7a1856002d66d9427801b4d060b92cb0bc axes2.stl 4 | e27c2b82a6ad881a341ab53fae2f0e513ec1c539 arrow1.stl 5 | -------------------------------------------------------------------------------- /examples/arrow/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Arrow/Axes Example 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | func arrow1() (sdf.SDF3, error) { 23 | k := obj.ArrowParms{ 24 | Axis: [2]float64{50, 1}, 25 | Head: [2]float64{5, 2}, 26 | Tail: [2]float64{5, 2}, 27 | Style: "cb", 28 | } 29 | return obj.Arrow3D(&k) 30 | } 31 | 32 | //----------------------------------------------------------------------------- 33 | 34 | func axes1() (sdf.SDF3, error) { 35 | return obj.Axes3D(v3.Vec{-10, -10, -10}, v3.Vec{10, 20, 20}) 36 | } 37 | 38 | func axes2() (sdf.SDF3, error) { 39 | return obj.Axes3D(v3.Vec{-10, -20, -30}, v3.Vec{0, 0, 0}) 40 | } 41 | 42 | func axes3() (sdf.SDF3, error) { 43 | return obj.Axes3D(v3.Vec{0, 0, 0}, v3.Vec{500, 500, 1000}) 44 | } 45 | 46 | //----------------------------------------------------------------------------- 47 | 48 | func main() { 49 | arrow1, err := arrow1() 50 | if err != nil { 51 | log.Fatalf("error: %s", err) 52 | } 53 | render.ToSTL(arrow1, "arrow1.stl", render.NewMarchingCubesOctree(300)) 54 | 55 | axes1, err := axes1() 56 | if err != nil { 57 | log.Fatalf("error: %s", err) 58 | } 59 | render.ToSTL(axes1, "axes1.stl", render.NewMarchingCubesOctree(300)) 60 | 61 | axes2, err := axes2() 62 | if err != nil { 63 | log.Fatalf("error: %s", err) 64 | } 65 | render.ToSTL(axes2, "axes2.stl", render.NewMarchingCubesOctree(300)) 66 | 67 | axes3, err := axes3() 68 | if err != nil { 69 | log.Fatalf("error: %s", err) 70 | } 71 | render.ToSTL(axes3, "axes3.stl", render.NewMarchingCubesOctree(300)) 72 | 73 | } 74 | 75 | //----------------------------------------------------------------------------- 76 | -------------------------------------------------------------------------------- /examples/axochord/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/axochord/SHA1SUM: -------------------------------------------------------------------------------- 1 | b8949b06966e37c461fe3049fa2048c339cf7deb lower.stl 2 | e2f27841e47f74a1be3deecf551713ccbe77a92b upper.stl 3 | e8e7ffd3272cf1971354199052aa43967e65f506 plate.dxf 4 | -------------------------------------------------------------------------------- /examples/axoloti/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/axoloti/SHA1SUM: -------------------------------------------------------------------------------- 1 | fdd337d5bef744b283e18539ed8e1307bd4e3fc6 panel_and_base.stl 2 | ef1bfc7f9ff7929afe5e11cffbe14249728cb35f base.stl 3 | 8787e69b1f60d12fc3747ab7f7e9032dee8781ad panel.stl 4 | -------------------------------------------------------------------------------- /examples/beehive/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/beehive/SHA1SUM: -------------------------------------------------------------------------------- 1 | 84e8331b9c7ea2bb7b4e3431318718a133b55605 wheel.stl 2 | feb2edecb01188777216d15a00af662231a0ec34 antcap.stl 3 | f395c2d4885321096c16963979bb963f7bf7add2 retainer.stl 4 | 6db55018eb7f1f7d72656d06e14972530e9b84bd reducer.stl 5 | -------------------------------------------------------------------------------- /examples/benchmark/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/benchmark/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/deadsy/sdfx/sdf" 7 | v3 "github.com/deadsy/sdfx/vec/v3" 8 | ) 9 | 10 | func main() { 11 | s2d, err := sdf.Circle2D(5) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | sdf.BenchmarkSDF2("circle SDF2", s2d) 16 | 17 | s2d, err = sdf.FlatFlankCam2D(30, 20, 5) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | sdf.BenchmarkSDF2("cam1 SDF2", s2d) 22 | 23 | s2d, err = sdf.ThreeArcCam2D(30, 20, 5, 200) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | sdf.BenchmarkSDF2("cam2 SDF2", s2d) 28 | 29 | s2d, err = sdf.Polygon2D(sdf.Nagon(6, 10.0)) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | sdf.BenchmarkSDF2("poly6 SDF2", s2d) 34 | 35 | s2d, err = sdf.Polygon2D(sdf.Nagon(12, 10.0)) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | sdf.BenchmarkSDF2("poly12 SDF2", s2d) 40 | 41 | s3d, err := sdf.Box3D(v3.Vec{10, 20, 30}, 1) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | sdf.BenchmarkSDF3("box SDF3", s3d) 46 | } 47 | -------------------------------------------------------------------------------- /examples/bezier/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/bezier/SHA1SUM: -------------------------------------------------------------------------------- 1 | c9ba248df3904f9946a0c310d36bed0a87b3e809 bowlingpin.stl 2 | 9f82530ef50026fcfaa6941bc21e15d1b06d66b2 shape.stl 3 | adddacde85dabbb082ed4aabc756cbeeebf1e3a2 egg2.stl 4 | 1d89995aa46c3495e08993df787eb2ad803a860c vase.stl 5 | b51816e709434b4457e038806b34880f5fc3e11e bowl.stl 6 | 3ed59663d04b5d2d86e56c3c394962a4b2b2be57 egg1.stl 7 | -------------------------------------------------------------------------------- /examples/birdhouse/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/birdhouse/SHA1SUM: -------------------------------------------------------------------------------- 1 | 322d5fab83e578ccf5eb8b7fc74e3dc99365a393 birdhouse.stl 2 | -------------------------------------------------------------------------------- /examples/birdhouse/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | An A-Frame Birdhouse. 5 | 6 | git@github.com:deadsy/sdfx.git 7 | https://github.com/deadsy/sdfx/tree/master/examples/birdhouse 8 | 9 | */ 10 | //----------------------------------------------------------------------------- 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | "math" 17 | 18 | "github.com/deadsy/sdfx/obj" 19 | "github.com/deadsy/sdfx/render" 20 | "github.com/deadsy/sdfx/sdf" 21 | v3 "github.com/deadsy/sdfx/vec/v3" 22 | ) 23 | 24 | //----------------------------------------------------------------------------- 25 | 26 | const width = 120.0 27 | const height = 85.0 28 | const thickness = 2.0 29 | const hookHeight = 10.0 30 | const holeFactor = 0.9 // control the hole size 0..1 31 | 32 | //----------------------------------------------------------------------------- 33 | 34 | // holeRadius returns the radius for a circle inscribed within the frame triangle. 35 | func holeRadius() float64 { 36 | b := width * 0.5 37 | h := height 38 | a := math.Sqrt((b * b) + (h * h)) 39 | return b * math.Sqrt((a-b)/(a+b)) 40 | } 41 | 42 | // hook returns a hook used to suspend the birdhouse. 43 | func hook() (sdf.SDF3, error) { 44 | k := obj.WasherParms{ 45 | Thickness: thickness, 46 | InnerRadius: hookHeight * 0.5, 47 | OuterRadius: hookHeight, 48 | Remove: 0.5, 49 | } 50 | s, err := obj.Washer3D(&k) 51 | if err != nil { 52 | return nil, err 53 | } 54 | m := sdf.RotateY(sdf.DtoR(90)) 55 | m = sdf.Translate3d(v3.Vec{0, 0, height + thickness}).Mul(m) 56 | return sdf.Transform3D(s, m), nil 57 | } 58 | 59 | // frame returns a birdhouse A-frame. 60 | func frame() (sdf.SDF3, error) { 61 | p := sdf.NewPolygon() 62 | p.Add(width/2, 0) 63 | p.Add(0, height) 64 | p.Add(-width/2, 0) 65 | s, err := sdf.Polygon2D(p.Vertices()) 66 | if err != nil { 67 | return nil, err 68 | } 69 | sOuter := sdf.Offset2D(s, 2*thickness) 70 | sInner := sdf.Offset2D(s, thickness) 71 | f2d := sdf.Difference2D(sOuter, sInner) 72 | f3d := sdf.Extrude3D(f2d, width*1.1) 73 | return sdf.Transform3D(f3d, sdf.RotateX(sdf.DtoR(90))), nil 74 | } 75 | 76 | func hole() (sdf.SDF3, error) { 77 | r := holeRadius() 78 | s, err := sdf.Cylinder3D(2*width, r*holeFactor, 0) 79 | if err != nil { 80 | return nil, err 81 | } 82 | m := sdf.RotateX(sdf.DtoR(90)) 83 | m = sdf.Translate3d(v3.Vec{0, 0, r}).Mul(m) 84 | return sdf.Transform3D(s, m), nil 85 | } 86 | 87 | // cross returns the union of s and a copy rotated 90 degrees about the z axis. 88 | func cross(s sdf.SDF3) sdf.SDF3 { 89 | s1 := sdf.Transform3D(s, sdf.RotateZ(sdf.DtoR(90))) 90 | return sdf.Union3D(s, s1) 91 | } 92 | 93 | func birdhouse() (sdf.SDF3, error) { 94 | frame, err := frame() 95 | if err != nil { 96 | return nil, err 97 | } 98 | hole, err := hole() 99 | if err != nil { 100 | return nil, err 101 | } 102 | hook, err := hook() 103 | if err != nil { 104 | return nil, err 105 | } 106 | s := sdf.Difference3D(cross(frame), cross(hole)) 107 | s = sdf.Union3D(s, hook) 108 | return s, nil 109 | } 110 | 111 | //----------------------------------------------------------------------------- 112 | 113 | func main() { 114 | s, err := birdhouse() 115 | if err != nil { 116 | log.Fatalf("error: %s", err) 117 | } 118 | render.ToSTL(s, "birdhouse.stl", render.NewMarchingCubesOctree(300)) 119 | } 120 | 121 | //----------------------------------------------------------------------------- 122 | -------------------------------------------------------------------------------- /examples/bjj/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/bjj/SHA1SUM: -------------------------------------------------------------------------------- 1 | 69030ed0289a09f47470f04b29264d20e40f7ea0 bushing.stl 2 | 0e64b5ac2910539fd8315cb1bf33526be6029239 plate.stl 3 | 0ddedf9cb322fb860b144c36b1cc0e5bad2f34f6 gear.stl 4 | -------------------------------------------------------------------------------- /examples/bolt_container/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/bolt_container/SHA1SUM: -------------------------------------------------------------------------------- 1 | cf46d9d726cc5ac8605fb80bc7a786e2630f8901 container.stl 2 | -------------------------------------------------------------------------------- /examples/bolt_container/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Nuts and Bolts 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | const hexRadius = 40.0 23 | const hexHeight = 20.0 24 | const screwRadius = hexRadius * 0.7 25 | const threadPitch = screwRadius / 5.0 26 | const screwLength = 40.0 27 | const tolerance = 0.5 28 | 29 | const baseThickness = 4.0 30 | 31 | //----------------------------------------------------------------------------- 32 | 33 | func boltContainer() (sdf.SDF3, error) { 34 | 35 | // build hex head 36 | hex, err := obj.HexHead3D(hexRadius, hexHeight, "tb") 37 | if err != nil { 38 | return nil, err 39 | } 40 | // build the screw portion 41 | r := screwRadius - tolerance 42 | l := screwLength 43 | isoThread, err := sdf.ISOThread(r, threadPitch, true) 44 | if err != nil { 45 | return nil, err 46 | } 47 | screw, err := sdf.Screw3D(isoThread, l, 0, threadPitch, 1) 48 | if err != nil { 49 | return nil, err 50 | } 51 | // chamfer the thread 52 | screw, err = obj.ChamferedCylinder(screw, 0, 0.25) 53 | if err != nil { 54 | return nil, err 55 | } 56 | screw = sdf.Transform3D(screw, sdf.Translate3d(v3.Vec{0, 0, l / 2})) 57 | 58 | // build the internal cavity 59 | r = screwRadius * 0.75 60 | l = screwLength + hexHeight 61 | round := screwRadius * 0.1 62 | ofs := (l / 2) - (hexHeight / 2) + baseThickness 63 | cavity, err := sdf.Cylinder3D(l, r, round) 64 | if err != nil { 65 | return nil, err 66 | } 67 | cavity = sdf.Transform3D(cavity, sdf.Translate3d(v3.Vec{0, 0, ofs})) 68 | 69 | return sdf.Difference3D(sdf.Union3D(hex, screw), cavity), nil 70 | } 71 | 72 | //----------------------------------------------------------------------------- 73 | 74 | func nutTop() sdf.SDF3 { 75 | return nil 76 | } 77 | 78 | //----------------------------------------------------------------------------- 79 | 80 | func main() { 81 | bc, err := boltContainer() 82 | if err != nil { 83 | log.Fatalf("error: %s", err) 84 | } 85 | render.ToSTL(bc, "container.stl", render.NewMarchingCubesOctree(200)) 86 | } 87 | 88 | //----------------------------------------------------------------------------- 89 | -------------------------------------------------------------------------------- /examples/bucky/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/bucky/SHA1SUM: -------------------------------------------------------------------------------- 1 | 7794b3356063098862ae85487169769df56cbd15 icosahedron.stl 2 | -------------------------------------------------------------------------------- /examples/bucky/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Bucky Ball 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | type edge [2]int 23 | 24 | var edges = []edge{ 25 | {1, 4}, {4, 8}, {8, 10}, {10, 6}, {6, 1}, 26 | {0, 4}, {4, 9}, {9, 11}, {11, 6}, {6, 0}, 27 | {3, 7}, {7, 10}, {10, 8}, {8, 5}, {5, 3}, 28 | {5, 9}, {9, 11}, {11, 7}, {7, 2}, {2, 5}, 29 | {0, 1}, {1, 9}, {9, 5}, {5, 8}, {8, 0}, 30 | {4, 9}, {9, 3}, {3, 2}, {2, 8}, {8, 4}, 31 | {1, 0}, {0, 10}, {10, 7}, {7, 11}, {11, 1}, 32 | {2, 3}, {3, 11}, {11, 6}, {6, 10}, {10, 2}, 33 | {0, 10}, {10, 2}, {2, 5}, {5, 4}, {4, 0}, 34 | {1, 4}, {4, 5}, {5, 3}, {3, 11}, {11, 1}, 35 | {7, 2}, {2, 8}, {8, 0}, {0, 6}, {6, 7}, 36 | {1, 6}, {6, 7}, {7, 3}, {3, 9}, {9, 1}, 37 | } 38 | 39 | const φ = 1.618033988749895 40 | 41 | var vertex = []v3.Vec{ 42 | {1, φ, 0}, {-1, φ, 0}, {1, -φ, 0}, {-1, -φ, 0}, 43 | {0, 1, φ}, {0, -1, φ}, {0, 1, -φ}, {0, -1, -φ}, 44 | {φ, 0, 1}, {-φ, 0, 1}, {φ, 0, -1}, {-φ, 0, -1}, 45 | } 46 | 47 | func icosahedron() (sdf.SDF3, error) { 48 | 49 | r0 := φ * 0.05 50 | r1 := r0 * 2.0 51 | 52 | k := obj.ArrowParms{ 53 | Axis: [2]float64{0, r0}, 54 | Head: [2]float64{0, r1}, 55 | Tail: [2]float64{0, r1}, 56 | Style: "b.", 57 | } 58 | 59 | var bb sdf.SDF3 60 | for _, e := range edges { 61 | head := vertex[e[0]] 62 | tail := vertex[e[1]] 63 | s, err := obj.DirectedArrow3D(&k, head, tail) 64 | if err != nil { 65 | return nil, err 66 | } 67 | bb = sdf.Union3D(bb, s) 68 | } 69 | 70 | return bb, nil 71 | } 72 | 73 | //----------------------------------------------------------------------------- 74 | 75 | func main() { 76 | s, err := icosahedron() 77 | if err != nil { 78 | log.Fatalf("error: %s", err) 79 | } 80 | render.ToSTL(s, "icosahedron.stl", render.NewMarchingCubesOctree(300)) 81 | render.To3MF(s, "icosahedron.3mf", render.NewMarchingCubesOctree(300)) 82 | } 83 | 84 | //----------------------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- /examples/camshaft/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/camshaft/SHA1SUM: -------------------------------------------------------------------------------- 1 | 50c80de94a736b0f68d51479424a5228a411db8a camshaft.stl 2 | -------------------------------------------------------------------------------- /examples/camshaft/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Wallaby Camshaft 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | "math" 14 | 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | func camshaft() (sdf.SDF3, error) { 23 | 24 | // build the shaft from an SoR 25 | const l0 = 13.0 / 16.0 26 | const r0 = (5.0 / 16.0) / 2.0 27 | const l1 = (3.0/32.0)*2.0 + (5.0/16.0)*2.0 + (11.0 / 16.0) + (3.0/16.0)*4.0 28 | const r1 = (13.0 / 32.0) / 2.0 29 | const l2 = 1.0 / 2.0 30 | const r2 = (5.0 / 16.0) / 2.0 31 | const l3 = 3.0 / 8.0 32 | r3 := r2 - l3*math.Tan(sdf.DtoR(10.0)) 33 | const l4 = 1.0 / 4.0 34 | 35 | p := sdf.NewPolygon() 36 | p.Add(0, 0) 37 | p.Add(r0, 0).Rel() 38 | p.Add(0, l0).Rel() 39 | p.Add(r1-r0, 0).Rel() 40 | p.Add(0, l1).Rel() 41 | p.Add(r2-r1, 0).Rel() 42 | p.Add(0, l2).Rel() 43 | p.Add(r3-r2, l3).Rel() 44 | p.Add(0, l4).Rel() 45 | p.Add(-r3, 0).Rel() 46 | 47 | shaft2d, err := sdf.Polygon2D(p.Vertices()) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | shaft3d, err := sdf.Revolve3D(shaft2d) 53 | if err != nil { 54 | return nil, err 55 | } 56 | // make the cams 57 | const valveDiameter = 0.25 58 | const rockerRatio = 1.0 59 | const lift = valveDiameter * rockerRatio * 0.25 60 | const camDiameter = 5.0 / 8.0 61 | const camWidth = 3.0 / 16.0 62 | const k = 1.05 63 | inletTheta := sdf.DtoR(-110) 64 | 65 | inlet2d, _ := sdf.MakeThreeArcCam(lift, sdf.DtoR(115), camDiameter, k) 66 | inlet3d := sdf.Extrude3D(inlet2d, camWidth) 67 | exhaust2d, _ := sdf.MakeThreeArcCam(lift, sdf.DtoR(125), camDiameter, k) 68 | exhaust3d := sdf.Extrude3D(exhaust2d, camWidth) 69 | 70 | zOfs := (13.0 / 16.0) + (3.0 / 32.0) + (camWidth / 2.0) 71 | m := sdf.Translate3d(v3.Vec{0, 0, zOfs}) 72 | m = sdf.RotateZ(0).Mul(m) 73 | ex4 := sdf.Transform3D(exhaust3d, m) 74 | 75 | zOfs += (5.0 / 16.0) + camWidth 76 | m = sdf.Translate3d(v3.Vec{0, 0, zOfs}) 77 | m = sdf.RotateZ(inletTheta).Mul(m) 78 | in3 := sdf.Transform3D(inlet3d, m) 79 | 80 | zOfs += (11.0 / 16.0) + camWidth 81 | m = sdf.Translate3d(v3.Vec{0, 0, zOfs}) 82 | m = sdf.RotateZ(inletTheta + sdf.Pi).Mul(m) 83 | in2 := sdf.Transform3D(inlet3d, m) 84 | 85 | zOfs += (5.0 / 16.0) + camWidth 86 | m = sdf.Translate3d(v3.Vec{0, 0, zOfs}) 87 | m = sdf.RotateZ(sdf.Pi).Mul(m) 88 | ex1 := sdf.Transform3D(exhaust3d, m) 89 | 90 | return sdf.Union3D(shaft3d, ex1, in2, in3, ex4), nil 91 | } 92 | 93 | //----------------------------------------------------------------------------- 94 | 95 | func main() { 96 | s, err := camshaft() 97 | if err != nil { 98 | log.Fatalf("error: %s", err) 99 | } 100 | render.ToSTL(s, "camshaft.stl", render.NewMarchingCubesOctree(400)) 101 | } 102 | 103 | //----------------------------------------------------------------------------- 104 | -------------------------------------------------------------------------------- /examples/cap/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/cap/SHA1SUM: -------------------------------------------------------------------------------- 1 | cf4553902f2d381224be06c99f7d5689bd8d17f6 cap.stl 2 | -------------------------------------------------------------------------------- /examples/cap/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Tube Cap 5 | 6 | This is a simple round cap that fits onto the outside of a tube. 7 | 8 | */ 9 | //----------------------------------------------------------------------------- 10 | 11 | package main 12 | 13 | import ( 14 | "log" 15 | 16 | "github.com/deadsy/sdfx/render" 17 | "github.com/deadsy/sdfx/sdf" 18 | v3 "github.com/deadsy/sdfx/vec/v3" 19 | ) 20 | 21 | //----------------------------------------------------------------------------- 22 | 23 | const wallThickness = 2.0 24 | const innerDiameter = 75.5 25 | const innerHeight = 15.0 26 | 27 | //----------------------------------------------------------------------------- 28 | 29 | // material shrinkage 30 | const shrink = 1.0 / 0.999 // PLA ~0.1% 31 | //const shrink = 1.0/0.995; // ABS ~0.5% 32 | 33 | //----------------------------------------------------------------------------- 34 | 35 | func tubeCap() (sdf.SDF3, error) { 36 | 37 | h := innerHeight + wallThickness 38 | r := (innerDiameter * 0.5) + wallThickness 39 | outer, err := sdf.Cylinder3D(h, r, 1.0) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | h = innerHeight 45 | r = innerDiameter * 0.5 46 | inner, err := sdf.Cylinder3D(h, r, 1.0) 47 | inner = sdf.Transform3D(inner, sdf.Translate3d(v3.Vec{0, 0, wallThickness * 0.5})) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | return sdf.Difference3D(outer, inner), nil 53 | } 54 | 55 | //----------------------------------------------------------------------------- 56 | 57 | func main() { 58 | c, err := tubeCap() 59 | if err != nil { 60 | log.Fatalf("error: %s", err) 61 | } 62 | render.ToSTL(sdf.ScaleUniform3D(c, shrink), "cap.stl", render.NewMarchingCubesOctree(120)) 63 | } 64 | 65 | //----------------------------------------------------------------------------- 66 | -------------------------------------------------------------------------------- /examples/carburetor/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/carburetor/SHA1SUM: -------------------------------------------------------------------------------- 1 | f022c40c4661c7a2b0c3ace90eaf8aaba3e15858 air.stl 2 | 70e3044b059d284c335f9fa4d612ce8c032aa793 plate.stl 3 | -------------------------------------------------------------------------------- /examples/carburetor/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Carburetor/Manifold Block-Off Plates 5 | 6 | Block off plates to stop foreign objects getting into intake manifolds and 7 | carburetors. 8 | 9 | */ 10 | //----------------------------------------------------------------------------- 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/deadsy/sdfx/render" 18 | "github.com/deadsy/sdfx/sdf" 19 | v2 "github.com/deadsy/sdfx/vec/v2" 20 | v3 "github.com/deadsy/sdfx/vec/v3" 21 | ) 22 | 23 | //----------------------------------------------------------------------------- 24 | // air intake cover: derived from measurement of an Edelbrock carburetor. 25 | 26 | const airIntakeRadius = 0.5 * 5.125 * sdf.MillimetresPerInch 27 | const airIntakeWall = (3.0 / 16.0) * sdf.MillimetresPerInch 28 | const airIntakeHeight = 1.375 * sdf.MillimetresPerInch 29 | const airIntakeHole = 0.5 * (5.0 / 16.0) * sdf.MillimetresPerInch 30 | 31 | func airIntakeCover() (sdf.SDF3, error) { 32 | 33 | const h0 = 2.0 * (airIntakeHeight + airIntakeWall) 34 | const r0 = airIntakeRadius + airIntakeWall 35 | body, err := sdf.Cylinder3D(h0, r0, 0.75*airIntakeWall) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | const h1 = 2.0 * airIntakeHeight 41 | const r1 = airIntakeRadius 42 | cavity, err := sdf.Cylinder3D(h1, r1, 0) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | const h2 = h0 48 | const r2 = airIntakeHole 49 | hole, err := sdf.Cylinder3D(h2, r2, 0) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | cover := sdf.Difference3D(body, sdf.Union3D(cavity, hole)) 55 | return sdf.Cut3D(cover, v3.Vec{0, 0, 0}, v3.Vec{0, 0, 1}), nil 56 | } 57 | 58 | //----------------------------------------------------------------------------- 59 | // manifold blockoff plate: derived from measurement of an Edelbrock intake manifold. 60 | 61 | const dX = 0.5 * 5.625 * sdf.MillimetresPerInch 62 | const dY0 = 0.5 * 4.25 * sdf.MillimetresPerInch // spreadbore 63 | const dY1 = 0.5 * 5.16 * sdf.MillimetresPerInch // holley 64 | const holeRadius = 0.5 * (5.0 / 16.0) * sdf.MillimetresPerInch 65 | const holeClearance = 1.05 66 | 67 | const plateX = (2.0 * dX) + 20.0 68 | const plateY = (2.0 * dY1) + 20.0 69 | const plateZ = 4.0 70 | 71 | func blockOffPlate() (sdf.SDF3, error) { 72 | 73 | // plate 74 | plate := sdf.Box2D(v2.Vec{plateX, plateY}, 1.0*plateZ) 75 | 76 | // holes 77 | hole, err := sdf.Circle2D(holeClearance * holeRadius) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | posn := []v2.Vec{ 83 | {dX, dY0}, 84 | {-dX, -dY0}, 85 | {dX, -dY0}, 86 | {-dX, dY0}, 87 | {dX, dY1}, 88 | {-dX, -dY1}, 89 | {dX, -dY1}, 90 | {-dX, dY1}, 91 | } 92 | holes := sdf.Multi2D(hole, posn) 93 | 94 | return sdf.Extrude3D(sdf.Difference2D(plate, holes), plateZ), nil 95 | } 96 | 97 | //----------------------------------------------------------------------------- 98 | 99 | func main() { 100 | s, err := blockOffPlate() 101 | if err != nil { 102 | log.Fatalf("error: %s", err) 103 | } 104 | render.ToSTL(s, "plate.stl", render.NewMarchingCubesOctree(300)) 105 | 106 | s, err = airIntakeCover() 107 | if err != nil { 108 | log.Fatalf("error: %s", err) 109 | } 110 | render.ToSTL(s, "air.stl", render.NewMarchingCubesOctree(300)) 111 | 112 | } 113 | 114 | //----------------------------------------------------------------------------- 115 | -------------------------------------------------------------------------------- /examples/challenge/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/challenge/SHA1SUM: -------------------------------------------------------------------------------- 1 | 19eefd74e170f626b5cfabf704ca48116d19ff24 cc16a.stl 2 | c46acf64c98c70940845c1b93839ce00fe95167f cc18c.stl 3 | 6af88b0f55e0a7be1c0954f4e2acad4ee319f4a5 cc18b.stl 4 | ec17b150d1c94799f3c7ec405faba8c30c89c2bf cc16b.stl 5 | 4162385649041bde9b6871869f2f6e661986be36 cc18a.dxf 6 | -------------------------------------------------------------------------------- /examples/challenge/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | 3 | //----------------------------------------------------------------------------- 4 | 5 | package main 6 | 7 | import ( 8 | "log" 9 | 10 | "github.com/deadsy/sdfx/render" 11 | ) 12 | 13 | //----------------------------------------------------------------------------- 14 | 15 | func main() { 16 | 17 | s, err := cc16a() 18 | if err != nil { 19 | log.Fatalf("error: %s", err) 20 | } 21 | render.ToSTL(s, "cc16a.stl", render.NewMarchingCubesOctree(200)) 22 | 23 | s, err = cc16b() 24 | if err != nil { 25 | log.Fatalf("error: %s", err) 26 | } 27 | render.ToSTL(s, "cc16b.stl", render.NewMarchingCubesOctree(200)) 28 | 29 | cc18a() 30 | 31 | s, err = cc18b() 32 | if err != nil { 33 | log.Fatalf("error: %s", err) 34 | } 35 | render.ToSTL(s, "cc18b.stl", render.NewMarchingCubesOctree(200)) 36 | 37 | s, err = cc18c() 38 | if err != nil { 39 | log.Fatalf("error: %s", err) 40 | } 41 | render.ToSTL(s, "cc18c.stl", render.NewMarchingCubesOctree(200)) 42 | } 43 | 44 | //----------------------------------------------------------------------------- 45 | -------------------------------------------------------------------------------- /examples/cylinder_head/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/cylinder_head/SHA1SUM: -------------------------------------------------------------------------------- 1 | 0c9dfaf4cea0e8bea14770e86fe73d6a97f33a51 head.stl 2 | -------------------------------------------------------------------------------- /examples/dc2test/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/dc2test/SHA1SUM: -------------------------------------------------------------------------------- 1 | dee150f4f56fce0a473c82b704ca22ea2b2d62eb output.dxf 2 | -------------------------------------------------------------------------------- /examples/dc2test/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Text Example 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | func main() { 21 | 22 | f, err := sdf.LoadFont("../../files/cmr10.ttf") 23 | //f, err := sdf.LoadFont("Times_New_Roman.ttf") 24 | //f, err := sdf.LoadFont("wt064.ttf") 25 | 26 | if err != nil { 27 | log.Fatalf("can't read font file %s\n", err) 28 | } 29 | 30 | t := sdf.NewText("hi!") 31 | //t := sdf.NewText("相同的不同") 32 | 33 | s0, err := sdf.Text2D(f, t, 10.0) 34 | if err != nil { 35 | log.Fatalf("can't generate text %s\n", err) 36 | } 37 | 38 | //render.ToDXF(s2d, "output.dxf", render.NewMarchingSquaresQuadtree(600)) 39 | render.ToDXF(s0, "output.dxf", render.NewDualContouring2D(50)) 40 | } 41 | 42 | //----------------------------------------------------------------------------- 43 | -------------------------------------------------------------------------------- /examples/delta/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/delta/SHA1SUM: -------------------------------------------------------------------------------- 1 | 97ef663a5758b7445a22144ad5c334c92e3083b4 servomount.stl 2 | 5d9f0d05b27ff33eaa94a64d19ee4eed09420c68 base.stl 3 | f3507391bc64d4526e2e482c26ee6c7ac97ee45d platform.stl 4 | f4cea8dc2e93a4159834718c241537c0b7670cfc rodend.stl 5 | 0543b2f185d241c96dffc733f10bb2bc74387ea5 arm.stl 6 | -------------------------------------------------------------------------------- /examples/devo/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/devo/SHA1SUM: -------------------------------------------------------------------------------- 1 | 5baf90c8014d247e1c39cdea33b088a8df0d5c47 energy_dome.stl 2 | -------------------------------------------------------------------------------- /examples/devo/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Devo Energy Dome 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "log" 14 | 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | ) 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | func dome(r, h, w float64) (sdf.SDF3, error) { 22 | 23 | fillet := w 24 | 25 | // step heights 26 | k := 0.8 27 | stepH0 := h 28 | stepH1 := stepH0 * k 29 | stepH2 := stepH1 * k 30 | stepH3 := stepH2 * k 31 | 32 | height := stepH0 + stepH1 + stepH2 + stepH3 33 | fmt.Printf("height %f inches\n", height/sdf.MillimetresPerInch) 34 | 35 | // step ledges 36 | stepX := (r / 4.0) * 0.75 37 | stepX0 := stepX * 0.20 38 | stepX1 := stepX - stepX0 39 | 40 | // outer shell 41 | p := sdf.NewPolygon() 42 | p.Add(0, 0) 43 | p.Add(r, 0).Rel() 44 | p.Add(-stepX0, stepH0).Rel().Smooth(fillet, 4) 45 | p.Add(-stepX1, 0).Rel().Smooth(fillet, 4) 46 | p.Add(-stepX0, stepH1).Rel().Smooth(fillet, 4) 47 | p.Add(-stepX1, 0).Rel().Smooth(fillet, 4) 48 | p.Add(-stepX0, stepH2).Rel().Smooth(fillet, 4) 49 | p.Add(-stepX1, 0).Rel().Smooth(fillet, 4) 50 | p.Add(-stepX0, stepH3).Rel().Smooth(fillet, 4) 51 | p.Add(0, height) 52 | s, err := sdf.Polygon2D(p.Vertices()) 53 | if err != nil { 54 | return nil, err 55 | } 56 | outer, err := sdf.Revolve3D(s) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | // inner shell 62 | 63 | b := sdf.NewBezier() 64 | 65 | x := 0.0 66 | y := 0.0 67 | b.Add(x, y) 68 | 69 | x += r - w 70 | b.Add(x, y) 71 | 72 | x -= stepX 73 | y += stepH0 - w 74 | b.Add(x, y) 75 | 76 | x -= stepX 77 | y += stepH1 78 | b.Add(x, y) 79 | 80 | x -= stepX 81 | y += stepH2 82 | b.Add(x, y) 83 | 84 | y += stepH3 85 | b.Add(0, y) 86 | 87 | b.Close() 88 | 89 | p, err = b.Polygon() 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | s, err = sdf.Polygon2D(p.Vertices()) 95 | if err != nil { 96 | return nil, err 97 | } 98 | 99 | inner, err := sdf.Revolve3D(s) 100 | if err != nil { 101 | return nil, err 102 | } 103 | return sdf.Difference3D(outer, inner), nil 104 | } 105 | 106 | //----------------------------------------------------------------------------- 107 | 108 | func main() { 109 | radius := (9.5 * sdf.MillimetresPerInch) / 2.0 110 | h0 := 2.05 * sdf.MillimetresPerInch 111 | wall := 4.0 112 | 113 | s, err := dome(radius, h0, wall) 114 | if err != nil { 115 | log.Fatalf("error: %s", err) 116 | } 117 | 118 | //s = sdf.Cut3D(s, V3{0, 0, 0}, v3.Vec{0, 1, 0}) 119 | render.ToSTL(s, "energy_dome.stl", render.NewMarchingCubesOctree(150)) 120 | } 121 | 122 | //----------------------------------------------------------------------------- 123 | -------------------------------------------------------------------------------- /examples/draincover/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/draincover/SHA1SUM: -------------------------------------------------------------------------------- 1 | 393f550896847c97efc2756b635f8b9ae18a0209 drain4.stl 2 | d4aeee3e3d113db910c3051c4b2c236a9ee32582 vent2.stl 3 | 39404c947113643c8ebd27c84dc3a8a34513798c drain12.stl 4 | 61b8572f71a7a0023e797ae2e1a85520f203f2fa drain6.stl 5 | -------------------------------------------------------------------------------- /examples/drone/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/drone/SHA1SUM: -------------------------------------------------------------------------------- 1 | 8cb1b6d115b4a98c3f23d8bf92b1a5cf93db2076 socket.stl 2 | cce17e34e80a8324403df0b20728ad2cb967e7e6 arm.stl 3 | -------------------------------------------------------------------------------- /examples/drone/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Drone Parts 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v2 "github.com/deadsy/sdfx/vec/v2" 18 | v3 "github.com/deadsy/sdfx/vec/v3" 19 | ) 20 | 21 | //----------------------------------------------------------------------------- 22 | // material shrinkage 23 | 24 | const shrink = 1.0 / 0.999 // PLA ~0.1% 25 | //const shrink = 1.0/0.995; // ABS ~0.5% 26 | 27 | //----------------------------------------------------------------------------- 28 | 29 | // https://www.flashhobby.com/d2830-fixed-wing-motor.html 30 | var kArm = obj.DroneArmParms{ 31 | MotorSize: v2.Vec{28, 30}, // motor diameter/height 32 | MotorMount: v3.Vec{16, 19, 3.4}, // motor mount l0, l1, diameter 33 | RotorCavity: v2.Vec{9, 1.5}, // cavity for bottom of rotor 34 | WallThickness: 3.0, // wall thickness 35 | SideClearance: 1.5, // wall to motor clearance 36 | MountHeight: 0.7, // height of motor mount wrt motor height 37 | ArmHeight: 0.9, // height of arm wrt motor mount height 38 | ArmLength: 70.0, // length of rotor arm 39 | } 40 | 41 | var kSocket = obj.DroneArmSocketParms{ 42 | Arm: &kArm, // drone arm parameters 43 | Size: v3.Vec{40, 30, 30}, // body size for socket 44 | Clearance: 0.5, // clearance between arm and socket 45 | Stop: 35, // depth of arm stop 46 | } 47 | 48 | //----------------------------------------------------------------------------- 49 | 50 | func main() { 51 | 52 | arm, err := obj.DroneMotorArm(&kArm) 53 | if err != nil { 54 | log.Fatalf("error: %s", err) 55 | } 56 | render.ToSTL(sdf.ScaleUniform3D(arm, shrink), "arm.stl", render.NewMarchingCubesOctree(300)) 57 | 58 | socket, err := obj.DroneMotorArmSocket(&kSocket) 59 | if err != nil { 60 | log.Fatalf("error: %s", err) 61 | } 62 | render.ToSTL(sdf.ScaleUniform3D(socket, shrink), "socket.stl", render.NewMarchingCubesOctree(300)) 63 | 64 | } 65 | 66 | //----------------------------------------------------------------------------- 67 | -------------------------------------------------------------------------------- /examples/dust_collection/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/dust_collection/SHA1SUM: -------------------------------------------------------------------------------- 1 | 5a26014d78ea3aaae3fbd954d4beeea1977b3cd8 mvh25_mpvc.stl 2 | bea8fa61ef57965207486603da1b9e494e359317 fdd_mpvc.stl 3 | f460f403225b08cde92414e0f9628ef03effcd5c fdd_fvh25.stl 4 | -------------------------------------------------------------------------------- /examples/eurorack/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/eurorack/SHA1SUM: -------------------------------------------------------------------------------- 1 | 7882f266bf3fcc23a2ed182ea5bc714a3807e150 psu_mount.stl 2 | d564aa123ace80c138337110f8e13f9463ee21a5 pwr_mount.stl 3 | 78ecb6999434e8a2f5461746dc60b3cc1881be9e ar_panel.stl 4 | 68167fd3dd643898752fa9db919a54f7a145da14 bb_panel.stl 5 | 3fb5e0e24fbee00b087ed0cf03d16a9cd224ea31 pwr_panel.stl 6 | 4d787958166ca82c714c0ab268daf2fc4199c12a pwr_panel_routing.stl 7 | -------------------------------------------------------------------------------- /examples/extrusion/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/extrusion/SHA1SUM: -------------------------------------------------------------------------------- 1 | 6a8cc1a21d2b2d43bed7ee1ed38081d868f65be6 extrude2.stl 2 | 251f7fa0df5a1f16857c4ffb8fde55348c2b3dff extrude1.stl 3 | -------------------------------------------------------------------------------- /examples/extrusion/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Extrusions 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | v2 "github.com/deadsy/sdfx/vec/v2" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | func hex() (sdf.SDF2, error) { 23 | return sdf.Polygon2D(sdf.Nagon(6, 20)) 24 | } 25 | 26 | func extrude1() (sdf.SDF3, error) { 27 | 28 | h, err := hex() 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | // make the extrusions 34 | sLinear := sdf.Extrude3D(sdf.Offset2D(h, 8), 100) 35 | sFwd := sdf.TwistExtrude3D(sdf.Offset2D(h, 8), 100, sdf.Tau) 36 | sRev := sdf.TwistExtrude3D(sdf.Offset2D(h, 8), 100, -sdf.Tau) 37 | sCombo := sdf.Union3D(sFwd, sRev) 38 | 39 | // position them on the y-axis 40 | d := 60.0 41 | sLinear = sdf.Transform3D(sLinear, sdf.Translate3d(v3.Vec{0, -1.5 * d, 0})) 42 | sFwd = sdf.Transform3D(sFwd, sdf.Translate3d(v3.Vec{0, -0.5 * d, 0})) 43 | sRev = sdf.Transform3D(sRev, sdf.Translate3d(v3.Vec{0, 0.5 * d, 0})) 44 | sCombo = sdf.Transform3D(sCombo, sdf.Translate3d(v3.Vec{0, 1.5 * d, 0})) 45 | 46 | // return a union of them all 47 | return sdf.Union3D(sLinear, sFwd, sRev, sCombo), nil 48 | } 49 | 50 | func extrude2() (sdf.SDF3, error) { 51 | 52 | h, err := hex() 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | s0 := sdf.ScaleExtrude3D(sdf.Offset2D(h, 8), 80, v2.Vec{0.25, 0.5}) 58 | s1 := sdf.ScaleTwistExtrude3D(sdf.Offset2D(h, 8), 80, sdf.Pi, v2.Vec{0.25, 0.5}) 59 | 60 | // position them on the y-axis 61 | d := 30.0 62 | s0 = sdf.Transform3D(s0, sdf.Translate3d(v3.Vec{0, -d, 0})) 63 | s1 = sdf.Transform3D(s1, sdf.Translate3d(v3.Vec{0, d, 0})) 64 | 65 | return sdf.Union3D(s0, s1), nil 66 | } 67 | 68 | //----------------------------------------------------------------------------- 69 | 70 | func main() { 71 | ex1, err := extrude1() 72 | if err != nil { 73 | log.Fatalf("error: %s", err) 74 | } 75 | render.ToSTL(ex1, "extrude1.stl", render.NewMarchingCubesUniform(200)) 76 | 77 | ex2, err := extrude2() 78 | if err != nil { 79 | log.Fatalf("error: %s", err) 80 | } 81 | render.ToSTL(ex2, "extrude2.stl", render.NewMarchingCubesUniform(200)) 82 | } 83 | 84 | //----------------------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- /examples/fidget/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/fidget/SHA1SUM: -------------------------------------------------------------------------------- 1 | 9a5bd09c2d908028445a838beea1e10d1b57b170 cap_double_female.stl 2 | 75160f66179c8669ebad5938e981f7ba4c7db417 cap_single.stl 3 | 9e9f5b74d30f704eff968b7a861804cee2ebb219 body2.stl 4 | 08391b73c438609c5e9e8e7ffbc4279383f5320a cap_double_male.stl 5 | 3b655d9d212ff0643cd37a30f713739efe2eb133 body1.stl 6 | 535b7b8a297996ae16da4d76ed2adc9cfb353e8e washer.stl 7 | -------------------------------------------------------------------------------- /examples/finial/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/finial/SHA1SUM: -------------------------------------------------------------------------------- 1 | a7084874984ecddd96c14f47d1a82c94cd30779d f1.stl 2 | 7c6ded3a75f97d46be86e449fd8e62f6b99a4051 f2.stl 3 | -------------------------------------------------------------------------------- /examples/flask/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/flask/SHA1SUM: -------------------------------------------------------------------------------- 1 | 2ee697f28873349477261d23b0e80d6d6b10d543 flask_250.stl 2 | 76f19c5797eaf11cbb31e9a51e0f996b7dc68d8e flask_150.stl 3 | 3d5b0437f8f66101d2af4a545e42f903063b70d7 odd_side.stl 4 | 8607cb0bcaeff18dae6f0a248fd8b28a48030b7f flask_200.stl 5 | 81b4bcfca85f4a7ae0e7f5417fdc6ca83e118e50 flask_300.stl 6 | db1261c6c479f6b0a61e1bcca1ebfffbf9300329 pins.stl 7 | -------------------------------------------------------------------------------- /examples/gas_cap/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/gas_cap/SHA1SUM: -------------------------------------------------------------------------------- 1 | ed1b97a8196c354418b1a286358d9b0673f9ea74 cap.stl 2 | -------------------------------------------------------------------------------- /examples/gas_cap/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Replacement Cap for Plastic Gas/Oil Can 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | const capRadius = 56.0 / 2.0 23 | const capHeight = 28.0 24 | const capThickness = 4.0 25 | const threadPitch = 6.0 26 | const holeRadius = 0.0 // 33.0 / 2.0 27 | 28 | // var threadDiameter = 48.0 // tight 29 | const threadDiameter = 48.5 // just right 30 | // var threadDiameter = 49.0 // loose 31 | const threadRadius = threadDiameter / 2.0 32 | 33 | //----------------------------------------------------------------------------- 34 | 35 | func capOuter() (sdf.SDF3, error) { 36 | return obj.KnurledHead3D(capRadius, capHeight, capRadius*0.25) 37 | } 38 | 39 | func capInner() (sdf.SDF3, error) { 40 | tp, err := sdf.PlasticButtressThread(threadRadius, threadPitch) 41 | if err != nil { 42 | return nil, err 43 | } 44 | screw, err := sdf.Screw3D(tp, capHeight, 0, threadPitch, 1) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return sdf.Transform3D(screw, sdf.Translate3d(v3.Vec{0, 0, -capThickness})), nil 49 | } 50 | 51 | func capHole() (sdf.SDF3, error) { 52 | if holeRadius == 0 { 53 | // no hole 54 | return nil, nil 55 | } 56 | return sdf.Cylinder3D(capHeight, holeRadius, 0) 57 | } 58 | 59 | func gasCap() (sdf.SDF3, error) { 60 | // hole 61 | hole, err := capHole() 62 | if err != nil { 63 | return nil, err 64 | } 65 | // inner 66 | inner, err := capInner() 67 | if err != nil { 68 | return nil, err 69 | } 70 | inner = sdf.Union3D(inner, hole) 71 | // outer 72 | outer, err := capOuter() 73 | if err != nil { 74 | return nil, err 75 | } 76 | return sdf.Difference3D(outer, inner), nil 77 | } 78 | 79 | //----------------------------------------------------------------------------- 80 | 81 | func main() { 82 | gasCap, err := gasCap() 83 | if err != nil { 84 | log.Fatalf("error: %s", err) 85 | } 86 | render.ToSTL(gasCap, "cap.stl", render.NewMarchingCubesUniform(200)) 87 | } 88 | 89 | //----------------------------------------------------------------------------- 90 | -------------------------------------------------------------------------------- /examples/gears/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/gears/SHA1SUM: -------------------------------------------------------------------------------- 1 | 170f81e55b74b24995ac4f38e4eebba385b12020 gear.stl 2 | -------------------------------------------------------------------------------- /examples/gears/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Involute Gear and Gear Rack 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | var module = (5.0 / 8.0) / 20.0 23 | var pa = sdf.DtoR(20.0) 24 | var h = 0.15 25 | var numberTeeth = 20 26 | 27 | //----------------------------------------------------------------------------- 28 | 29 | func gear() (sdf.SDF3, error) { 30 | k := obj.InvoluteGearParms{ 31 | NumberTeeth: numberTeeth, 32 | Module: module, 33 | PressureAngle: pa, 34 | RingWidth: 0.05, 35 | Facets: 7, 36 | } 37 | gear2d, err := obj.InvoluteGear(&k) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return sdf.Extrude3D(gear2d, h), nil 42 | } 43 | 44 | //----------------------------------------------------------------------------- 45 | 46 | func rack() (sdf.SDF3, error) { 47 | k := sdf.GearRackParms{ 48 | NumberTeeth: 11, 49 | Module: module, 50 | PressureAngle: pa, 51 | BaseHeight: 0.025, 52 | } 53 | rack2d, err := sdf.GearRack2D(&k) 54 | if err != nil { 55 | return nil, err 56 | } 57 | return sdf.Extrude3D(rack2d, h), nil 58 | } 59 | 60 | //----------------------------------------------------------------------------- 61 | 62 | func main() { 63 | gear, err := gear() 64 | if err != nil { 65 | log.Fatalf("error: %s", err) 66 | } 67 | 68 | rack, err := rack() 69 | if err != nil { 70 | log.Fatalf("error: %s", err) 71 | } 72 | 73 | m := sdf.Rotate3d(v3.Vec{0, 0, 1}, sdf.DtoR(180.0/float64(numberTeeth))) 74 | m = sdf.Translate3d(v3.Vec{0, 0.39, 0}).Mul(m) 75 | gear = sdf.Transform3D(gear, m) 76 | 77 | render.ToSTL(sdf.Union3D(rack, gear), "gear.stl", render.NewMarchingCubesOctree(200)) 78 | } 79 | 80 | //----------------------------------------------------------------------------- 81 | -------------------------------------------------------------------------------- /examples/geneva/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/geneva/SHA1SUM: -------------------------------------------------------------------------------- 1 | 296e89913ae93e4a7bc016cbfe98fb3ee475b1b0 driver.stl 2 | a631a9bb063e8128ec3c16118d69816d9cae9ce3 geneva.stl 3 | 095429735fe9f68a0e84ac95938c26c2fa72ef60 driven.stl 4 | -------------------------------------------------------------------------------- /examples/geneva/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | 3D printable geneva drive mechanism 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | var k0 = obj.GenevaParms{ 23 | NumSectors: 6, 24 | CenterDistance: 50.0, 25 | DriverRadius: 20.0, 26 | DrivenRadius: 40.0, 27 | PinRadius: 2.5, 28 | Clearance: 0.1, 29 | } 30 | 31 | var k1 = obj.GenevaParms{ 32 | NumSectors: 10, 33 | CenterDistance: 45.0, 34 | DriverRadius: 12.0, 35 | DrivenRadius: 45.0, 36 | PinRadius: 2.0, 37 | Clearance: 0.1, 38 | } 39 | 40 | func main() { 41 | 42 | k := k0 43 | 44 | sDriver, sDriven, err := obj.Geneva2D(&k) 45 | if err != nil { 46 | log.Fatalf("error: %s", err) 47 | } 48 | 49 | wheelHeight := 5.0 // height of wheels 50 | holeRadius := 3.25 // radius of center hole 51 | hubRadius := 10.0 // hub radius for driven wheel 52 | baseRadius := 1.5 * k.DriverRadius // radius of base for driver wheel 53 | 54 | // extrude the driver wheel 55 | driver3d := sdf.Extrude3D(sDriver, wheelHeight) 56 | driver3d = sdf.Transform3D(driver3d, sdf.Translate3d(v3.Vec{0, 0, wheelHeight / 2})) 57 | // add a base 58 | base3d, _ := sdf.Cylinder3D(wheelHeight, baseRadius, 0) 59 | base3d = sdf.Transform3D(base3d, sdf.Translate3d(v3.Vec{0, 0, -wheelHeight / 2})) 60 | driver3d = sdf.Union3D(driver3d, base3d) 61 | // remove a center hole 62 | hole3d, _ := sdf.Cylinder3D(2*wheelHeight, holeRadius, 0) 63 | driver3d = sdf.Difference3D(driver3d, hole3d) 64 | 65 | // extrude the driven wheel 66 | driven3d := sdf.Extrude3D(sDriven, wheelHeight) 67 | driven3d = sdf.Transform3D(driven3d, sdf.Translate3d(v3.Vec{0, 0, -wheelHeight / 2})) 68 | // add a hub 69 | hub3d, _ := sdf.Cylinder3D(wheelHeight, hubRadius, 0) 70 | hub3d = sdf.Transform3D(hub3d, sdf.Translate3d(v3.Vec{0, 0, wheelHeight / 2})) 71 | driven3d = sdf.Union3D(driven3d, hub3d) 72 | // remove a center hole 73 | driven3d = sdf.Difference3D(driven3d, hole3d) 74 | 75 | const meshCells = 300 76 | render.ToSTL(driver3d, "driver.stl", render.NewMarchingCubesOctree(meshCells)) 77 | render.ToSTL(driven3d, "driven.stl", render.NewMarchingCubesOctree(meshCells)) 78 | 79 | driver3d = sdf.Transform3D(driver3d, sdf.Translate3d(v3.Vec{-0.8 * k.DrivenRadius, 0, 0})) 80 | driven3d = sdf.Transform3D(driven3d, sdf.Translate3d(v3.Vec{k.DrivenRadius, 0, 0})) 81 | render.ToSTL(sdf.Union3D(driver3d, driven3d), "geneva.stl", render.NewMarchingCubesOctree(meshCells)) 82 | } 83 | 84 | //----------------------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- /examples/gridfinity/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/gridfinity/SHA1SUM: -------------------------------------------------------------------------------- 1 | 55d9c91181b8f0fbd9a87c5b00b470ab57187e6b base_4x4.stl 2 | ba2f2d8c9950c2a5dca960ab476c24d05db2afa5 body_1x1x3.stl 3 | 26b282a6f550b7a552b75c735fb285b44a577ffc body_1x2x1.stl 4 | -------------------------------------------------------------------------------- /examples/gridfinity/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Gridfinity Storage Parts 5 | 6 | https://gridfinity.xyz/ 7 | 8 | */ 9 | //----------------------------------------------------------------------------- 10 | 11 | package main 12 | 13 | import ( 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/vec/v2i" 17 | "github.com/deadsy/sdfx/vec/v3i" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | func main() { 23 | 24 | kBase := obj.GfBaseParms{ 25 | Size: v2i.Vec{4, 4}, 26 | Magnet: true, 27 | Hole: true, 28 | } 29 | base := obj.GfBase(&kBase) 30 | render.ToSTL(base, "base_4x4.stl", render.NewMarchingCubesOctree(300)) 31 | 32 | kBody := obj.GfBodyParms{ 33 | Size: v3i.Vec{1, 1, 3}, 34 | Hole: true, 35 | Empty: true, 36 | } 37 | body := obj.GfBody(&kBody) 38 | render.ToSTL(body, "body_1x1x3.stl", render.NewMarchingCubesOctree(300)) 39 | 40 | kBody = obj.GfBodyParms{ 41 | Size: v3i.Vec{1, 2, 1}, 42 | } 43 | body = obj.GfBody(&kBody) 44 | render.ToSTL(body, "body_1x2x1.stl", render.NewMarchingCubesOctree(300)) 45 | 46 | } 47 | 48 | //----------------------------------------------------------------------------- 49 | -------------------------------------------------------------------------------- /examples/gyroid/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/gyroid/SHA1SUM: -------------------------------------------------------------------------------- 1 | 2df88d6594aec5f8258ba4157d09185234356322 gyroid_cube.stl 2 | 6b7f3136512def0513e84e1561e4c180b1c6accd gyroid_surface.stl 3 | 618df9ec6cf2fff1f0072a33f5c699be97352675 gyroid_teapot.stl 4 | -------------------------------------------------------------------------------- /examples/hole_patterns/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/hole_patterns/SHA1SUM: -------------------------------------------------------------------------------- 1 | 32ec3b01f08847b08586caef7be43091212760b9 holes.dxf 2 | -------------------------------------------------------------------------------- /examples/hole_patterns/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | hole patterns 5 | 6 | Experiments in distributing holes on a circular disk. 7 | 8 | */ 9 | //----------------------------------------------------------------------------- 10 | 11 | package main 12 | 13 | import ( 14 | "log" 15 | 16 | "github.com/deadsy/sdfx/render" 17 | "github.com/deadsy/sdfx/sdf" 18 | v2 "github.com/deadsy/sdfx/vec/v2" 19 | ) 20 | 21 | //----------------------------------------------------------------------------- 22 | 23 | func holes() (sdf.SDF2, error) { 24 | 25 | l := 1.0 26 | circleRadius := l * 0.2 27 | n := 6 28 | steps := 11 29 | 30 | // Base circle 31 | c, err := sdf.Circle2D(circleRadius) 32 | if err != nil { 33 | return nil, err 34 | } 35 | s := c 36 | 37 | dBase := 0.0 38 | 39 | for i := 1; i <= steps; i++ { 40 | k := i * n 41 | r := float64(i) * l 42 | dTheta := sdf.Tau / float64(k) 43 | c0 := sdf.Transform2D(c, sdf.Translate2d(v2.Vec{0, r})) 44 | 45 | for j := 0; j < k; j++ { 46 | c1 := sdf.Transform2D(c0, sdf.Rotate2d(dBase+float64(j)*dTheta)) 47 | s = sdf.Union2D(s, c1) 48 | } 49 | 50 | dBase += 0.5 * dTheta 51 | } 52 | 53 | return s, nil 54 | } 55 | 56 | //----------------------------------------------------------------------------- 57 | 58 | func main() { 59 | s, err := holes() 60 | if err != nil { 61 | log.Fatalf("error: %s", err) 62 | } 63 | render.ToDXF(s, "holes.dxf", render.NewMarchingSquaresQuadtree(300)) 64 | } 65 | 66 | //----------------------------------------------------------------------------- 67 | -------------------------------------------------------------------------------- /examples/holes/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/holes/SHA1SUM: -------------------------------------------------------------------------------- 1 | d2784c9846d50e7efeed831fa099659b13668a41 test_holes.stl 2 | -------------------------------------------------------------------------------- /examples/holes/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Create a test panel for hole sizes. 5 | 6 | Smallest diameter is 3 mm 7 | Increase diameter in 0.2 mm increments 8 | Largest diameter is 10.8 mm 9 | 10 | */ 11 | //----------------------------------------------------------------------------- 12 | 13 | package main 14 | 15 | import ( 16 | "log" 17 | 18 | "github.com/deadsy/sdfx/obj" 19 | "github.com/deadsy/sdfx/render" 20 | "github.com/deadsy/sdfx/sdf" 21 | v2 "github.com/deadsy/sdfx/vec/v2" 22 | ) 23 | 24 | //----------------------------------------------------------------------------- 25 | 26 | // material shrinkage 27 | var shrink = 1.0 / 0.999 // PLA ~0.1% 28 | //var shrink = 1.0/0.995; // ABS ~0.5% 29 | 30 | //----------------------------------------------------------------------------- 31 | 32 | // testHoles returns a panel with various holes for test fitting. 33 | func testHoles() (sdf.SDF3, error) { 34 | 35 | const xInc = 15 36 | const yInc = 15 37 | const rInc = 0.1 38 | 39 | const nX = 5 40 | const nY = 8 41 | 42 | xOfs := 0.0 43 | yOfs := 0.0 44 | r := 1.5 45 | 46 | s := make([]sdf.SDF2, nX*nY) 47 | i := 0 48 | 49 | for j := 0; j < nY; j++ { 50 | for k := 0; k < nX; k++ { 51 | c, _ := sdf.Circle2D(r) 52 | s[i] = sdf.Transform2D(c, sdf.Translate2d(v2.Vec{xOfs, yOfs})) 53 | i++ 54 | r += rInc 55 | xOfs += xInc 56 | } 57 | xOfs = 0.0 58 | yOfs += yInc 59 | } 60 | 61 | holes := sdf.Union2D(s...) 62 | xOfs = -float64(nX-1) * xInc * 0.5 63 | yOfs = -float64(nY-1) * yInc * 0.5 64 | holes = sdf.Transform2D(holes, sdf.Translate2d(v2.Vec{xOfs, yOfs})) 65 | 66 | // make a panel 67 | k := obj.PanelParms{ 68 | Size: v2.Vec{(nX + 1) * xInc, (nY + 1) * yInc}, 69 | CornerRadius: xInc * 0.2, 70 | } 71 | panel, err := obj.Panel2D(&k) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | return sdf.Extrude3D(sdf.Difference2D(panel, holes), 3), nil 77 | } 78 | 79 | //----------------------------------------------------------------------------- 80 | 81 | func main() { 82 | s, err := testHoles() 83 | if err != nil { 84 | log.Fatalf("error: %s", err) 85 | } 86 | render.ToSTL(sdf.ScaleUniform3D(s, shrink), "test_holes.stl", render.NewMarchingCubesOctree(300)) 87 | } 88 | 89 | //----------------------------------------------------------------------------- 90 | -------------------------------------------------------------------------------- /examples/hollowing_stl/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/hollowing_stl/SHA1SUM: -------------------------------------------------------------------------------- 1 | 9fed40da27fa4d906d7642c80780af57295296ae inside-carved-out.stl 2 | -------------------------------------------------------------------------------- /examples/hollowing_stl/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Import an existing STL. Carve its inside before hollowing it. Re-render. 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | ) 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | const wallThickness = 1.0 22 | 23 | //----------------------------------------------------------------------------- 24 | 25 | func carveinside(path string) (sdf.SDF3, error) { 26 | 27 | // create the SDF from the mesh 28 | // WARNING: It will only work on non-intersecting closed-surface(s) meshes. 29 | imported, err := obj.ImportSTL(path, 20, 3, 5) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | inside := sdf.Offset3D(imported, -wallThickness) // Pass negative value for inside. 35 | 36 | return inside, nil 37 | } 38 | 39 | //----------------------------------------------------------------------------- 40 | 41 | func main() { 42 | inside, err := carveinside("../../files/teapot.stl") 43 | if err != nil { 44 | log.Fatalf("error: %s", err) 45 | } 46 | render.ToSTL(inside, "inside-carved-out.stl", render.NewMarchingCubesUniform(300)) 47 | } 48 | 49 | //----------------------------------------------------------------------------- 50 | -------------------------------------------------------------------------------- /examples/inlet_hood/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/inlet_hood/SHA1SUM: -------------------------------------------------------------------------------- 1 | f23a71c6858b8a2532786d579a14cb7c43a73b24 hood.stl 2 | -------------------------------------------------------------------------------- /examples/inlet_hood/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Inlet Masking Hood 5 | 6 | As seen on various GDI engines: 7 | Covers an Audi/VW inlet port while walnut blasting carbon deposits. 8 | 9 | */ 10 | //----------------------------------------------------------------------------- 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/deadsy/sdfx/obj" 18 | "github.com/deadsy/sdfx/render" 19 | "github.com/deadsy/sdfx/sdf" 20 | v3 "github.com/deadsy/sdfx/vec/v3" 21 | ) 22 | 23 | //----------------------------------------------------------------------------- 24 | 25 | // material shrinkage 26 | const shrink = 1.0 / 0.999 // PLA ~0.1% 27 | //const shrink = 1.0/0.995; // ABS ~0.5% 28 | 29 | //----------------------------------------------------------------------------- 30 | 31 | var baseSize = v3.Vec{40, 60, 10} // 20 32 | var portSize = v3.Vec{30, 50, 10} // 15 33 | 34 | //----------------------------------------------------------------------------- 35 | 36 | func outerBase() (sdf.SDF3, error) { 37 | 38 | trp := &obj.TruncRectPyramidParms{ 39 | Size: baseSize, 40 | BaseAngle: sdf.DtoR(90.0 - 2.0), 41 | BaseRadius: baseSize.X * 0.5, 42 | RoundRadius: 0, 43 | } 44 | 45 | return obj.TruncRectPyramid3D(trp) 46 | } 47 | 48 | func innerBase() (sdf.SDF3, error) { 49 | 50 | trp := &obj.TruncRectPyramidParms{ 51 | Size: portSize, 52 | BaseAngle: sdf.DtoR(90.0 - 5.0), 53 | BaseRadius: portSize.X * 0.5, 54 | RoundRadius: 0, 55 | } 56 | 57 | return obj.TruncRectPyramid3D(trp) 58 | } 59 | 60 | func hood() (sdf.SDF3, error) { 61 | 62 | ob, err := outerBase() 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | ib, err := innerBase() 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | return sdf.Difference3D(ob, ib), nil 73 | } 74 | 75 | //----------------------------------------------------------------------------- 76 | 77 | func main() { 78 | s, err := hood() 79 | if err != nil { 80 | log.Fatalf("error: %s", err) 81 | } 82 | render.ToSTL(sdf.ScaleUniform3D(s, shrink), "hood.stl", render.NewMarchingCubesOctree(300)) 83 | } 84 | 85 | //----------------------------------------------------------------------------- 86 | -------------------------------------------------------------------------------- /examples/joko/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/joko/SHA1SUM: -------------------------------------------------------------------------------- 1 | 65a4b51616cc641f60a4d10ea8da28dff9173c2d part.stl 2 | -------------------------------------------------------------------------------- /examples/keycap/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/keycap/SHA1SUM: -------------------------------------------------------------------------------- 1 | ebb8225b51eb566d2521c235eaae4faf9e6f82bc round_cap.stl 2 | -------------------------------------------------------------------------------- /examples/keycap/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | KeyCaps for Cherry MX key switches 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | v3 "github.com/deadsy/sdfx/vec/v3" 17 | ) 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | // material shrinkage 22 | var shrink = 1.0 / 0.999 // PLA ~0.1% 23 | //var shrink = 1.0/0.995; // ABS ~0.5% 24 | 25 | //----------------------------------------------------------------------------- 26 | 27 | const stemX = 6.0 28 | const stemY = 5.0 29 | 30 | const crossDepth = 4.0 31 | const crossWidth = 1.0 32 | const crossX = 4.0 33 | const stemRound = 0.05 34 | 35 | // keyStem returns a keycap stem of a given length. 36 | func keyStem(length float64) (sdf.SDF3, error) { 37 | ofs := length - crossDepth 38 | s0, err := sdf.Box3D(v3.Vec{crossX, crossWidth, length}, crossX*stemRound) 39 | if err != nil { 40 | return nil, err 41 | } 42 | s1, err := sdf.Box3D(v3.Vec{crossWidth, stemY * (1.0 + 2.0*stemRound), length}, crossX*stemRound) 43 | if err != nil { 44 | return nil, err 45 | } 46 | cavity := sdf.Transform3D(sdf.Union3D(s0, s1), sdf.Translate3d(v3.Vec{0, 0, ofs})) 47 | stem, err := sdf.Box3D(v3.Vec{stemX, stemY, length}, stemX*stemRound) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return sdf.Difference3D(stem, cavity), nil 52 | } 53 | 54 | //----------------------------------------------------------------------------- 55 | 56 | const stemLength = 15.0 57 | 58 | // roundCap returns a round keycap. 59 | func roundCap(diameter, height, wall float64) (sdf.SDF3, error) { 60 | rOuter := 0.5 * diameter 61 | rInner := 0.5 * (diameter - (2.0 * wall)) 62 | 63 | outer, err := sdf.Cylinder3D(height, rOuter, 0) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | inner, err := sdf.Cylinder3D(height, rInner, 0) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | inner = sdf.Transform3D(inner, sdf.Translate3d(v3.Vec{0, 0, wall})) 74 | keycap := sdf.Difference3D(outer, inner) 75 | 76 | stem, err := keyStem(stemLength) 77 | if err != nil { 78 | return nil, err 79 | } 80 | ofs := (stemLength - height) * 0.5 81 | stem = sdf.Transform3D(stem, sdf.Translate3d(v3.Vec{0, 0, ofs})) 82 | 83 | return sdf.Union3D(keycap, stem), nil 84 | } 85 | 86 | //----------------------------------------------------------------------------- 87 | 88 | func main() { 89 | s, err := roundCap(18, 6, 1.5) 90 | if err != nil { 91 | log.Fatalf("error: %s", err) 92 | } 93 | render.ToSTL(sdf.ScaleUniform3D(s, shrink), "round_cap.stl", render.NewMarchingCubesOctree(150)) 94 | } 95 | 96 | //----------------------------------------------------------------------------- 97 | -------------------------------------------------------------------------------- /examples/loadcell/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/loadcell/SHA1SUM: -------------------------------------------------------------------------------- 1 | 916dd7c097a641db5f550ccb7b4a34b52fe15e0a holder.stl 2 | -------------------------------------------------------------------------------- /examples/maestro/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/maestro/SHA1SUM: -------------------------------------------------------------------------------- 1 | 3612ad2a8f95bd3877c51f989626165299794189 mm18.stl 2 | -------------------------------------------------------------------------------- /examples/maestro/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | PCB Mounting Board for a Pololu Mini Maestro 18 Servo Controller 5 | 6 | https://www.pololu.com/product/1354 7 | 8 | */ 9 | //----------------------------------------------------------------------------- 10 | 11 | package main 12 | 13 | import ( 14 | "log" 15 | 16 | "github.com/deadsy/sdfx/obj" 17 | "github.com/deadsy/sdfx/render" 18 | "github.com/deadsy/sdfx/sdf" 19 | v2 "github.com/deadsy/sdfx/vec/v2" 20 | v3 "github.com/deadsy/sdfx/vec/v3" 21 | ) 22 | 23 | //----------------------------------------------------------------------------- 24 | 25 | // material shrinkage 26 | const shrink = 1.0 / 0.999 // PLA ~0.1% 27 | //const shrink = 1.0/0.995; // ABS ~0.5% 28 | 29 | //----------------------------------------------------------------------------- 30 | 31 | func servoControllerMount() (sdf.SDF3, error) { 32 | 33 | // standoff 34 | k0 := obj.StandoffParms{ 35 | PillarHeight: 0.5 * sdf.MillimetresPerInch, 36 | PillarDiameter: 5, 37 | HoleDepth: 10, 38 | HoleDiameter: 2.4, // #4 screw 39 | } 40 | standoff, err := obj.Standoff3D(&k0) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | // standoffs 46 | h0 := v3.Vec{-0.45, -0.8, 0.25}.MulScalar(sdf.MillimetresPerInch) 47 | h1 := v3.Vec{0.05, 0.8, 0.25}.MulScalar(sdf.MillimetresPerInch) 48 | standoffs := sdf.Multi3D(standoff, []v3.Vec{h0, h1}) 49 | 50 | // base 51 | k1 := obj.PanelParms{ 52 | Size: v2.Vec{1.1, 1.8}.MulScalar(sdf.MillimetresPerInch), 53 | CornerRadius: 2, 54 | HoleDiameter: 2.4, // #4 screw 55 | HoleMargin: [4]float64{4, 4, 4, 4}, 56 | HolePattern: [4]string{"x", "x", ".x", ""}, 57 | Thickness: 3, 58 | } 59 | base, err := obj.Panel3D(&k1) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | return sdf.Union3D(base, standoffs), nil 65 | } 66 | 67 | //----------------------------------------------------------------------------- 68 | 69 | func main() { 70 | 71 | s, err := servoControllerMount() 72 | if err != nil { 73 | log.Fatalf("error: %s", err) 74 | } 75 | s = sdf.ScaleUniform3D(s, shrink) 76 | render.ToSTL(s, "mm18.stl", render.NewMarchingCubesOctree(300)) 77 | 78 | } 79 | 80 | //----------------------------------------------------------------------------- 81 | -------------------------------------------------------------------------------- /examples/maixgo/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/maixgo/SHA1SUM: -------------------------------------------------------------------------------- 1 | 7d9cbed1270d525c6b6d2b0ca90e4e24f39d9c97 bezel.stl 2 | -------------------------------------------------------------------------------- /examples/mcg/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/mcg/SHA1SUM: -------------------------------------------------------------------------------- 1 | 3a827b3342c857a7f216a5a68996b47b18a1c02f mcg.stl 2 | -------------------------------------------------------------------------------- /examples/mesh_test/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/mesh_test/SHA1SUM: -------------------------------------------------------------------------------- 1 | ff39b42ac5767309e2ab9d81beb6b98665c81293 test.dxf 2 | -------------------------------------------------------------------------------- /examples/midget/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/midget/SHA1SUM: -------------------------------------------------------------------------------- 1 | c5dd4356bc02b9022a27e0038234d6fc8252cd0b crankcase_front.stl 2 | 7d8d39a9ead1a1e30acf3e3917cce9e1a7d9f87e cylinder_pattern.stl 3 | -------------------------------------------------------------------------------- /examples/midget/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Midget Motor Casting Patterns 5 | Popular Mechanics 1936 6 | 7 | */ 8 | //----------------------------------------------------------------------------- 9 | 10 | package main 11 | 12 | import ( 13 | "log" 14 | 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | ) 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | // material shrinkage 22 | const shrink = 1.0 / 0.999 // PLA ~0.1% 23 | //const shrink = 1.0/0.995; // ABS ~0.5% 24 | 25 | //----------------------------------------------------------------------------- 26 | 27 | func main() { 28 | const scale = shrink * sdf.MillimetresPerInch 29 | 30 | cp, err := cylinderPattern(true, true) 31 | if err != nil { 32 | log.Fatalf("error: %s", err) 33 | } 34 | render.ToSTL(sdf.ScaleUniform3D(cp, scale), "cylinder_pattern.stl", render.NewMarchingCubesOctree(330)) 35 | 36 | ccfp, err := ccFrontPattern() 37 | if err != nil { 38 | log.Fatalf("error: %s", err) 39 | } 40 | render.ToSTL(sdf.ScaleUniform3D(ccfp, scale), "crankcase_front.stl", render.NewMarchingCubesOctree(300)) 41 | 42 | //render.ToSTL(sdf.ScaleUniform3D(cylinderCoreBox(), shrink), "cylinder_corebox.stl", render.NewMarchingCubesOctree(330)) 43 | } 44 | 45 | //----------------------------------------------------------------------------- 46 | -------------------------------------------------------------------------------- /examples/monkey_hat/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/monkey_hat/SHA1SUM: -------------------------------------------------------------------------------- 1 | f7649befff327283a12099b2f71fcb68df6c1a83 monkey-out.stl 2 | -------------------------------------------------------------------------------- /examples/monkey_hat/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Import an existing STL. Modify it. Re-render. 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | func monkeyWithHat() (sdf.SDF3, error) { 23 | 24 | // create the SDF from the mesh (a modified Suzanne from Blender with 366 faces) 25 | monkeyImported, err := obj.ImportSTL("../../files/monkey.stl", 20, 3, 5) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | // build the hat 31 | hatHeight := 0.5 32 | hat, err := sdf.Cylinder3D(hatHeight, 0.6, 0) 33 | if err != nil { 34 | return nil, err 35 | } 36 | edge, err := sdf.Cylinder3D(hatHeight*0.4, 1, 0) 37 | if err != nil { 38 | return nil, err 39 | } 40 | edge = sdf.Transform3D(edge, sdf.Translate3d(v3.Vec{Z: -hatHeight / 2})) 41 | fullHat := sdf.Union3D(hat, edge) 42 | 43 | // put the hat on the monkey 44 | fullHat = sdf.Transform3D(fullHat, sdf.Translate3d(v3.Vec{Y: 0.15, Z: 1})) 45 | monkeyHat := sdf.Union3D(monkeyImported, fullHat) 46 | 47 | // Cache the mesh full SDF3 hierarchy for faster evaluation (at the cost of initialization time and memory). 48 | // It also smooths the mesh a little using trilinear interpolation. 49 | // It is actually slower for this mesh (unless meshCells <<< renderer's meshCells), but should be faster for 50 | // more complex meshes (with more triangles) or SDF3 hierarchies that take longer to evaluate. 51 | monkeyHat = sdf.NewVoxelSDF3(monkeyHat, 64, nil) 52 | 53 | return monkeyHat, nil 54 | } 55 | 56 | //----------------------------------------------------------------------------- 57 | 58 | func main() { 59 | monkeyHat, err := monkeyWithHat() 60 | if err != nil { 61 | log.Fatalf("error: %s", err) 62 | } 63 | render.ToSTL(monkeyHat, "monkey-out.stl", render.NewMarchingCubesUniform(128)) 64 | //render.ToSTL(monkeyHat, "monkey-out.stl", render.NewMarchingCubesOctree(128)) 65 | //render.ToSTL(monkeyHat, 64, "monkey-out.stl", dc.NewDualContouringDefault()) 66 | } 67 | 68 | //----------------------------------------------------------------------------- 69 | -------------------------------------------------------------------------------- /examples/msquare/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/msquare/SHA1SUM: -------------------------------------------------------------------------------- 1 | 48fd5b9b918af4e361f98ac81c9b718a2951756a ms6_upper.stl 2 | 482b7e2692f703cc7d84cb88edd183302b49459e ms6_lower.stl 3 | ed30d6ad716af8199dd1b0b4d20151d17e383a53 ms6.stl 4 | -------------------------------------------------------------------------------- /examples/nordic/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/nordic/SHA1SUM: -------------------------------------------------------------------------------- 1 | 9d554e73113456b5678aa7f1ce374f16e4ef1c64 nrf52dk.stl 2 | 2773c2b035c4dd8e490a7373d08fa0597f921b31 nrf52833dk.stl 3 | -------------------------------------------------------------------------------- /examples/nutcover/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/nutcover/SHA1SUM: -------------------------------------------------------------------------------- 1 | ec616ec540309101ad01d025e107e937f349bbe6 cover.stl 2 | -------------------------------------------------------------------------------- /examples/nutcover/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | nut cover 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | "math" 14 | 15 | "github.com/deadsy/sdfx/obj" 16 | "github.com/deadsy/sdfx/render" 17 | "github.com/deadsy/sdfx/sdf" 18 | v3 "github.com/deadsy/sdfx/vec/v3" 19 | ) 20 | 21 | //----------------------------------------------------------------------------- 22 | 23 | const nutFlat2Flat = 19.0 // measured flat 2 flat nut size 24 | const recessHeight = 20.0 // recess within cover 25 | const wallThickness = 2.0 // wall thickness 26 | const counterBoreDiameter = 23.0 // diameter of washer counterbore 27 | const counterBoreDepth = 2.0 // depth of washer counterbore 28 | const nutFit = 1.01 // press fit on nut 29 | 30 | //----------------------------------------------------------------------------- 31 | 32 | func hexRadius(f2f float64) float64 { 33 | return f2f / (2.0 * math.Cos(sdf.DtoR(30))) 34 | } 35 | 36 | func cover() (sdf.SDF3, error) { 37 | r := (hexRadius(nutFlat2Flat) * nutFit) + wallThickness 38 | h := recessHeight + wallThickness 39 | return sdf.Cylinder3D(2*h, r, 0.1*r) 40 | } 41 | 42 | func recess() (sdf.SDF3, error) { 43 | r := hexRadius(nutFlat2Flat) * nutFit 44 | h := recessHeight 45 | return obj.HexHead3D(r, 2*h, "") 46 | } 47 | 48 | func counterbore() (sdf.SDF3, error) { 49 | r := counterBoreDiameter * 0.5 50 | h := counterBoreDepth 51 | return sdf.Cylinder3D(2*h, r, 0) 52 | } 53 | 54 | func nutcover() (sdf.SDF3, error) { 55 | cover, err := cover() 56 | if err != nil { 57 | return nil, err 58 | } 59 | recess, err := recess() 60 | if err != nil { 61 | return nil, err 62 | } 63 | counterbore, err := counterbore() 64 | if err != nil { 65 | return nil, err 66 | } 67 | cover = sdf.Difference3D(cover, sdf.Union3D(recess, counterbore)) 68 | return sdf.Cut3D(cover, v3.Vec{0, 0, 0}, v3.Vec{0, 0, 1}), nil 69 | } 70 | 71 | func main() { 72 | s, err := nutcover() 73 | if err != nil { 74 | log.Fatalf("error: %s", err) 75 | } 76 | // un-comment for a cut-away view 77 | //s = sdf.Cut3D(s, v3.Vec{0, 0, 0}, v3.Vec{1, 0, 0}) 78 | render.ToSTL(s, "cover.stl", render.NewMarchingCubesOctree(150)) 79 | } 80 | 81 | //----------------------------------------------------------------------------- 82 | -------------------------------------------------------------------------------- /examples/nutsandbolts/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/nutsandbolts/SHA1SUM: -------------------------------------------------------------------------------- 1 | 89aa1acd441b79ca7a773a96ca7d28f4deacf135 nutandbolt.stl 2 | -------------------------------------------------------------------------------- /examples/nutsandbolts/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Nuts and Bolts 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | func nutAndBolt( 23 | name string, // name of thread 24 | totalLength float64, // threaded length + shank length 25 | shankLength float64, // non threaded length 26 | ) (sdf.SDF3, error) { 27 | 28 | // bolt 29 | boltParms := obj.BoltParms{ 30 | Thread: name, 31 | Style: "hex", 32 | TotalLength: totalLength, 33 | ShankLength: shankLength, 34 | } 35 | bolt, err := obj.Bolt(&boltParms) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | // nut 41 | nutParms := obj.NutParms{ 42 | Thread: name, 43 | Style: "hex", 44 | } 45 | nut, err := obj.Nut(&nutParms) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | zOffset := totalLength * 1.5 51 | nut = sdf.Transform3D(nut, sdf.Translate3d(v3.Vec{0, 0, zOffset})) 52 | 53 | return sdf.Union3D(nut, bolt), nil 54 | } 55 | 56 | //----------------------------------------------------------------------------- 57 | 58 | func main() { 59 | 60 | xOffset := 1.5 61 | 62 | s0, err := nutAndBolt("unc_1/4", 2, 0.5) 63 | if err != nil { 64 | log.Fatalf("error: %s", err) 65 | } 66 | s0 = sdf.Transform3D(s0, sdf.Translate3d(v3.Vec{-0.6 * xOffset, 0, 0})) 67 | 68 | s1, err := nutAndBolt("unc_1/2", 2.0, 0.5) 69 | if err != nil { 70 | log.Fatalf("error: %s", err) 71 | } 72 | //s1 = sdf.Transform3D(s1, sdf.Translate3d(v3.Vec{0, 0, 0})) 73 | 74 | s2, err := nutAndBolt("unc_1", 2.0, 0.5) 75 | if err != nil { 76 | log.Fatalf("error: %s", err) 77 | } 78 | s2 = sdf.Transform3D(s2, sdf.Translate3d(v3.Vec{xOffset, 0, 0})) 79 | 80 | s := sdf.Union3D(s0, s1, s2) 81 | render.ToSTL(s, "nutandbolt.stl", render.NewMarchingCubesUniform(400)) 82 | } 83 | 84 | //----------------------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- /examples/offset_box/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/offset_box/SHA1SUM: -------------------------------------------------------------------------------- 1 | e3c7f02fb285edba70fb5a013983fddbf7500828 top.stl 2 | 8200f6f80feee18dae64c1ea0e74f5e730801a85 base.stl 3 | -------------------------------------------------------------------------------- /examples/offset_box/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Build a box using offsets from a rectangular box. 5 | 6 | TODO Add a retaining lip to the base or top so the lid stays in place. 7 | 8 | */ 9 | //----------------------------------------------------------------------------- 10 | 11 | package main 12 | 13 | import ( 14 | "log" 15 | 16 | "github.com/deadsy/sdfx/render" 17 | "github.com/deadsy/sdfx/sdf" 18 | v3 "github.com/deadsy/sdfx/vec/v3" 19 | ) 20 | 21 | //----------------------------------------------------------------------------- 22 | 23 | const sizeX = 30.0 24 | const sizeY = 40.0 25 | const sizeZ = 30.0 26 | 27 | const wallThickness = 3.0 28 | const outerRadius = 6.0 29 | const lidPosition = 0.75 // 0..1 position of lid on box 30 | 31 | //----------------------------------------------------------------------------- 32 | 33 | func box() error { 34 | 35 | if outerRadius < wallThickness { 36 | return sdf.ErrMsg("outerRadius < wallThickness") 37 | } 38 | 39 | innerOfs := outerRadius - wallThickness 40 | outerOfs := innerOfs + wallThickness 41 | 42 | if sizeX < outerOfs { 43 | return sdf.ErrMsg("sizeX < outerOfs") 44 | } 45 | if sizeY < outerOfs { 46 | return sdf.ErrMsg("sizeY < outerOfs") 47 | } 48 | if sizeZ < outerOfs { 49 | return sdf.ErrMsg("sizeZ < outerOfs") 50 | } 51 | 52 | baseBox, err := sdf.Box3D(v3.Vec{sizeX - outerOfs, sizeY - outerOfs, sizeZ - outerOfs}, 0) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | innerBox := sdf.Offset3D(baseBox, innerOfs) 58 | outerBox := sdf.Offset3D(baseBox, outerOfs) 59 | box := sdf.Difference3D(outerBox, innerBox) 60 | 61 | lidZ := (lidPosition - 0.5) * sizeZ 62 | base := sdf.Cut3D(box, v3.Vec{0, 0, lidZ}, v3.Vec{0, 0, -1}) 63 | top := sdf.Cut3D(box, v3.Vec{0, 0, lidZ}, v3.Vec{0, 0, 1}) 64 | 65 | render.ToSTL(base, "base.stl", render.NewMarchingCubesOctree(300)) 66 | render.ToSTL(top, "top.stl", render.NewMarchingCubesOctree(300)) 67 | 68 | return nil 69 | } 70 | 71 | //----------------------------------------------------------------------------- 72 | 73 | func main() { 74 | err := box() 75 | if err != nil { 76 | log.Fatalf("error: %s", err) 77 | } 78 | } 79 | 80 | //----------------------------------------------------------------------------- 81 | -------------------------------------------------------------------------------- /examples/opengate/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/opengate/SHA1SUM: -------------------------------------------------------------------------------- 1 | cc8076715f0e645b2ee127b7414d7cdefaaba92e main_board.stl 2 | -------------------------------------------------------------------------------- /examples/opengate/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | https://shop.sb-components.co.uk/products/raspberry-pi-pico-hat-expansion 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v2 "github.com/deadsy/sdfx/vec/v2" 18 | v3 "github.com/deadsy/sdfx/vec/v3" 19 | ) 20 | 21 | //----------------------------------------------------------------------------- 22 | // material shrinkage 23 | 24 | const shrink = 1.0 / 0.999 // PLA ~0.1% 25 | //const shrink = 1.0/0.995; // ABS ~0.5% 26 | 27 | //----------------------------------------------------------------------------- 28 | 29 | const baseThickness = 3 30 | const pillarHeight = 8 31 | 32 | const pcbX = 116 33 | const pcbY = 61 34 | 35 | const baseX = pcbX + 30 36 | const baseY = pcbY + 20 37 | 38 | //----------------------------------------------------------------------------- 39 | 40 | func standoffs() (sdf.SDF3, error) { 41 | 42 | // standoffs with screw holes 43 | k := &obj.StandoffParms{ 44 | PillarHeight: pillarHeight, 45 | PillarDiameter: 6.0, 46 | HoleDepth: pillarHeight, 47 | HoleDiameter: 2.4, // #4 screw 48 | } 49 | 50 | s, err := obj.Standoff3D(k) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | positions0 := v3.VecSet{ 56 | {0, 0, 0}, 57 | {pcbX, 0, 0}, 58 | {pcbX, pcbY, 0}, 59 | {0, pcbY, 0}, 60 | } 61 | s = sdf.Multi3D(s, positions0) 62 | 63 | xOfs := -0.5 * pcbX 64 | yOfs := -0.5 * pcbY 65 | zOfs := 0.5 * (pillarHeight + baseThickness) 66 | s = sdf.Transform3D(s, sdf.Translate3d(v3.Vec{xOfs, yOfs, zOfs})) 67 | 68 | return s, nil 69 | } 70 | 71 | func mainBoard() (sdf.SDF3, error) { 72 | 73 | // base 74 | base := &obj.PanelParms{ 75 | Size: v2.Vec{baseX, baseY}, 76 | CornerRadius: 5.0, 77 | HoleDiameter: 3.5, 78 | HoleMargin: [4]float64{5.0, 5.0, 5.0, 5.0}, 79 | HolePattern: [4]string{"x", "x", "x", "x"}, 80 | } 81 | s0, err := obj.Panel2D(base) 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | // cutout 87 | cutout := &obj.PanelParms{ 88 | Size: v2.Vec{baseX - 40, baseY - 40}, 89 | CornerRadius: 5.0, 90 | } 91 | s1, err := obj.Panel2D(cutout) 92 | if err != nil { 93 | return nil, err 94 | } 95 | 96 | s2 := sdf.Difference2D(s0, s1) 97 | 98 | // extrude the base 99 | s3 := sdf.Extrude3D(s2, baseThickness) 100 | 101 | // add the standoffs 102 | s4, err := standoffs() 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | s5 := sdf.Union3D(s3, s4) 108 | s5.(*sdf.UnionSDF3).SetMin(sdf.PolyMin(3.0)) 109 | 110 | return s5, nil 111 | } 112 | 113 | //----------------------------------------------------------------------------- 114 | 115 | func main() { 116 | 117 | s, err := mainBoard() 118 | if err != nil { 119 | log.Fatalf("error: %s", err) 120 | } 121 | render.ToSTL(sdf.ScaleUniform3D(s, shrink), "main_board.stl", render.NewMarchingCubesOctree(300)) 122 | 123 | } 124 | 125 | //----------------------------------------------------------------------------- 126 | -------------------------------------------------------------------------------- /examples/panel_box/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/panel_box/SHA1SUM: -------------------------------------------------------------------------------- 1 | d98110d092bb1e3a564d7e74e40b65bd2fe1af29 top.stl 2 | 535d69110164ae7b75de5d9e354cc2c1293e07fc bottom.stl 3 | a8db3ca28d49f0b8012c20aaa24b421dfb6049df panel.stl 4 | -------------------------------------------------------------------------------- /examples/panel_box/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Demonstration for Parametric Box/Case 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | func box() ([]sdf.SDF3, error) { 23 | k := obj.PanelBoxParms{ 24 | Size: v3.Vec{50.0, 40.0, 60.0}, // width, height, length 25 | Wall: 2.5, // wall thickness 26 | Panel: 3.0, // panel thickness 27 | Rounding: 5.0, // outer corner rounding 28 | FrontInset: 2.0, // inset for front panel 29 | BackInset: 2.0, // inset for pack panel 30 | Hole: 3.4, // #6 screw 31 | SideTabs: "TbtbT", // tab pattern 32 | } 33 | return obj.PanelBox3D(&k) 34 | } 35 | 36 | //----------------------------------------------------------------------------- 37 | 38 | func main() { 39 | s, err := box() 40 | if err != nil { 41 | log.Fatalf("error: %s\n", err) 42 | } 43 | render.ToSTL(s[0], "panel.stl", render.NewMarchingCubesOctree(300)) 44 | render.ToSTL(s[1], "top.stl", render.NewMarchingCubesOctree(300)) 45 | render.ToSTL(s[2], "bottom.stl", render.NewMarchingCubesOctree(300)) 46 | } 47 | 48 | //----------------------------------------------------------------------------- 49 | -------------------------------------------------------------------------------- /examples/phone/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/phone/SHA1SUM: -------------------------------------------------------------------------------- 1 | 9033aa45e4462fc013f6cc0a6dceda60e050bf4c clip.stl 2 | 3ab48d7cdc00764ca65c90fe80746449de7b96e7 holder.stl 3 | -------------------------------------------------------------------------------- /examples/pico_cnc/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/pico_cnc/SHA1SUM: -------------------------------------------------------------------------------- 1 | 033255dd88475e5820b81998863930c0f4d538e2 pen_holder.stl 2 | 4d9d7f989da039576d40b14554ee6cad5372168a serial.stl 3 | 12a5d3b6ab2644f42ae5b2b6c8f279abd601935b pico_cnc.stl 4 | b557553f1a0940c3a76178605501eaa306b52640 keypad_panel.stl 5 | -------------------------------------------------------------------------------- /examples/pico_cnc/penholder.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Pen Holder for Path Testing 5 | 6 | Inspired by: https://www.thingiverse.com/thing:2625750) 7 | 8 | */ 9 | //----------------------------------------------------------------------------- 10 | 11 | package main 12 | 13 | import ( 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/sdf" 16 | v3 "github.com/deadsy/sdfx/vec/v3" 17 | ) 18 | 19 | //----------------------------------------------------------------------------- 20 | // pen holder 21 | 22 | func penHolder() (sdf.SDF3, error) { 23 | 24 | const holderHeight = 20.0 25 | const holderWidth = 25.0 26 | const shaftRadius = 8.0 * 0.5 27 | const penRadius = 13.0 * 0.5 28 | const bossDiameter = 6.0 29 | 30 | // spring 31 | k := &obj.SpringParms{ 32 | Width: holderWidth, // width of spring 33 | Height: holderHeight, // height of spring (3d only) 34 | WallThickness: 1, // thickness of wall 35 | Diameter: 5, // diameter of spring turn 36 | NumSections: 3, // number of spring sections 37 | Boss: [2]float64{2.0 * bossDiameter, 8}, // boss sizes 38 | } 39 | spring, err := k.Spring3D() 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | // shaft hole 45 | shaft, err := sdf.Cylinder3D(k.SpringLength(), shaftRadius, 0) 46 | if err != nil { 47 | return nil, err 48 | } 49 | shaft = sdf.Transform3D(shaft, sdf.RotateY(sdf.DtoR(90))) 50 | 51 | // shaft screw boss 52 | bossParms := &obj.ThreadedCylinderParms{ 53 | Height: 0.5 * holderHeight, 54 | Diameter: bossDiameter, 55 | Thread: "unc_8_32", 56 | Tolerance: 0, 57 | } 58 | boss, err := bossParms.Object() 59 | if err != nil { 60 | return nil, err 61 | } 62 | boss = sdf.Transform3D(boss, sdf.Translate3d(v3.Vec{0, 0, 30})) 63 | 64 | return sdf.Difference3D(sdf.Union3D(spring, boss), shaft), nil 65 | } 66 | 67 | //----------------------------------------------------------------------------- 68 | -------------------------------------------------------------------------------- /examples/pillar_holder/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/pillar_holder/SHA1SUM: -------------------------------------------------------------------------------- 1 | 52e30172cc63e86759bc67b1a23ee9daf8d3b4be holder.stl 2 | -------------------------------------------------------------------------------- /examples/pillar_holder/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Pillar Holder 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "github.com/deadsy/sdfx/render" 13 | "github.com/deadsy/sdfx/sdf" 14 | v2 "github.com/deadsy/sdfx/vec/v2" 15 | v3 "github.com/deadsy/sdfx/vec/v3" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | // material shrinkage 21 | var shrink = 1.0 / 0.999 // PLA ~0.1% 22 | //var shrink = 1.0/0.995; // ABS ~0.5% 23 | 24 | //----------------------------------------------------------------------------- 25 | 26 | var wallThickness = 2.5 27 | var wallHeight = 15.0 28 | var pillarWidth = 33.0 29 | var pillarRadius = 4.0 30 | var feetWidth = 6.0 31 | var baseThickness = 3.0 32 | 33 | //----------------------------------------------------------------------------- 34 | 35 | func base() sdf.SDF3 { 36 | w := pillarWidth + 2.0*(feetWidth+wallThickness) 37 | h := pillarWidth + 2.0*wallThickness 38 | r := pillarRadius + wallThickness 39 | base2d := sdf.Box2D(v2.Vec{w, h}, r) 40 | return sdf.Extrude3D(base2d, baseThickness) 41 | } 42 | 43 | func wall(w, r float64) sdf.SDF3 { 44 | base := sdf.Box2D(v2.Vec{w, w}, r) 45 | s := sdf.Extrude3D(base, wallHeight) 46 | ofs := 0.5 * (wallHeight - baseThickness) 47 | return sdf.Transform3D(s, sdf.Translate3d(v3.Vec{0, 0, ofs})) 48 | } 49 | 50 | func holder() sdf.SDF3 { 51 | base := base() 52 | outer := wall(pillarWidth+2.0*wallThickness, pillarRadius+wallThickness) 53 | inner := wall(pillarWidth, pillarRadius) 54 | return sdf.Difference3D(sdf.Union3D(base, outer), inner) 55 | } 56 | 57 | //----------------------------------------------------------------------------- 58 | 59 | func main() { 60 | s := holder() 61 | s = sdf.ScaleUniform3D(s, shrink) 62 | render.ToSTL(s, "holder.stl", render.NewMarchingCubesOctree(300)) 63 | } 64 | 65 | //----------------------------------------------------------------------------- 66 | -------------------------------------------------------------------------------- /examples/pipe_connectors/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/pipe_connectors/SHA1SUM: -------------------------------------------------------------------------------- 1 | 95d6a38a0d672c6552cc77f40681348455b5dac6 pipe_connector_3a.stl 2 | 4ad6edcfaab468408021848841ece53669e421c9 pipe_connector_5a.stl 3 | c108916ffeb0e2e00d285a5a3f206c2ecc782392 pipe_connector_4b.stl 4 | cd1f5fa4443691a6bd7c03267f0f4475f065b6fd pipe_connector_4a.stl 5 | 33bfd9b569ca66648a191c3f711d6ff6541403f2 pipe_connector_2a.stl 6 | 18f46c177d4759d51e98070ff08bfc92faf5d052 pipe_connector_3b.stl 7 | 09db51ea4ecf3a9c2a3c3e4101068694994c5749 pipe_connector_2b.stl 8 | -------------------------------------------------------------------------------- /examples/pipe_connectors/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Pipe Connectors 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | const name = "sch40:1" 21 | const units = "mm" 22 | const length = 40.0 23 | 24 | //----------------------------------------------------------------------------- 25 | 26 | func main() { 27 | 28 | // 2-way 29 | s, err := obj.StdPipeConnector3D(name, units, length, [6]bool{false, false, false, false, true, true}) 30 | if err != nil { 31 | log.Fatalf("error: %s", err) 32 | } 33 | render.ToSTL(s, "pipe_connector_2a.stl", render.NewMarchingCubesOctree(300)) 34 | 35 | // 2-way 36 | s, err = obj.StdPipeConnector3D(name, units, length, [6]bool{true, false, false, false, true, false}) 37 | if err != nil { 38 | log.Fatalf("error: %s", err) 39 | } 40 | render.ToSTL(s, "pipe_connector_2b.stl", render.NewMarchingCubesOctree(300)) 41 | 42 | // 3-way 43 | s, err = obj.StdPipeConnector3D(name, units, length, [6]bool{true, false, false, false, true, true}) 44 | if err != nil { 45 | log.Fatalf("error: %s", err) 46 | } 47 | render.ToSTL(s, "pipe_connector_3a.stl", render.NewMarchingCubesOctree(300)) 48 | 49 | // 3-way 50 | s, err = obj.StdPipeConnector3D(name, units, length, [6]bool{true, false, true, false, true, false}) 51 | if err != nil { 52 | log.Fatalf("error: %s", err) 53 | } 54 | render.ToSTL(s, "pipe_connector_3b.stl", render.NewMarchingCubesOctree(300)) 55 | 56 | // 4-way 57 | s, err = obj.StdPipeConnector3D(name, units, length, [6]bool{true, true, true, true, false, false}) 58 | if err != nil { 59 | log.Fatalf("error: %s", err) 60 | } 61 | render.ToSTL(s, "pipe_connector_4a.stl", render.NewMarchingCubesOctree(300)) 62 | 63 | // 4-way 64 | s, err = obj.StdPipeConnector3D(name, units, length, [6]bool{true, false, true, true, true, false}) 65 | if err != nil { 66 | log.Fatalf("error: %s", err) 67 | } 68 | render.ToSTL(s, "pipe_connector_4b.stl", render.NewMarchingCubesOctree(300)) 69 | 70 | // 5-way 71 | s, err = obj.StdPipeConnector3D(name, units, length, [6]bool{true, true, true, true, true, false}) 72 | if err != nil { 73 | log.Fatalf("error: %s", err) 74 | } 75 | render.ToSTL(s, "pipe_connector_5a.stl", render.NewMarchingCubesOctree(300)) 76 | 77 | } 78 | 79 | //----------------------------------------------------------------------------- 80 | -------------------------------------------------------------------------------- /examples/pool/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/pool/SHA1SUM: -------------------------------------------------------------------------------- 1 | 7dff183b3fafdb8540d8369b20915f9349e01a94 pool1.stl 2 | -------------------------------------------------------------------------------- /examples/pool/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Pool Model 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | v2 "github.com/deadsy/sdfx/vec/v2" 17 | ) 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | const cubicInchesPerGallon = 231.0 22 | 23 | // pool dimensions are in inches 24 | const poolWidth = 234.0 25 | const poolLength = 477.0 26 | 27 | var poolDepth = []v2.Vec{ 28 | {0.0, 43.0}, 29 | {101.0, 46.0}, 30 | {202.0, 58.0}, 31 | {298.0, 83.0}, 32 | {394.0, 96.0}, 33 | {477.0, 96.0}, 34 | } 35 | 36 | const vol = (7738.3005 * 1000.0) / cubicInchesPerGallon // gallons 37 | 38 | //----------------------------------------------------------------------------- 39 | 40 | func pool() (sdf.SDF3, error) { 41 | log.Printf("pool volume %f gallons\n", vol) 42 | p := sdf.NewPolygon() 43 | p.Add(0, 0) 44 | p.AddV2Set(poolDepth) 45 | p.Add(poolLength, 0) 46 | profile, err := sdf.Polygon2D(p.Vertices()) 47 | if err != nil { 48 | return nil, err 49 | } 50 | return sdf.Extrude3D(profile, poolWidth), nil 51 | } 52 | 53 | //----------------------------------------------------------------------------- 54 | 55 | func main() { 56 | pool, err := pool() 57 | if err != nil { 58 | log.Fatalf("error: %s", err) 59 | } 60 | render.ToSTL(pool, "pool1.stl", render.NewMarchingCubesOctree(300)) 61 | //render.ToSTL(pool, 15, "pool2.stl", dc.NewDualContouringDefault()) 62 | } 63 | 64 | //----------------------------------------------------------------------------- 65 | -------------------------------------------------------------------------------- /examples/pottery_wheel/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/pottery_wheel/SHA1SUM: -------------------------------------------------------------------------------- 1 | 4980cc758dc2569b7d5bb74314e0e95a944f8cd5 wheel.stl 2 | 0568472581e3833567bda218a768e4f00f51f31c core_box.stl 3 | d78b394cdced7576787ea604a0e24a46eab8e1e2 wheel.dxf 4 | -------------------------------------------------------------------------------- /examples/ringnut_tool/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/ringnut_tool/SHA1SUM: -------------------------------------------------------------------------------- 1 | 077cfbc13946409efecb92385474631926fb3582 tool.stl 2 | -------------------------------------------------------------------------------- /examples/rpi/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/rpi/SHA1SUM: -------------------------------------------------------------------------------- 1 | bf655073bd79cd23dfbf4b15bdf3f155125b0825 display_stand.stl 2 | -------------------------------------------------------------------------------- /examples/rpi/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Raspberry Pi Parts 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/examples/rpi/stand" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | ) 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | // material shrinkage 22 | const shrink = 1.0 / 0.999 // PLA ~0.1% 23 | //const shrink = 1.0/0.995; // ABS ~0.5% 24 | 25 | //----------------------------------------------------------------------------- 26 | 27 | func main() { 28 | s, err := stand.DisplayStand() 29 | if err != nil { 30 | log.Fatalf("error: %s", err) 31 | } 32 | render.ToSTL(sdf.ScaleUniform3D(s, shrink), "display_stand.stl", render.NewMarchingCubesOctree(300)) 33 | } 34 | 35 | //----------------------------------------------------------------------------- 36 | -------------------------------------------------------------------------------- /examples/servo/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/servo/SHA1SUM: -------------------------------------------------------------------------------- 1 | ca5c01c247dd5a480714318930ef37ba0043f194 servos.stl 2 | -------------------------------------------------------------------------------- /examples/servo/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Servo Models 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | func servos() error { 23 | 24 | names := []string{ 25 | "nano", 26 | "submicro", 27 | "micro", 28 | "mini", 29 | "standard", 30 | "large", 31 | "giant", 32 | } 33 | 34 | var s sdf.SDF3 35 | yOfs := 0.0 36 | 37 | for _, n := range names { 38 | k, err := obj.ServoLookup(n) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | yOfs += 0.5*k.Body.Y + 10.0 44 | 45 | servo, err := obj.Servo3D(k) 46 | if err != nil { 47 | return err 48 | } 49 | servo = sdf.Transform3D(servo, sdf.Translate3d(v3.Vec{0, yOfs, 20})) 50 | 51 | outline2, err := obj.Servo2D(k, -1) 52 | if err != nil { 53 | return err 54 | } 55 | outline := sdf.Extrude3D(outline2, 5) 56 | outline = sdf.Transform3D(outline, sdf.Translate3d(v3.Vec{0, yOfs, 0})) 57 | 58 | s = sdf.Union3D(s, servo, outline) 59 | yOfs += 0.5 * k.Body.Y 60 | } 61 | 62 | render.ToSTL(s, "servos.stl", render.NewMarchingCubesOctree(300)) 63 | return nil 64 | } 65 | 66 | //----------------------------------------------------------------------------- 67 | 68 | func main() { 69 | err := servos() 70 | if err != nil { 71 | log.Fatalf("error: %s", err) 72 | } 73 | } 74 | 75 | //----------------------------------------------------------------------------- 76 | -------------------------------------------------------------------------------- /examples/simple_stl/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/simple_stl/SHA1SUM: -------------------------------------------------------------------------------- 1 | 1088007d1b37cdb441f2c6c33d1bca246d615772 simple.stl 2 | -------------------------------------------------------------------------------- /examples/simple_stl/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | The Simplest Manifold STL object. 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | v3 "github.com/deadsy/sdfx/vec/v3" 17 | ) 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | func main() { 22 | 23 | side := 30.0 24 | 25 | a := v3.Vec{0, 0, 0} 26 | b := v3.Vec{side, 0, 0} 27 | c := v3.Vec{0, side, 0} 28 | d := v3.Vec{0, 0, side} 29 | 30 | t1 := &sdf.Triangle3{a, b, d} 31 | t2 := &sdf.Triangle3{a, c, b} 32 | t3 := &sdf.Triangle3{a, d, c} 33 | t4 := &sdf.Triangle3{b, c, d} 34 | 35 | err := render.SaveSTL("simple.stl", []*sdf.Triangle3{t1, t2, t3, t4}) 36 | if err != nil { 37 | fmt.Printf("%s", err) 38 | } 39 | } 40 | 41 | //----------------------------------------------------------------------------- 42 | -------------------------------------------------------------------------------- /examples/spiral/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/spiral/SHA1SUM: -------------------------------------------------------------------------------- 1 | e49bc93d1dbcd097e4b2b3270600a52f1c0baec5 spiral.dxf 2 | -------------------------------------------------------------------------------- /examples/spiral/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Spirals 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | func main() { 21 | s, err := sdf.ArcSpiral2D(1.0, 20.0, 0.25*sdf.Pi, 8*sdf.Tau, 1.0) 22 | if err != nil { 23 | log.Fatalf("error: %s\n", err) 24 | } 25 | render.ToDXF(s, "spiral.dxf", render.NewMarchingSquaresQuadtree(400)) 26 | } 27 | 28 | //----------------------------------------------------------------------------- 29 | -------------------------------------------------------------------------------- /examples/sprue/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/sprue/SHA1SUM: -------------------------------------------------------------------------------- 1 | 55b3439fabeac744238d2052fef624d0fca9cd08 sprue.stl 2 | -------------------------------------------------------------------------------- /examples/sprue/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Tapered Casting Sprue 5 | 6 | Generate a model for a tapered pouring sprue. 7 | Metal accelerates as it falls through the sprue but to maintain laminar flow the 8 | vol/time at any point in the sprue must be constant. To have this the cross 9 | sectional area gets smaller as the metal falls through the sprue. 10 | 11 | In general: 12 | 13 | a = sprue cross sectional area 14 | h = sprue height 15 | 16 | a * sqrt(h) = constant 17 | 18 | */ 19 | //----------------------------------------------------------------------------- 20 | 21 | package main 22 | 23 | import ( 24 | "log" 25 | "math" 26 | 27 | "github.com/deadsy/sdfx/render" 28 | "github.com/deadsy/sdfx/sdf" 29 | ) 30 | 31 | //----------------------------------------------------------------------------- 32 | 33 | // material shrinkage 34 | const shrink = 1.0 / 0.999 // PLA ~0.1% 35 | //const shrink = 1.0/0.995; // ABS ~0.5% 36 | 37 | //----------------------------------------------------------------------------- 38 | 39 | const steps = 20 40 | 41 | func sprue(r, l, k float64) (sdf.SDF3, error) { 42 | 43 | a0 := math.Pi * r * r 44 | h0 := math.Pow(k/a0, 2) 45 | dh := l / steps 46 | 47 | p := sdf.NewPolygon() 48 | p.Add(0, 0) 49 | for h := 0.0; h <= l; h += dh { 50 | a := k / math.Sqrt(h+h0) 51 | r := math.Sqrt(a / math.Pi) 52 | p.Add(r, h) 53 | } 54 | p.Add(0, l) 55 | 56 | s, err := sdf.Polygon2D(p.Vertices()) 57 | if err != nil { 58 | return nil, err 59 | } 60 | return sdf.Revolve3D(s) 61 | } 62 | 63 | //----------------------------------------------------------------------------- 64 | 65 | func main() { 66 | s, err := sprue(20, 100, 3000) 67 | if err != nil { 68 | log.Fatalf("error: %s", err) 69 | } 70 | s = sdf.ScaleUniform3D(s, shrink) 71 | render.ToSTL(s, "sprue.stl", render.NewMarchingCubesOctree(300)) 72 | } 73 | 74 | //----------------------------------------------------------------------------- 75 | -------------------------------------------------------------------------------- /examples/square_flange/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/square_flange/SHA1SUM: -------------------------------------------------------------------------------- 1 | ddf54e3477b00ac380b961ee08de526cfe1c8333 flange.stl 2 | -------------------------------------------------------------------------------- /examples/square_flange/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Pipe Flange with a Square base 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/obj" 15 | "github.com/deadsy/sdfx/render" 16 | "github.com/deadsy/sdfx/sdf" 17 | "github.com/deadsy/sdfx/vec/conv" 18 | v2 "github.com/deadsy/sdfx/vec/v2" 19 | v3 "github.com/deadsy/sdfx/vec/v3" 20 | ) 21 | 22 | //----------------------------------------------------------------------------- 23 | 24 | // material shrinkage 25 | const shrink = 1.0 / 0.999 // PLA ~0.1% 26 | //var shrink = 1.0/0.995; // ABS ~0.5% 27 | 28 | //----------------------------------------------------------------------------- 29 | 30 | const pipeClearance = 1.01 // ID of pipe holder slightly larger to allow a slip fit 31 | const pipeDiameter = 48.45 * pipeClearance // OD of pipe to be fitted 32 | var baseSize = v2.Vec{77.0, 77.0} // size of rectangular base 33 | const baseThickness = 3.0 // base thickness 34 | const pipeWall = 3.0 // pipe holder wall thickness 35 | const pipeLength = 30.0 // length of pipe holder (from bottom) 36 | var pipeOffset = v2.Vec{0, 0} // offset of pipe holder from base center 37 | 38 | const pipeRadius = 0.5 * pipeDiameter 39 | const pipeFillet = 0.95 * pipeWall 40 | 41 | //----------------------------------------------------------------------------- 42 | 43 | func flange() (sdf.SDF3, error) { 44 | 45 | // base 46 | pp := &obj.PanelParms{ 47 | Size: baseSize, 48 | CornerRadius: 18.0, 49 | HoleDiameter: 3.5, // #6 screw 50 | HoleMargin: [4]float64{12.0, 12.0, 12.0, 12.0}, 51 | HolePattern: [4]string{"x", "x", "x", "x"}, 52 | } 53 | panel, err := obj.Panel2D(pp) 54 | if err != nil { 55 | return nil, err 56 | } 57 | base := sdf.Extrude3D(panel, 2.0*baseThickness) 58 | 59 | // outer pipe 60 | outerPipe, _ := sdf.Cylinder3D(2.0*pipeLength, pipeRadius+pipeWall, 0.0) 61 | outerPipe = sdf.Transform3D(outerPipe, sdf.Translate3d(conv.V2ToV3(pipeOffset, 0))) 62 | 63 | // inner pipe 64 | innerPipe, _ := sdf.Cylinder3D(2.0*pipeLength, pipeRadius, 0.0) 65 | innerPipe = sdf.Transform3D(innerPipe, sdf.Translate3d(conv.V2ToV3(pipeOffset, 0))) 66 | 67 | // combine the outer pipe and base (with a fillet) 68 | s0 := sdf.Union3D(base, outerPipe) 69 | s0.(*sdf.UnionSDF3).SetMin(sdf.PolyMin(pipeFillet)) 70 | 71 | // cut the through hole 72 | s := sdf.Difference3D(s0, innerPipe) 73 | 74 | // return the upper half 75 | return sdf.Cut3D(s, v3.Vec{0, 0, 0}, v3.Vec{0, 0, 1}), nil 76 | } 77 | 78 | //----------------------------------------------------------------------------- 79 | 80 | func main() { 81 | flange, err := flange() 82 | if err != nil { 83 | log.Fatalf("error: %s", err) 84 | } 85 | render.ToSTL(sdf.ScaleUniform3D(flange, shrink), "flange.stl", render.NewMarchingCubesOctree(300)) 86 | } 87 | 88 | //----------------------------------------------------------------------------- 89 | -------------------------------------------------------------------------------- /examples/tabbox/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/tabbox/SHA1SUM: -------------------------------------------------------------------------------- 1 | f874b064006446cc8eb7530dc2da078d8d1fa92c box0_upper.stl 2 | 6c16b9628d3747b3e9092aef6e3ea1a27024809b box1_lower.stl 3 | d868f85aae9ffa662f651cbf6181cd8e4cffc19d box0_lower.stl 4 | 015656ba1ed2562e2c50d1afae7db0c8c6da4679 box1_upper.stl 5 | -------------------------------------------------------------------------------- /examples/tacho_bracket/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/tacho_bracket/SHA1SUM: -------------------------------------------------------------------------------- 1 | c0887daaa070e07bce4f2bc9ad82c828bd81ca79 tacho.stl 2 | -------------------------------------------------------------------------------- /examples/tacho_bracket/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Tachometer Holding Bracket 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | v2 "github.com/deadsy/sdfx/vec/v2" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | const tachoRadius = 0.5 * 3.5 * sdf.MillimetresPerInch 23 | const bracketHeight = 15.0 24 | const bracketWidth = 10.0 25 | const tabWidth = 20.0 26 | const tabLength = 20.0 27 | const slotWidth = 4.0 28 | const screwRadius = 1.1 * 0.5 * (5.0 / 32.0) * sdf.MillimetresPerInch 29 | 30 | //----------------------------------------------------------------------------- 31 | 32 | func tachoBracket() (sdf.SDF3, error) { 33 | 34 | // outer bracket 35 | const outerRadius = tachoRadius + bracketWidth 36 | body, err := sdf.Circle2D(outerRadius) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | // inner hole 42 | hole, err := sdf.Circle2D(tachoRadius) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | // side tabs 48 | tabs := sdf.Box2D(v2.Vec{2.0 * (outerRadius + tabLength), tabWidth}, 0.07*(tabWidth+tabLength)) 49 | 50 | // slot 51 | l := bracketWidth + tabLength 52 | slot := sdf.Box2D(v2.Vec{l, slotWidth}, 0) 53 | slot = sdf.Transform2D(slot, sdf.Translate2d(v2.Vec{0.5*l + tachoRadius, 0})) 54 | 55 | // panel hole 56 | panelHole, err := sdf.Circle2D(screwRadius) 57 | if err != nil { 58 | return nil, err 59 | } 60 | const xOfs = tachoRadius + bracketWidth + 0.5*tabLength 61 | panelHole = sdf.Transform2D(panelHole, sdf.Translate2d(v2.Vec{-xOfs, 0})) 62 | 63 | // outer body 64 | s3 := sdf.Union2D(body, tabs) 65 | s3.(*sdf.UnionSDF2).SetMin(sdf.PolyMin(bracketWidth)) 66 | 67 | // remove the holes 68 | s4 := sdf.Difference2D(s3, sdf.Union2D(hole, slot, panelHole)) 69 | bracket := sdf.Extrude3D(s4, bracketHeight) 70 | 71 | // clamp hole 72 | clampHole, err := sdf.Cylinder3D(1.1*tabWidth, screwRadius, 0) 73 | clampHole = sdf.Transform3D(clampHole, sdf.RotateX(0.5*sdf.Pi)) 74 | clampHole = sdf.Transform3D(clampHole, sdf.Translate3d(v3.Vec{xOfs, 0, 0})) 75 | 76 | bracket = sdf.Difference3D(bracket, clampHole) 77 | 78 | return bracket, nil 79 | } 80 | 81 | //----------------------------------------------------------------------------- 82 | 83 | func main() { 84 | s, err := tachoBracket() 85 | if err != nil { 86 | log.Fatalf("error: %s", err) 87 | } 88 | render.ToSTL(s, "tacho.stl", render.NewMarchingCubesOctree(300)) 89 | 90 | } 91 | 92 | //----------------------------------------------------------------------------- 93 | -------------------------------------------------------------------------------- /examples/tapers/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/tapers/SHA1SUM: -------------------------------------------------------------------------------- 1 | 86e337c963e5878e7568c7c8dace8e5bf31e477e taper2.stl 2 | f36cbf39c34dd2488c214fdf572bce8e69d77fb4 taper1.stl 3 | -------------------------------------------------------------------------------- /examples/tapers/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Tapered Threads 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | func taper1() (sdf.SDF3, error) { 21 | 22 | pitch := 0.50 23 | radius := 2.0 24 | length := 5.0 25 | taper := sdf.DtoR(20) 26 | 27 | isoThread, err := sdf.ISOThread(radius, pitch, true) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | s0, _ := sdf.Screw3D(isoThread, length, taper, pitch, 7) 33 | s1, _ := sdf.Screw3D(isoThread, length, taper, pitch, -7) 34 | 35 | return sdf.Union3D(s0, s1), nil 36 | } 37 | 38 | //----------------------------------------------------------------------------- 39 | 40 | func taper2() (sdf.SDF3, error) { 41 | 42 | pitch := 0.50 43 | radius := 2.0 44 | length := 10.0 45 | taper := sdf.DtoR(3) 46 | 47 | isoThread, err := sdf.ISOThread(radius, pitch, true) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | return sdf.Screw3D(isoThread, length, taper, pitch, 1) 53 | } 54 | 55 | //----------------------------------------------------------------------------- 56 | 57 | func main() { 58 | s1, err := taper1() 59 | if err != nil { 60 | log.Fatalf("error: %s", err) 61 | } 62 | render.ToSTL(s1, "taper1.stl", render.NewMarchingCubesUniform(300)) 63 | 64 | s2, err := taper2() 65 | if err != nil { 66 | log.Fatalf("error: %s", err) 67 | } 68 | render.ToSTL(s2, "taper2.stl", render.NewMarchingCubesUniform(300)) 69 | } 70 | 71 | //----------------------------------------------------------------------------- 72 | -------------------------------------------------------------------------------- /examples/test/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/test/SHA1SUM: -------------------------------------------------------------------------------- 1 | 11162d0b9af736ee2e8fd106a37a86709b13363a ellipsoid_egg.stl 2 | 397ef43b3d047a16623c9f8def0ed5d98689b2d6 test6.stl 3 | e7657822e5acc11956d1bde0e7d131696c006742 test19.stl 4 | 690d13892bdf3953ee3a2163a898ba4494bad08a test26.stl 5 | 4f02179179e7a6db6a6e8e4d1d7533008b7d9d3a cut2d.stl 6 | cec10c8de98312f30da55028632f00435a233107 test30.stl 7 | d8c9d09a64da8b3cae851d21c90e153e64c0d537 cam1.stl 8 | ec884aec363798a0dc964196bc82d7ff490d4545 test13.stl 9 | 5c9f8a124aa62eb17b877af4999d27b0155c3053 test3.stl 10 | ebfa594bcf5e63ff85b4effbd31d14ccc69da0e6 test16.stl 11 | b288710d07d20d4f61839b4253c6bdc5f1e13a8f cam2.stl 12 | 9db2812997ec4009fa791c7284c478c40bc99f2b test2.stl 13 | 1bcf50afa2d9495946f31ae441cef6582672ffe3 driver.stl 14 | 639d8c3005ff4511b3337282ed9902a87b59134f test14.stl 15 | 87ad9a20e61afbf13c094479596a1734537c8b16 test1.stl 16 | 9c4a86bafa823d60ea7d19a107e77ac795bcd5d9 test5.stl 17 | be5fdbfd6eb216e3b664e0174216ad7d15e852a4 test9.stl 18 | 730b024c8df2519a1bff56f58e14e977dd662463 test22.stl 19 | c8730de7c2fb10c77a39ac5bb286bdb264f12311 test12.stl 20 | fa9a5be2c035d98be4678e836fc1e6a75b7eed24 test10.stl 21 | cd0f60e64f827b228c34caa70f4ddf422e874193 test29.stl 22 | 8c051b63eabb8b6f92cdec7d61a0d1a6a1873292 test4.stl 23 | 175303fe7975a092691501d805c07f3809cfbc3c rotate_copy.stl 24 | 6971de9e278943f7584abc645cb84804c04fce49 test20.stl 25 | 1196fc3a56e4f9f26c451ed21d216d6c29b1a8e8 test31.stl 26 | 47a29b2948d43486c8d0ce78cad4d490a8542ac3 rounded_box.stl 27 | 03cd9a89a0f77b30aef8af20310793c3dede7b97 test17.stl 28 | a9e78227521256d037889ca759424947d0232c87 flange.stl 29 | 9a4de589d71433afb4cb14dcd975c97a2a334fe5 washer.stl 30 | 64a8189b1fd324b23dd9252d669fe2e4ee5e5eca test11.stl 31 | c62e1ed7bca22326719549ecc7317f9c37693870 test7.stl 32 | 3fe68164f0fe09dc54208e0cc6bb9694ff6e7465 test21.stl 33 | 4ff18a7cd49ea9e6f0408676e2f580bf9c57e460 test18.stl 34 | e84b6cd56d21272eec42a8f175305391eddd9135 standard_pipe.stl 35 | 48fb28668c31ddf581bf5098635a4ff610d7be87 test15.stl 36 | 11a9d8c84a86858a181ed220db2f873f0e55eb36 screw.stl 37 | 1465a0a7b47a38de51830b3501cabd19143846a9 test28.stl 38 | d0c2d209a8b24ec3cbe18796a402de42fe1da797 loft.stl 39 | cc4aeae854f0f70d12542d3567075b3cc38810cc cam0.stl 40 | a5a82faeaf72a02285533701cf0c4584ef1aa97f test27.stl 41 | a27adffab3c8ca6136ef80fffb275530c977e046 driven.stl 42 | d1b3f78f93d7dcb90140928dd22b52c928cdd347 circle_2d.dxf 43 | -------------------------------------------------------------------------------- /examples/text/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/text/SHA1SUM: -------------------------------------------------------------------------------- 1 | ca4e4bfa168559104edb837f9e4727cb9eee42a2 shape.stl 2 | 5321bf047989f75d1de44db3751641c9b052403f shape.svg 3 | bd950896148d9b2a8efba9a67b61ba868eb7171c shape.dxf 4 | -------------------------------------------------------------------------------- /examples/text/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Text Example 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | func main() { 21 | 22 | f, err := sdf.LoadFont("../../files/cmr10.ttf") 23 | //f, err := sdf.LoadFont("Times_New_Roman.ttf") 24 | //f, err := sdf.LoadFont("wt064.ttf") 25 | 26 | if err != nil { 27 | log.Fatalf("can't read font file %s\n", err) 28 | } 29 | 30 | t := sdf.NewText("SDFX!\nHello,\nWorld!") 31 | //t := sdf.NewText("相同的不同") 32 | 33 | s0, err := sdf.Text2D(f, t, 10.0) 34 | if err != nil { 35 | log.Fatalf("can't generate text %s\n", err) 36 | } 37 | 38 | // cache the sdf for an evaluation speedup 39 | s0 = sdf.Cache2D(s0) 40 | 41 | render.ToDXF(s0, "shape.dxf", render.NewMarchingSquaresQuadtree(600)) 42 | render.ToSVG(s0, "shape.svg", render.NewMarchingSquaresQuadtree(600)) 43 | 44 | s1, err := sdf.ExtrudeRounded3D(s0, 1.0, 0.2) 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | render.ToSTL(s1, "shape.stl", render.NewMarchingCubesOctree(600)) 49 | } 50 | 51 | //----------------------------------------------------------------------------- 52 | -------------------------------------------------------------------------------- /examples/voronoi/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | include $(TOP)/mk/example.mk 3 | -------------------------------------------------------------------------------- /examples/voronoi/SHA1SUM: -------------------------------------------------------------------------------- 1 | 720f32948efef614e76a2f004f108defa1f1947b voronoi.png 2 | -------------------------------------------------------------------------------- /examples/voronoi/main.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Voronoi Diagram and Delaunay Triangulation 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package main 10 | 11 | import ( 12 | "log" 13 | 14 | "github.com/deadsy/sdfx/render" 15 | "github.com/deadsy/sdfx/sdf" 16 | v2 "github.com/deadsy/sdfx/vec/v2" 17 | "github.com/deadsy/sdfx/vec/v2i" 18 | ) 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | func main() { 23 | // create a random set of vertices 24 | b := sdf.NewBox2(v2.Vec{0, 0}, v2.Vec{20, 20}) 25 | s := b.RandomSet(20) 26 | pixels := v2i.Vec{800, 800} 27 | k := 1.5 28 | path := "voronoi.png" 29 | 30 | // use a 0 radius circle as a point 31 | point, err := sdf.Circle2D(0.0) 32 | if err != nil { 33 | log.Fatalf("error: %s", err) 34 | } 35 | 36 | // build an SDF for the points 37 | var s0 sdf.SDF2 38 | for i := range s { 39 | s0 = sdf.Union2D(s0, sdf.Transform2D(point, sdf.Translate2d(s[i]))) 40 | } 41 | 42 | // work out the region we will sample 43 | bb := s0.BoundingBox().ScaleAboutCenter(k) 44 | log.Printf("rendering %s (%dx%d)\n", path, pixels.X, pixels.Y) 45 | d, err := render.NewPNG(path, bb, pixels) 46 | if err != nil { 47 | log.Fatalf("error: %s", err) 48 | } 49 | 50 | d.RenderSDF2(s0) 51 | 52 | // create the delaunay triangulation 53 | ts, _ := render.Delaunay2d(s) 54 | // render the triangles 55 | for _, t := range ts { 56 | d.Triangle(t.ToTriangle2(s)) 57 | } 58 | 59 | d.Save() 60 | } 61 | 62 | //----------------------------------------------------------------------------- 63 | -------------------------------------------------------------------------------- /files/cmr10.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/files/cmr10.ttf -------------------------------------------------------------------------------- /files/monkey.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/files/monkey.stl -------------------------------------------------------------------------------- /files/teapot.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadsy/sdfx/030b425ef91c66976a32ce3537d9af08beeca160/files/teapot.stl -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/deadsy/sdfx 2 | 3 | go 1.22 4 | 5 | toolchain go1.23.0 6 | 7 | require ( 8 | github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b 9 | github.com/dhconnelly/rtreego v1.2.0 10 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 11 | github.com/hpinc/go3mf v0.24.2 12 | github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195 13 | github.com/stretchr/testify v1.7.0 14 | github.com/yofu/dxf v0.0.0-20240729034626-50c66fc03e0d 15 | golang.org/x/image v0.22.0 16 | gonum.org/v1/gonum v0.15.1 17 | ) 18 | 19 | require ( 20 | github.com/davecgh/go-spew v1.1.0 // indirect 21 | github.com/pmezard/go-difflib v1.0.0 // indirect 22 | github.com/qmuntal/opc v0.7.12 // indirect 23 | gopkg.in/yaml.v3 v3.0.0 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /mk/example.mk: -------------------------------------------------------------------------------- 1 | EXEC = $(shell basename $(CURDIR)) 2 | 3 | .PHONY: all 4 | all: 5 | go build 6 | 7 | .PHONY: test 8 | test: all 9 | ./$(EXEC) 10 | if [ -f SHA1SUM ]; then shasum -c SHA1SUM; fi; 11 | 12 | .PHONY: hash 13 | hash: all 14 | ./$(EXEC) 15 | $(TOP)/tools/sha1tool.py > SHA1SUM 16 | 17 | .PHONY: clean 18 | clean: 19 | go clean 20 | -rm -f *.svg 21 | -rm -f *.png 22 | -rm -f *.stl 23 | -rm -f *.dxf 24 | -rm -f *.3mf 25 | -------------------------------------------------------------------------------- /obj/angle.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Angle: Create profiles for steel/aluminum angle. 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package obj 10 | 11 | import ( 12 | "github.com/deadsy/sdfx/sdf" 13 | ) 14 | 15 | //----------------------------------------------------------------------------- 16 | 17 | // AngleLeg defines the parameters for one leg of a piece of angle. 18 | type AngleLeg struct { 19 | Length float64 20 | Thickness float64 21 | } 22 | 23 | // AngleParms defines the parameters for a piece of angle. 24 | type AngleParms struct { 25 | X, Y AngleLeg // angle legs 26 | RootRadius float64 // radius of inside fillet 27 | Length float64 // length (3d only) 28 | } 29 | 30 | // Angle2D returns a 2d angle profile. 31 | func Angle2D(k *AngleParms) (sdf.SDF2, error) { 32 | if k.X.Length <= 0 { 33 | return nil, sdf.ErrMsg("k.X.Length <= 0") 34 | } 35 | if k.X.Thickness <= 0 { 36 | return nil, sdf.ErrMsg("k.X.Thickness <= 0") 37 | } 38 | if k.Y.Length <= 0 { 39 | return nil, sdf.ErrMsg("k.Y.Length <= 0") 40 | } 41 | if k.Y.Thickness <= 0 { 42 | return nil, sdf.ErrMsg("k.Y.Thickness <= 0") 43 | } 44 | if k.Y.Thickness >= k.X.Length { 45 | return nil, sdf.ErrMsg("k.Y.Thickness >= k.X.Length") 46 | } 47 | if k.X.Thickness >= k.Y.Length { 48 | return nil, sdf.ErrMsg("k.X.Thickness >= k.Y.Length") 49 | } 50 | if k.RootRadius < 0 { 51 | return nil, sdf.ErrMsg("k.RootRadius < 0") 52 | } 53 | if k.RootRadius > (k.X.Length - k.Y.Thickness) { 54 | return nil, sdf.ErrMsg("k.RootRadius > (k.X.LengthA - k.Y.Thickness)") 55 | } 56 | if k.RootRadius > (k.Y.Length - k.X.Thickness) { 57 | return nil, sdf.ErrMsg("k.RootRadius > (k.Y.Length - k.X.Thickness)") 58 | } 59 | 60 | p := sdf.NewPolygon() 61 | p.Add(0, 0) 62 | p.Add(k.X.Length, 0) 63 | p.Add(k.X.Length, k.X.Thickness) 64 | p.Add(k.Y.Thickness, k.X.Thickness).Smooth(k.RootRadius, 6) 65 | p.Add(k.Y.Thickness, k.Y.Length) 66 | p.Add(0, k.Y.Length) 67 | 68 | return sdf.Polygon2D(p.Vertices()) 69 | } 70 | 71 | // Angle3D returns a piece of 3d angle. 72 | func Angle3D(k *AngleParms) (sdf.SDF3, error) { 73 | if k.Length <= 0 { 74 | return nil, sdf.ErrMsg("k.Length <= 0") 75 | } 76 | s, err := Angle2D(k) 77 | if err != nil { 78 | return nil, err 79 | } 80 | return sdf.Extrude3D(s, k.Length), nil 81 | } 82 | 83 | //----------------------------------------------------------------------------- 84 | -------------------------------------------------------------------------------- /obj/bolt.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Bolt: Simple Bolts for 3d printing. 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package obj 10 | 11 | import ( 12 | "fmt" 13 | 14 | "github.com/deadsy/sdfx/sdf" 15 | v3 "github.com/deadsy/sdfx/vec/v3" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | // BoltParms defines the parameters for a bolt. 21 | type BoltParms struct { 22 | Thread string // name of thread 23 | Style string // head style "hex" or "knurl" 24 | Tolerance float64 // subtract from external thread radius 25 | TotalLength float64 // threaded length + shank length 26 | ShankLength float64 // non threaded length 27 | } 28 | 29 | // Bolt returns a simple bolt suitable for 3d printing. 30 | func Bolt(k *BoltParms) (sdf.SDF3, error) { 31 | // validate parameters 32 | t, err := sdf.ThreadLookup(k.Thread) 33 | if err != nil { 34 | return nil, err 35 | } 36 | if k.TotalLength < 0 { 37 | return nil, sdf.ErrMsg("TotalLength < 0") 38 | } 39 | if k.ShankLength < 0 { 40 | return nil, sdf.ErrMsg("ShankLength < 0") 41 | } 42 | if k.Tolerance < 0 { 43 | return nil, sdf.ErrMsg("Tolerance < 0") 44 | } 45 | 46 | // head 47 | var head sdf.SDF3 48 | hr := t.HexRadius() 49 | hh := t.HexHeight() 50 | switch k.Style { 51 | case "hex": 52 | head, err = HexHead3D(hr, hh, "b") 53 | case "knurl": 54 | head, err = KnurledHead3D(hr, hh, hr*0.25) 55 | default: 56 | return nil, sdf.ErrMsg(fmt.Sprintf("unknown style \"%s\"", k.Style)) 57 | } 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | // shank 63 | shankLength := k.ShankLength + hh/2 64 | shankOffset := shankLength / 2 65 | shank, err := sdf.Cylinder3D(shankLength, t.Radius, hh*0.08) 66 | if err != nil { 67 | return nil, err 68 | } 69 | shank = sdf.Transform3D(shank, sdf.Translate3d(v3.Vec{0, 0, shankOffset})) 70 | 71 | // external thread 72 | threadLength := k.TotalLength - k.ShankLength 73 | if threadLength < 0 { 74 | threadLength = 0 75 | } 76 | var thread sdf.SDF3 77 | if threadLength != 0 { 78 | r := t.Radius - k.Tolerance 79 | threadOffset := threadLength/2 + shankLength 80 | isoThread, err := sdf.ISOThread(r, t.Pitch, true) 81 | if err != nil { 82 | return nil, err 83 | } 84 | thread, err = sdf.Screw3D(isoThread, threadLength, t.Taper, t.Pitch, 1) 85 | if err != nil { 86 | return nil, err 87 | } 88 | // chamfer the thread 89 | thread, err = ChamferedCylinder(thread, 0, 0.5) 90 | if err != nil { 91 | return nil, err 92 | } 93 | thread = sdf.Transform3D(thread, sdf.Translate3d(v3.Vec{0, 0, threadOffset})) 94 | } 95 | 96 | return sdf.Union3D(head, shank, thread), nil 97 | } 98 | 99 | //----------------------------------------------------------------------------- 100 | -------------------------------------------------------------------------------- /obj/chamfer.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Chamfered Cylinder 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package obj 10 | 11 | import "github.com/deadsy/sdfx/sdf" 12 | 13 | //----------------------------------------------------------------------------- 14 | 15 | // ChamferedCylinder intersects a chamfered cylinder with an SDF3. 16 | func ChamferedCylinder(s sdf.SDF3, kb, kt float64) (sdf.SDF3, error) { 17 | // get the length and radius from the bounding box 18 | l := s.BoundingBox().Max.Z 19 | r := s.BoundingBox().Max.X 20 | p := sdf.NewPolygon() 21 | p.Add(0, -l) 22 | p.Add(r, -l).Chamfer(r * kb) 23 | p.Add(r, l).Chamfer(r * kt) 24 | p.Add(0, l) 25 | s0, err := sdf.Polygon2D(p.Vertices()) 26 | if err != nil { 27 | return nil, err 28 | } 29 | cc, err := sdf.Revolve3D(s0) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return sdf.Intersect3D(s, cc), nil 34 | } 35 | 36 | //----------------------------------------------------------------------------- 37 | -------------------------------------------------------------------------------- /obj/finger.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | 2D Finger Button 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package obj 10 | 11 | import ( 12 | "github.com/deadsy/sdfx/sdf" 13 | v2 "github.com/deadsy/sdfx/vec/v2" 14 | ) 15 | 16 | //----------------------------------------------------------------------------- 17 | 18 | // FingerButtonParms defines the parameters for a 2D finger button. 19 | type FingerButtonParms struct { 20 | Width float64 // finger width 21 | Gap float64 // gap between finger and body 22 | Length float64 // length of the finger 23 | } 24 | 25 | // FingerButton2D returns a 2D cutout for a finger button. 26 | func FingerButton2D(k *FingerButtonParms) (sdf.SDF2, error) { 27 | r0 := 0.5 * k.Width 28 | r1 := r0 - k.Gap 29 | l := 2.0 * k.Length 30 | s := sdf.Difference2D(sdf.Line2D(l, r0), sdf.Line2D(l, r1)) 31 | s = sdf.Cut2D(s, v2.Vec{0, 0}, v2.Vec{0, 1}) 32 | return sdf.Transform2D(s, sdf.Translate2d(v2.Vec{-k.Length, 0})), nil 33 | } 34 | 35 | //----------------------------------------------------------------------------- 36 | -------------------------------------------------------------------------------- /obj/hex.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Hex Heads for nuts and bolts. 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package obj 10 | 11 | import ( 12 | "math" 13 | 14 | "github.com/deadsy/sdfx/sdf" 15 | v3 "github.com/deadsy/sdfx/vec/v3" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | // Hex2D returns a 2d hexagon with rounded corners. 21 | func Hex2D(radius, round float64) (sdf.SDF2, error) { 22 | delta := 2 * round / math.Sqrt(3) 23 | hex, err := sdf.Polygon2D(sdf.Nagon(6, radius-delta)) 24 | if err != nil { 25 | return nil, err 26 | } 27 | return sdf.Offset2D(hex, round), nil 28 | } 29 | 30 | // Hex3D returns a 3d hexagon with rounded corners. 31 | func Hex3D(radius, height, round float64) (sdf.SDF3, error) { 32 | hex, err := Hex2D(radius, round) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return sdf.Extrude3D(hex, height), nil 37 | } 38 | 39 | //----------------------------------------------------------------------------- 40 | 41 | // HexHead3D returns the rounded hex head for a nut or bolt. 42 | func HexHead3D( 43 | radius float64, // radius of hex head 44 | height float64, // height of hex head 45 | round string, // rounding control (t)top, (b)bottom, (tb)top/bottom 46 | ) (sdf.SDF3, error) { 47 | // basic hex body 48 | hex3d, err := Hex3D(radius, height, radius*0.08) 49 | if err != nil { 50 | return nil, err 51 | } 52 | // round out the top and/or bottom as required 53 | if round != "" { 54 | topRound := radius * 1.6 55 | d := radius * math.Cos(sdf.DtoR(30)) 56 | sphere3d, err := sdf.Sphere3D(topRound) 57 | if err != nil { 58 | return nil, err 59 | } 60 | zOfs := math.Sqrt(topRound*topRound-d*d) - height/2 61 | if round == "t" || round == "tb" { 62 | hex3d = sdf.Intersect3D(hex3d, sdf.Transform3D(sphere3d, sdf.Translate3d(v3.Vec{0, 0, -zOfs}))) 63 | } 64 | if round == "b" || round == "tb" { 65 | hex3d = sdf.Intersect3D(hex3d, sdf.Transform3D(sphere3d, sdf.Translate3d(v3.Vec{0, 0, zOfs}))) 66 | } 67 | } 68 | return hex3d, nil 69 | } 70 | 71 | //----------------------------------------------------------------------------- 72 | -------------------------------------------------------------------------------- /obj/hole.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Holes 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package obj 10 | 11 | import ( 12 | "github.com/deadsy/sdfx/sdf" 13 | v2 "github.com/deadsy/sdfx/vec/v2" 14 | v3 "github.com/deadsy/sdfx/vec/v3" 15 | ) 16 | 17 | //----------------------------------------------------------------------------- 18 | 19 | // CounterBoredHole3D returns the SDF3 for a counterbored hole. 20 | func CounterBoredHole3D( 21 | l float64, // total length (includes counterbore) 22 | r float64, // hole radius 23 | cbRadius float64, // counter bore radius 24 | cbDepth float64, // counter bore depth 25 | ) (sdf.SDF3, error) { 26 | s0, err := sdf.Cylinder3D(l, r, 0) 27 | if err != nil { 28 | return nil, err 29 | } 30 | s1, err := sdf.Cylinder3D(cbDepth, cbRadius, 0) 31 | if err != nil { 32 | return nil, err 33 | } 34 | s1 = sdf.Transform3D(s1, sdf.Translate3d(v3.Vec{0, 0, (l - cbDepth) * 0.5})) 35 | return sdf.Union3D(s0, s1), nil 36 | } 37 | 38 | // ChamferedHole3D returns the SDF3 for a chamfered hole (45 degrees). 39 | func ChamferedHole3D( 40 | l float64, // total length (includes chamfer) 41 | r float64, // hole radius 42 | chRadius float64, // chamfer radius 43 | ) (sdf.SDF3, error) { 44 | s0, err := sdf.Cylinder3D(l, r, 0) 45 | if err != nil { 46 | return nil, err 47 | } 48 | s1, err := sdf.Cone3D(chRadius, r, r+chRadius, 0) 49 | if err != nil { 50 | return nil, err 51 | } 52 | s1 = sdf.Transform3D(s1, sdf.Translate3d(v3.Vec{0, 0, (l - chRadius) * 0.5})) 53 | return sdf.Union3D(s0, s1), nil 54 | } 55 | 56 | // CounterSunkHole3D returns the SDF3 for a countersunk hole (45 degrees). 57 | func CounterSunkHole3D( 58 | l float64, // total length 59 | r float64, // hole radius 60 | ) (sdf.SDF3, error) { 61 | return ChamferedHole3D(l, r, r) 62 | } 63 | 64 | //----------------------------------------------------------------------------- 65 | 66 | // BoltCircle2D returns a 2D profile for a flange bolt circle. 67 | func BoltCircle2D( 68 | holeRadius float64, // radius of bolt holes 69 | circleRadius float64, // radius of bolt circle 70 | numHoles int, // number of bolts 71 | ) (sdf.SDF2, error) { 72 | s, err := sdf.Circle2D(holeRadius) 73 | if err != nil { 74 | return nil, err 75 | } 76 | s = sdf.Transform2D(s, sdf.Translate2d(v2.Vec{circleRadius, 0})) 77 | s = sdf.RotateCopy2D(s, numHoles) 78 | return s, nil 79 | } 80 | 81 | // BoltCircle3D returns a 3D object for a flange bolt circle. 82 | func BoltCircle3D( 83 | holeDepth float64, // depth of bolt holes 84 | holeRadius float64, // radius of bolt holes 85 | circleRadius float64, // radius of bolt circle 86 | numHoles int, // number of bolts 87 | ) (sdf.SDF3, error) { 88 | s, err := BoltCircle2D(holeRadius, circleRadius, numHoles) 89 | if err != nil { 90 | return nil, err 91 | } 92 | return sdf.Extrude3D(s, holeDepth), nil 93 | } 94 | 95 | //----------------------------------------------------------------------------- 96 | -------------------------------------------------------------------------------- /obj/keyway.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Keyways in Shafts 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package obj 10 | 11 | import ( 12 | "github.com/deadsy/sdfx/sdf" 13 | v2 "github.com/deadsy/sdfx/vec/v2" 14 | ) 15 | 16 | //----------------------------------------------------------------------------- 17 | 18 | // KeywayParameters defines the parameters for a keyway and shaft. 19 | type KeywayParameters struct { 20 | ShaftRadius float64 // shaft radius 21 | KeyRadius float64 // shaft center to bottom/top of key 22 | KeyWidth float64 // key width 23 | ShaftLength float64 // shaft length (3d only) 24 | } 25 | 26 | // Keyway2D returns the 2d profile of a shaft and keyway. 27 | func Keyway2D(k *KeywayParameters) (sdf.SDF2, error) { 28 | if k.ShaftRadius <= 0 { 29 | return nil, sdf.ErrMsg("k.ShaftRadius <= 0") 30 | } 31 | if k.KeyRadius < 0 { 32 | return nil, sdf.ErrMsg("k.KeyRadius < 0") 33 | } 34 | if k.KeyWidth < 0 { 35 | return nil, sdf.ErrMsg("k.KeyWidth < 0") 36 | } 37 | shaft, err := sdf.Circle2D(k.ShaftRadius) 38 | if err != nil { 39 | return nil, err 40 | } 41 | var s sdf.SDF2 42 | if k.KeyRadius < k.ShaftRadius { 43 | // The key is cut into the shaft (shaft profile) 44 | l := k.ShaftRadius - k.KeyRadius 45 | key := sdf.Box2D(v2.Vec{l, k.KeyWidth}, 0) 46 | key = sdf.Transform2D(key, sdf.Translate2d(v2.Vec{k.ShaftRadius - l*0.5, 0})) 47 | s = sdf.Difference2D(shaft, key) 48 | } else { 49 | // The key is proud of the shaft (bore profile) 50 | key := sdf.Box2D(v2.Vec{k.KeyRadius, k.KeyWidth}, 0) 51 | key = sdf.Transform2D(key, sdf.Translate2d(v2.Vec{k.KeyRadius * 0.5, 0})) 52 | s = sdf.Union2D(shaft, key) 53 | } 54 | return s, nil 55 | } 56 | 57 | // Keyway3D returns a shaft and keyway. 58 | func Keyway3D(k *KeywayParameters) (sdf.SDF3, error) { 59 | if k.ShaftLength <= 0 { 60 | return nil, sdf.ErrMsg("k.ShaftLength <= 0") 61 | } 62 | s, err := Keyway2D(k) 63 | if err != nil { 64 | return nil, err 65 | } 66 | return sdf.Extrude3D(s, k.ShaftLength), nil 67 | } 68 | 69 | //----------------------------------------------------------------------------- 70 | -------------------------------------------------------------------------------- /obj/knurl.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Knurled Cylinders 5 | 6 | See: https://en.wikipedia.org/wiki/Knurling 7 | 8 | This code builds a knurl with the intersection of left and right hand 9 | multistart screw "threads". 10 | 11 | */ 12 | //----------------------------------------------------------------------------- 13 | 14 | package obj 15 | 16 | import ( 17 | "math" 18 | 19 | "github.com/deadsy/sdfx/sdf" 20 | ) 21 | 22 | //----------------------------------------------------------------------------- 23 | 24 | // KnurlParms specifies the knurl parameters. 25 | type KnurlParms struct { 26 | Length float64 // length of cylinder 27 | Radius float64 // radius of cylinder 28 | Pitch float64 // knurl pitch 29 | Height float64 // knurl height 30 | Theta float64 // knurl helix angle 31 | } 32 | 33 | // knurlProfile returns a 2D knurl profile. 34 | func knurlProfile(k *KnurlParms) (sdf.SDF2, error) { 35 | knurl := sdf.NewPolygon() 36 | knurl.Add(k.Pitch/2, 0) 37 | knurl.Add(k.Pitch/2, k.Radius) 38 | knurl.Add(0, k.Radius+k.Height) 39 | knurl.Add(-k.Pitch/2, k.Radius) 40 | knurl.Add(-k.Pitch/2, 0) 41 | //knurl.Render("knurl.dxf") 42 | return sdf.Polygon2D(knurl.Vertices()) 43 | } 44 | 45 | // Knurl3D returns a knurled cylinder. 46 | func Knurl3D(k *KnurlParms) (sdf.SDF3, error) { 47 | if k.Length <= 0 { 48 | return nil, sdf.ErrMsg("Length <= 0") 49 | } 50 | if k.Radius <= 0 { 51 | return nil, sdf.ErrMsg("Radius <= 0") 52 | } 53 | if k.Pitch <= 0 { 54 | return nil, sdf.ErrMsg("Pitch <= 0") 55 | } 56 | if k.Height <= 0 { 57 | return nil, sdf.ErrMsg("Height <= 0") 58 | } 59 | if k.Theta < 0 { 60 | return nil, sdf.ErrMsg("Theta < 0") 61 | } 62 | if k.Theta >= sdf.DtoR(90) { 63 | return nil, sdf.ErrMsg("Theta >= 90") 64 | } 65 | // Work out the number of starts using the desired helix angle. 66 | n := int(sdf.Tau * k.Radius * math.Tan(k.Theta) / k.Pitch) 67 | // build the knurl profile. 68 | knurl2d, err := knurlProfile(k) 69 | if err != nil { 70 | return nil, err 71 | } 72 | // create the left/right hand spirals 73 | knurl0_3d, err := sdf.Screw3D(knurl2d, k.Length, 0, k.Pitch, n) 74 | if err != nil { 75 | return nil, err 76 | } 77 | knurl1_3d, err := sdf.Screw3D(knurl2d, k.Length, 0, k.Pitch, -n) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return sdf.Intersect3D(knurl0_3d, knurl1_3d), nil 82 | } 83 | 84 | // KnurledHead3D returns a generic cylindrical knurled head. 85 | func KnurledHead3D( 86 | r float64, // radius 87 | h float64, // height 88 | pitch float64, // knurl pitch 89 | ) (sdf.SDF3, error) { 90 | cylinderRound := r * 0.05 91 | knurlLength := pitch * math.Floor((h-cylinderRound)/pitch) 92 | k := KnurlParms{ 93 | Length: knurlLength, 94 | Radius: r, 95 | Pitch: pitch, 96 | Height: pitch * 0.3, 97 | Theta: sdf.DtoR(45), 98 | } 99 | knurl, err := Knurl3D(&k) 100 | if err != nil { 101 | return nil, err 102 | } 103 | cylinder, err := sdf.Cylinder3D(h, r, cylinderRound) 104 | if err != nil { 105 | return nil, err 106 | } 107 | return sdf.Union3D(cylinder, knurl), nil 108 | } 109 | 110 | //----------------------------------------------------------------------------- 111 | -------------------------------------------------------------------------------- /obj/nut.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Nuts: Simple Nut for 3d printing. 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package obj 10 | 11 | import ( 12 | "fmt" 13 | 14 | "github.com/deadsy/sdfx/sdf" 15 | ) 16 | 17 | //----------------------------------------------------------------------------- 18 | 19 | type ThreadedCylinderParms struct { 20 | Height float64 // height of cylinder 21 | Diameter float64 // diameter of cylinder 22 | Thread string // name of thread 23 | Tolerance float64 // add to internal thread radius 24 | } 25 | 26 | // Object returns a cylinder with an internal thread. 27 | func (k *ThreadedCylinderParms) Object() (sdf.SDF3, error) { 28 | // validate parameters 29 | t, err := sdf.ThreadLookup(k.Thread) 30 | if err != nil { 31 | return nil, err 32 | } 33 | if k.Diameter < 0 { 34 | return nil, sdf.ErrMsg("Diameter < 0") 35 | } 36 | if k.Height < 0 { 37 | return nil, sdf.ErrMsg("Height < 0") 38 | } 39 | if k.Tolerance < 0 { 40 | return nil, sdf.ErrMsg("Tolerance < 0") 41 | } 42 | body, err := sdf.Cylinder3D(k.Height, 0.5*k.Diameter, 0) 43 | if err != nil { 44 | return nil, err 45 | } 46 | // internal thread 47 | t = t.ToMillimetre() 48 | isoThread, err := sdf.ISOThread(t.Radius+k.Tolerance, t.Pitch, false) 49 | if err != nil { 50 | return nil, err 51 | } 52 | thread, err := sdf.Screw3D(isoThread, k.Height, t.Taper, t.Pitch, 1) 53 | if err != nil { 54 | return nil, err 55 | } 56 | return sdf.Difference3D(body, thread), nil 57 | } 58 | 59 | //----------------------------------------------------------------------------- 60 | 61 | // NutParms defines the parameters for a nut. 62 | type NutParms struct { 63 | Thread string // name of thread 64 | Style string // head style "hex" or "knurl" 65 | Tolerance float64 // add to internal thread radius 66 | } 67 | 68 | // Nut returns a simple nut suitable for 3d printing. 69 | func Nut(k *NutParms) (sdf.SDF3, error) { 70 | // validate parameters 71 | t, err := sdf.ThreadLookup(k.Thread) 72 | if err != nil { 73 | return nil, err 74 | } 75 | if k.Tolerance < 0 { 76 | return nil, sdf.ErrMsg("Tolerance < 0") 77 | } 78 | 79 | // nut body 80 | var nut sdf.SDF3 81 | nr := t.HexRadius() 82 | nh := t.HexHeight() 83 | switch k.Style { 84 | case "hex": 85 | nut, err = HexHead3D(nr, nh, "tb") 86 | case "knurl": 87 | nut, err = KnurledHead3D(nr, nh, nr*0.25) 88 | default: 89 | return nil, sdf.ErrMsg(fmt.Sprintf("unknown style \"%s\"", k.Style)) 90 | } 91 | if err != nil { 92 | return nil, err 93 | } 94 | 95 | // internal thread 96 | isoThread, err := sdf.ISOThread(t.Radius+k.Tolerance, t.Pitch, false) 97 | if err != nil { 98 | return nil, err 99 | } 100 | thread, err := sdf.Screw3D(isoThread, nh, t.Taper, t.Pitch, 1) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | return sdf.Difference3D(nut, thread), nil 106 | } 107 | 108 | //----------------------------------------------------------------------------- 109 | -------------------------------------------------------------------------------- /obj/standoff.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | PCB Standoffs, Mounting Pillars 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package obj 10 | 11 | import ( 12 | "math" 13 | 14 | "github.com/deadsy/sdfx/sdf" 15 | v3 "github.com/deadsy/sdfx/vec/v3" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | // StandoffParms defines the parameters for a board standoff pillar. 21 | type StandoffParms struct { 22 | PillarHeight float64 23 | PillarDiameter float64 24 | HoleDepth float64 // > 0 is a hole, < 0 is a support stub 25 | HoleDiameter float64 26 | NumberWebs int // number of triangular gussets around the standoff base 27 | WebHeight float64 28 | WebDiameter float64 29 | WebWidth float64 30 | } 31 | 32 | // pillarWeb returns a single pillar web 33 | func pillarWeb(k *StandoffParms) (sdf.SDF3, error) { 34 | w := sdf.NewPolygon() 35 | w.Add(0, 0) 36 | w.Add(0.5*k.WebDiameter, 0) 37 | w.Add(0, k.WebHeight) 38 | p, err := sdf.Polygon2D(w.Vertices()) 39 | if err != nil { 40 | return nil, err 41 | } 42 | s := sdf.Extrude3D(p, k.WebWidth) 43 | m := sdf.Translate3d(v3.Vec{0, 0, -0.5 * k.PillarHeight}).Mul(sdf.RotateX(sdf.DtoR(90.0))) 44 | return sdf.Transform3D(s, m), nil 45 | } 46 | 47 | // pillarWebs returns a set of pillar webs 48 | func pillarWebs(k *StandoffParms) (sdf.SDF3, error) { 49 | if k.NumberWebs == 0 { 50 | // no webs 51 | return nil, nil 52 | } 53 | web, err := pillarWeb(k) 54 | if err != nil { 55 | return nil, err 56 | } 57 | return sdf.RotateCopy3D(web, k.NumberWebs), nil 58 | } 59 | 60 | // pillar returns a cylindrical pillar 61 | func pillar(k *StandoffParms) (sdf.SDF3, error) { 62 | return sdf.Cylinder3D(k.PillarHeight, 0.5*k.PillarDiameter, 0) 63 | } 64 | 65 | // pillarHole returns a pillar screw hole (or support stub) 66 | func pillarHole(k *StandoffParms) (sdf.SDF3, error) { 67 | if k.HoleDiameter == 0.0 || k.HoleDepth == 0.0 { 68 | // no hole 69 | return nil, nil 70 | } 71 | s, err := sdf.Cylinder3D(math.Abs(k.HoleDepth), 0.5*k.HoleDiameter, 0) 72 | if err != nil { 73 | return nil, err 74 | } 75 | zOfs := 0.5 * (k.PillarHeight - k.HoleDepth) 76 | return sdf.Transform3D(s, sdf.Translate3d(v3.Vec{0, 0, zOfs})), nil 77 | } 78 | 79 | // Standoff3D returns a single board standoff. 80 | func Standoff3D(k *StandoffParms) (sdf.SDF3, error) { 81 | pillar, err := pillar(k) 82 | if err != nil { 83 | return nil, err 84 | } 85 | webs, err := pillarWebs(k) 86 | if err != nil { 87 | return nil, err 88 | } 89 | s := sdf.Union3D(pillar, webs) 90 | if k.NumberWebs != 0 { 91 | // Cut off any part of the webs that protrude from the top of the pillar 92 | cut, err := sdf.Cylinder3D(k.PillarHeight, k.WebDiameter, 0) 93 | if err != nil { 94 | return nil, err 95 | } 96 | s = sdf.Intersect3D(s, cut) 97 | } 98 | // Add the pillar hole/stub 99 | hole, err := pillarHole(k) 100 | if err != nil { 101 | return nil, err 102 | } 103 | if k.HoleDepth >= 0.0 { 104 | s = sdf.Difference3D(s, hole) 105 | } else { 106 | // support stub 107 | s = sdf.Union3D(s, hole) 108 | } 109 | return s, nil 110 | } 111 | 112 | //----------------------------------------------------------------------------- 113 | -------------------------------------------------------------------------------- /obj/trp.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Truncated Rectangular Pyramid 5 | 6 | This a rectangular base pyramid that has rounded edges and has been truncated. 7 | 8 | It's an attractive object in its own right, but it's particularly useful for 9 | sand-casting patterns because the slope implements a pattern draft and the 10 | rounded edges minimise sand crumbling. 11 | 12 | */ 13 | //----------------------------------------------------------------------------- 14 | 15 | package obj 16 | 17 | import ( 18 | "math" 19 | 20 | "github.com/deadsy/sdfx/sdf" 21 | v3 "github.com/deadsy/sdfx/vec/v3" 22 | ) 23 | 24 | //----------------------------------------------------------------------------- 25 | 26 | // TruncRectPyramidParms defines the parameters for a truncated rectangular pyramid. 27 | type TruncRectPyramidParms struct { 28 | Size v3.Vec // size of truncated pyramid 29 | BaseAngle float64 // base angle of pyramid (radians) 30 | BaseRadius float64 // base corner radius 31 | RoundRadius float64 // edge rounding radius 32 | } 33 | 34 | // TruncRectPyramid3D returns a truncated rectangular pyramid with rounded edges. 35 | func TruncRectPyramid3D(k *TruncRectPyramidParms) (sdf.SDF3, error) { 36 | if k.Size.LTZero() { 37 | return nil, sdf.ErrMsg("Size < 0") 38 | } 39 | if k.BaseAngle <= 0 || k.BaseAngle > sdf.DtoR(90) { 40 | return nil, sdf.ErrMsg("BaseAngle must be (0,90] degrees") 41 | } 42 | if k.BaseRadius < 0 { 43 | return nil, sdf.ErrMsg("BaseRadius < 0") 44 | } 45 | if k.RoundRadius < 0 { 46 | return nil, sdf.ErrMsg("RoundRadius < 0") 47 | } 48 | h := k.Size.Z 49 | dr := h / math.Tan(k.BaseAngle) 50 | rb := k.BaseRadius + dr 51 | rt := math.Max(k.BaseRadius-dr, 0) 52 | round := math.Min(0.5*rt, k.RoundRadius) 53 | s, err := sdf.Cone3D(2.0*h, rb, rt, round) 54 | if err != nil { 55 | return nil, err 56 | } 57 | wx := math.Max(k.Size.X-2.0*k.BaseRadius, 0) 58 | wy := math.Max(k.Size.Y-2.0*k.BaseRadius, 0) 59 | s = sdf.Elongate3D(s, v3.Vec{wx, wy, 0}) 60 | s = sdf.Cut3D(s, v3.Vec{0, 0, 0}, v3.Vec{0, 0, 1}) 61 | return s, nil 62 | } 63 | 64 | //----------------------------------------------------------------------------- 65 | -------------------------------------------------------------------------------- /obj/washer.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Simple Washer. 5 | 6 | The washer can be partial and used to create circular wall segments. 7 | 8 | */ 9 | //----------------------------------------------------------------------------- 10 | 11 | package obj 12 | 13 | import ( 14 | "github.com/deadsy/sdfx/sdf" 15 | v2 "github.com/deadsy/sdfx/vec/v2" 16 | ) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | // WasherParms defines the parameters for a washer. 21 | type WasherParms struct { 22 | Thickness float64 // thickness (3d only) 23 | InnerRadius float64 // inner radius 24 | OuterRadius float64 // outer radius 25 | Remove float64 // fraction of complete washer removed 26 | } 27 | 28 | //----------------------------------------------------------------------------- 29 | 30 | // Washer2D returns a 2d washer. 31 | func Washer2D(k *WasherParms) (sdf.SDF2, error) { 32 | if k.InnerRadius >= k.OuterRadius { 33 | return nil, sdf.ErrMsg("InnerRadius >= OuterRadius") 34 | } 35 | if k.Remove != 0 { 36 | return nil, sdf.ErrMsg("TODO support Remove != 0") 37 | } 38 | outer, err := sdf.Circle2D(k.OuterRadius) 39 | if err != nil { 40 | return nil, err 41 | } 42 | inner, err := sdf.Circle2D(k.InnerRadius) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return sdf.Difference2D(outer, inner), nil 47 | } 48 | 49 | //----------------------------------------------------------------------------- 50 | 51 | // Washer3D returns a 3d washer. 52 | // This can also be used to create circular walls. 53 | func Washer3D(k *WasherParms) (sdf.SDF3, error) { 54 | if k.Thickness <= 0 { 55 | return nil, sdf.ErrMsg("Thickness <= 0") 56 | } 57 | if k.InnerRadius >= k.OuterRadius { 58 | return nil, sdf.ErrMsg("InnerRadius >= OuterRadius") 59 | } 60 | if k.Remove < 0 || k.Remove >= 1.0 { 61 | return nil, sdf.ErrMsg("Remove must be [0..1)") 62 | } 63 | 64 | if k.Remove == 0 { 65 | // difference of cylinders 66 | outer, err := sdf.Cylinder3D(k.Thickness, k.OuterRadius, 0) 67 | if err != nil { 68 | return nil, err 69 | } 70 | inner, err := sdf.Cylinder3D(k.Thickness, k.InnerRadius, 0) 71 | if err != nil { 72 | return nil, err 73 | } 74 | return sdf.Difference3D(outer, inner), nil 75 | } 76 | 77 | // build a 2d profile box 78 | dx := k.OuterRadius - k.InnerRadius 79 | dy := k.Thickness 80 | xofs := 0.5 * (k.InnerRadius + k.OuterRadius) 81 | b := sdf.Box2D(v2.Vec{dx, dy}, 0) 82 | b = sdf.Transform2D(b, sdf.Translate2d(v2.Vec{xofs, 0})) 83 | // rotate about the z-axis 84 | theta := sdf.Tau * (1.0 - k.Remove) 85 | s, err := sdf.RevolveTheta3D(b, theta) 86 | if err != nil { 87 | return nil, err 88 | } 89 | // center the removed portion on the x-axis 90 | dtheta := 0.5 * (sdf.Tau - theta) 91 | return sdf.Transform3D(s, sdf.RotateZ(dtheta)), nil 92 | } 93 | 94 | //----------------------------------------------------------------------------- 95 | -------------------------------------------------------------------------------- /render/3mf.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Output a 3D triangle mesh to a 3MF file. 5 | 6 | https://3mf.io/specification/ 7 | 8 | Notes: 9 | 10 | 3D manufacturing files (3mf) generally contain meta data about the 3d object. 11 | The files produced by this code are very basic. They are the equivalent of an 12 | STL in 3MF format. That is: just the triangle mesh with a default 1mm unit. 13 | 14 | File sizes for 3MF are around 7x smaller than an STL with the same mesh. 15 | 16 | 3MF files are not identical from run to run. 3MF files are a zipped archive. 17 | The contents of the archive *are* the same but the containing zip file differs. 18 | 19 | */ 20 | //----------------------------------------------------------------------------- 21 | 22 | package render 23 | 24 | import ( 25 | "fmt" 26 | "sync" 27 | 28 | "github.com/deadsy/sdfx/sdf" 29 | v3 "github.com/deadsy/sdfx/vec/v3" 30 | "github.com/hpinc/go3mf" 31 | ) 32 | 33 | //----------------------------------------------------------------------------- 34 | 35 | // toPoint3D converts a 3D float vector to a go3mf 3D vector. 36 | func toPoint3D(a v3.Vec) go3mf.Point3D { 37 | return go3mf.Point3D{float32(a.X), float32(a.Y), float32(a.Z)} 38 | } 39 | 40 | //----------------------------------------------------------------------------- 41 | 42 | // write3MF writes a stream of triangles to a 3MF file. 43 | func write3MF(wg *sync.WaitGroup, path string) (chan<- []*sdf.Triangle3, error) { 44 | 45 | f, err := go3mf.CreateWriter(path) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | // External code writes triangles to this channel. 51 | // This goroutine reads the channel and writes triangles to the file. 52 | c := make(chan []*sdf.Triangle3) 53 | 54 | var model go3mf.Model 55 | var mesh go3mf.Mesh 56 | 57 | // add the mesh to the model 58 | obj := &go3mf.Object{Mesh: &mesh} 59 | obj.ID = model.Resources.UnusedID() 60 | model.Resources.Objects = append(model.Resources.Objects, obj) 61 | model.Build.Items = append(model.Build.Items, &go3mf.Item{ObjectID: obj.ID}) 62 | 63 | // use the mesh builder to de-dup the vertices 64 | mb := go3mf.NewMeshBuilder(&mesh) 65 | 66 | wg.Add(1) 67 | go func() { 68 | defer wg.Done() 69 | defer f.Close() 70 | // read triangles from the channel and add them to the model 71 | for ts := range c { 72 | for _, t := range ts { 73 | v1 := mb.AddVertex(toPoint3D(t[0])) 74 | v2 := mb.AddVertex(toPoint3D(t[1])) 75 | v3 := mb.AddVertex(toPoint3D(t[2])) 76 | mesh.Triangles.Triangle = append(mesh.Triangles.Triangle, go3mf.Triangle{V1: v1, V2: v2, V3: v3}) 77 | } 78 | } 79 | // encode and write out the file 80 | if err := f.Encode(&model); err != nil { 81 | fmt.Printf("%s\n", err) 82 | return 83 | } 84 | }() 85 | 86 | return c, nil 87 | } 88 | 89 | //----------------------------------------------------------------------------- 90 | -------------------------------------------------------------------------------- /render/dc/leastsquares.go: -------------------------------------------------------------------------------- 1 | package dc 2 | 3 | import ( 4 | "log" 5 | "math" 6 | 7 | v3 "github.com/deadsy/sdfx/vec/v3" 8 | ) 9 | 10 | func (dc *DualContouringV2) determinant(a, b, c, d, e, f, g, h, i float64) float64 { 11 | return a*e*i + b*f*g + c*d*h - a*f*h - b*d*i - c*e*g 12 | } 13 | 14 | /* dcSolve3x3 Solves for x in A*x = b. 'A' contains the matrix row-wise. 'b' and 'x' are column vectors. Uses cramer's rule. */ 15 | func (dc *DualContouringV2) solve3x3(A []v3.Vec, b []float64) v3.Vec { 16 | det := dc.determinant( 17 | A[0].X, A[0].Y, A[0].Z, 18 | A[1].X, A[1].Y, A[1].Z, 19 | A[2].X, A[2].Y, A[2].Z) 20 | if math.Abs(det) <= 1e-12 { 21 | if !dc.qefFailedImplWarned { 22 | log.Println("[DualContouringV1] WARNING: Oh-oh - small determinant:", det) 23 | dc.qefFailedImplWarned = true 24 | } 25 | return v3.Vec{X: math.Inf(1)} 26 | } 27 | return v3.Vec{ 28 | X: dc.determinant( 29 | b[0], A[0].Y, A[0].Z, 30 | b[1], A[1].Y, A[1].Z, 31 | b[2], A[2].Y, A[2].Z), 32 | Y: dc.determinant( 33 | A[0].X, b[0], A[0].Z, 34 | A[1].X, b[1], A[1].Z, 35 | A[2].X, b[2], A[2].Z), 36 | Z: dc.determinant( 37 | A[0].X, A[0].Y, b[0], 38 | A[1].X, A[1].Y, b[1], 39 | A[2].X, A[2].Y, b[2]), 40 | }.DivScalar(det) 41 | } 42 | 43 | func (dc *DualContouringV2) leastSquares(A []v3.Vec, b []float64) v3.Vec { 44 | // assert len(A) == len(b) 45 | if len(A) == 3 { 46 | return dc.solve3x3(A, b) 47 | } 48 | AtA := [3]v3.Vec{} 49 | Atb := [3]float64{} 50 | for i := 0; i < 3; i++ { 51 | for j := 0; j < 3; j++ { 52 | sum := 0. 53 | for k := 0; k < len(A); k++ { 54 | sum += A[k].Get(i) * A[k].Get(j) 55 | } 56 | AtA[i].Set(j, sum) 57 | } 58 | } 59 | for i := 0; i < 3; i++ { 60 | sum := 0. 61 | for k := 0; k < len(A); k++ { 62 | sum += A[k].Get(i) * b[k] 63 | } 64 | Atb[i] = sum 65 | } 66 | return dc.solve3x3(AtA[:], Atb[:]) 67 | } 68 | -------------------------------------------------------------------------------- /render/dc/utils.go: -------------------------------------------------------------------------------- 1 | package dc 2 | 3 | import "github.com/deadsy/sdfx/sdf" 4 | 5 | //----------------------------------------------------------------------------- 6 | // UTILITIES/MISC 7 | //----------------------------------------------------------------------------- 8 | 9 | func dcFlip(t *sdf.Triangle3) *sdf.Triangle3 { 10 | t[1], t[2] = t[2], t[1] 11 | return t 12 | } 13 | 14 | func dcMaxI(i int, i2 int) int { 15 | if i >= i2 { 16 | return i 17 | } 18 | return i2 19 | } 20 | -------------------------------------------------------------------------------- /render/stl_test.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | STL File Load/Save Testing 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package render 10 | 11 | import ( 12 | "testing" 13 | ) 14 | 15 | //----------------------------------------------------------------------------- 16 | 17 | func Test_LoadSTL(t *testing.T) { 18 | loadTests := []struct { 19 | path string 20 | meshSize int 21 | }{ 22 | {"../files/bottle.stl", 1240}, 23 | {"../files/monkey.stl", 366}, 24 | {"../files/teapot.stl", 9438}, 25 | } 26 | for _, test := range loadTests { 27 | mesh, err := LoadSTL(test.path) 28 | if err != nil { 29 | t.Errorf("%s", err) 30 | } 31 | if len(mesh) != test.meshSize { 32 | t.Errorf("%s expected %d triangles (got %d)", test.path, test.meshSize, len(mesh)) 33 | } 34 | } 35 | } 36 | 37 | //----------------------------------------------------------------------------- 38 | -------------------------------------------------------------------------------- /render/svg.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Output a 2D line set to an SVG file. 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package render 10 | 11 | import ( 12 | "fmt" 13 | "os" 14 | "sync" 15 | 16 | svg "github.com/ajstarks/svgo/float" 17 | "github.com/deadsy/sdfx/sdf" 18 | v2 "github.com/deadsy/sdfx/vec/v2" 19 | ) 20 | 21 | //----------------------------------------------------------------------------- 22 | 23 | // SVG represents an SVG renderer. 24 | type SVG struct { 25 | filename string 26 | lineStyle string 27 | p0s, p1s []v2.Vec 28 | min, max v2.Vec 29 | } 30 | 31 | // NewSVG returns an SVG renderer. 32 | func NewSVG(filename, lineStyle string) *SVG { 33 | return &SVG{ 34 | filename: filename, 35 | lineStyle: lineStyle, 36 | } 37 | } 38 | 39 | // Line outputs a line to the SVG file. 40 | func (s *SVG) Line(p0, p1 v2.Vec) { 41 | if len(s.p0s) == 0 { 42 | s.min = p0.Min(p1) 43 | s.max = p0.Max(p1) 44 | } else { 45 | s.min = s.min.Min(p0) 46 | s.min = s.min.Min(p1) 47 | s.max = s.max.Max(p0) 48 | s.max = s.max.Max(p1) 49 | } 50 | s.p0s = append(s.p0s, p0) 51 | s.p1s = append(s.p1s, p1) 52 | } 53 | 54 | // Save closes the SVG file. 55 | func (s *SVG) Save() error { 56 | f, err := os.Create(s.filename) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | width := s.max.X - s.min.X 62 | height := s.max.Y - s.min.Y 63 | canvas := svg.New(f) 64 | canvas.Start(width, height) 65 | for i, p0 := range s.p0s { 66 | p1 := s.p1s[i] 67 | canvas.Line(p0.X-s.min.X, s.max.Y-p0.Y, p1.X-s.min.X, s.max.Y-p1.Y, s.lineStyle) 68 | } 69 | canvas.End() 70 | return f.Close() 71 | } 72 | 73 | //----------------------------------------------------------------------------- 74 | 75 | // SaveSVG writes line segments to an SVG file. 76 | func SaveSVG(path, lineStyle string, mesh []*sdf.Line2) error { 77 | s := NewSVG(path, lineStyle) 78 | for _, v := range mesh { 79 | s.Line(v[0], v[1]) 80 | } 81 | if err := s.Save(); err != nil { 82 | return err 83 | } 84 | return nil 85 | } 86 | 87 | //----------------------------------------------------------------------------- 88 | 89 | // writeSVG writes a stream of line segments to an SVG file. 90 | func writeSVG(wg *sync.WaitGroup, path, lineStyle string) (chan<- []*sdf.Line2, error) { 91 | 92 | s := NewSVG(path, lineStyle) 93 | 94 | // External code writes line segments to this channel. 95 | // This goroutine reads the channel and writes line segments to the file. 96 | c := make(chan []*sdf.Line2) 97 | 98 | wg.Add(1) 99 | go func() { 100 | defer wg.Done() 101 | for ls := range c { 102 | for _, l := range ls { 103 | s.Line(l[0], l[1]) 104 | } 105 | } 106 | 107 | if err := s.Save(); err != nil { 108 | fmt.Printf("%s\n", err) 109 | return 110 | } 111 | }() 112 | 113 | return c, nil 114 | } 115 | 116 | //----------------------------------------------------------------------------- 117 | -------------------------------------------------------------------------------- /render/utils.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | 3 | //----------------------------------------------------------------------------- 4 | 5 | package render 6 | 7 | //----------------------------------------------------------------------------- 8 | 9 | const tolerance = 1e-9 10 | const epsilon = 1e-12 11 | 12 | //----------------------------------------------------------------------------- 13 | 14 | // nextCombination generates the next k-length combination of 0 to n-1. (returns false when done). 15 | func nextCombination(n int, a []int) bool { 16 | k := len(a) 17 | m := 0 18 | i := 0 19 | for { 20 | i++ 21 | if i > k { 22 | return false 23 | } 24 | if a[k-i] < n-i { 25 | m = a[k-i] 26 | for j := i; j >= 1; j-- { 27 | m++ 28 | a[k-j] = m 29 | } 30 | return true 31 | } 32 | } 33 | } 34 | 35 | // mapCombinations applies a function f to each k-length combination from 0 to n-1. 36 | func mapCombinations(n, k int, f func([]int)) { 37 | if k >= 0 && n >= k { 38 | a := make([]int, k) 39 | for i := range a { 40 | a[i] = i 41 | } 42 | for { 43 | f(a) 44 | if nextCombination(n, a) == false { 45 | break 46 | } 47 | } 48 | } 49 | } 50 | 51 | //----------------------------------------------------------------------------- 52 | -------------------------------------------------------------------------------- /sdf/benchmark.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Report benchmarking results for evaluations on SDF2/SDF3 objects. 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package sdf 10 | 11 | import ( 12 | "fmt" 13 | "time" 14 | ) 15 | 16 | //----------------------------------------------------------------------------- 17 | 18 | const nEvals = 1000000 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | // fmtEPS returns a string with a formatted evaluations per second. 23 | func fmtEPS(eps float64) string { 24 | if eps > 1000000000.0 { 25 | return fmt.Sprintf("%.2f G evals/sec", eps/1000000000.0) 26 | } else if eps > 1000000.0 { 27 | return fmt.Sprintf("%.2f M evals/sec", eps/1000000.0) 28 | } else if eps > 1000.0 { 29 | return fmt.Sprintf("%.2f K evals/sec", eps/1000.0) 30 | } 31 | return fmt.Sprintf("%.2f evals/sec", eps) 32 | } 33 | 34 | //----------------------------------------------------------------------------- 35 | 36 | // BenchmarkSDF2 reports the evaluation speed for an SDF2. 37 | func BenchmarkSDF2(description string, s SDF2) { 38 | // sample over a region larger than the bounding box 39 | box := NewBox2(s.BoundingBox().Center(), s.BoundingBox().Size().MulScalar(1.2)) 40 | points := box.RandomSet(nEvals) 41 | 42 | start := time.Now() 43 | for _, p := range points { 44 | s.Evaluate(p) 45 | } 46 | elapsed := time.Since(start) 47 | 48 | eps := float64(nEvals) * float64(time.Second) / float64(elapsed) 49 | fmt.Printf("%s %s\n", description, fmtEPS(eps)) 50 | } 51 | 52 | //----------------------------------------------------------------------------- 53 | 54 | // BenchmarkSDF3 reports the evaluation speed for an SDF3. 55 | func BenchmarkSDF3(description string, s SDF3) { 56 | // sample over a region larger than the bounding box 57 | box := NewBox3(s.BoundingBox().Center(), s.BoundingBox().Size().MulScalar(1.2)) 58 | points := box.RandomSet(nEvals) 59 | 60 | start := time.Now() 61 | for _, p := range points { 62 | s.Evaluate(p) 63 | } 64 | elapsed := time.Since(start) 65 | 66 | eps := float64(nEvals) * float64(time.Second) / float64(elapsed) 67 | fmt.Printf("%s %s\n", description, fmtEPS(eps)) 68 | } 69 | 70 | //----------------------------------------------------------------------------- 71 | -------------------------------------------------------------------------------- /sdf/cache2.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | 2D Evaluation Cache 5 | 6 | In some cases (E.g. extrusion) 2d SDFs get evaluated repeatedly at the same points. 7 | If a map lookup is cheaper than a distance evaluation it's possible to save time 8 | by caching evaluation results. This SDF2 wraps an underlying SDF2 and caches the 9 | evaluations. 10 | 11 | */ 12 | //----------------------------------------------------------------------------- 13 | 14 | package sdf 15 | 16 | import ( 17 | "fmt" 18 | 19 | v2 "github.com/deadsy/sdfx/vec/v2" 20 | ) 21 | 22 | //----------------------------------------------------------------------------- 23 | 24 | // CacheSDF2 is an SDF2 cache. 25 | type CacheSDF2 struct { 26 | sdf SDF2 27 | cache map[v2.Vec]float64 28 | reads, hits uint 29 | } 30 | 31 | // Cache2D wraps the passed SDF2 with an evaluation cache. 32 | func Cache2D(sdf SDF2) SDF2 { 33 | return &CacheSDF2{ 34 | sdf: sdf, 35 | cache: make(map[v2.Vec]float64), 36 | } 37 | } 38 | 39 | func (s *CacheSDF2) String() string { 40 | r := float64(s.hits) / float64(s.reads) 41 | return fmt.Sprintf("reads %d hits %d (%.2f)", s.reads, s.hits, r) 42 | } 43 | 44 | // Evaluate returns the minimum distance to a cached 2d sdf. 45 | func (s *CacheSDF2) Evaluate(p v2.Vec) float64 { 46 | s.reads++ 47 | if d, ok := s.cache[p]; ok { 48 | s.hits++ 49 | return d 50 | } 51 | d := s.sdf.Evaluate(p) 52 | s.cache[p] = d 53 | return d 54 | } 55 | 56 | // BoundingBox returns the bounding box of a cached 2d sdf. 57 | func (s *CacheSDF2) BoundingBox() Box2 { 58 | return s.sdf.BoundingBox() 59 | } 60 | 61 | //----------------------------------------------------------------------------- 62 | -------------------------------------------------------------------------------- /sdf/flange.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Flanges 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package sdf 10 | 11 | import ( 12 | "math" 13 | 14 | v2 "github.com/deadsy/sdfx/vec/v2" 15 | ) 16 | 17 | //----------------------------------------------------------------------------- 18 | 19 | // Flange1 is a flange shape made from a center circle with two side circles. 20 | type Flange1 struct { 21 | distance float64 // distance from center to side 22 | centerRadius float64 // radius of center circle 23 | sideRadius float64 // radius of side circle 24 | a v2.Vec // center point on flank line 25 | u v2.Vec // normalised line vector for flank 26 | l float64 // length of flank line 27 | bb Box2 // bounding box 28 | } 29 | 30 | // NewFlange1 returns a flange shape made from a center circle with two side circles. 31 | func NewFlange1( 32 | distance float64, // distance from center to side circle 33 | centerRadius float64, // radius of center circle 34 | sideRadius float64, // radius of side circle 35 | ) SDF2 { 36 | s := Flange1{} 37 | s.distance = distance 38 | s.centerRadius = centerRadius 39 | s.sideRadius = sideRadius 40 | // work out the flank line 41 | sin := (centerRadius - sideRadius) / distance 42 | cos := math.Sqrt(1 - sin*sin) 43 | // first point on line 44 | s.a = v2.Vec{sin, cos}.MulScalar(centerRadius) 45 | // second point on line 46 | b := v2.Vec{sin, cos}.MulScalar(sideRadius).Add(v2.Vec{distance, 0}) 47 | // line information 48 | u := b.Sub(s.a) 49 | s.u = u.Normalize() 50 | s.l = u.Length() 51 | // work out the bounding box 52 | w := distance + sideRadius 53 | h := centerRadius 54 | s.bb = Box2{v2.Vec{-w, -h}, v2.Vec{w, h}} 55 | return &s 56 | } 57 | 58 | // Evaluate returns the minimum distance to the flange. 59 | func (s *Flange1) Evaluate(p v2.Vec) float64 { 60 | // We are symmetrical about the x and y axis. 61 | // So- only consider the 1st quadrant. 62 | p = p.Abs() 63 | // vector to first point of flank line 64 | v := p.Sub(s.a) 65 | // work out the t-parameter of the projection onto the flank line 66 | t := v.Dot(s.u) 67 | var d float64 68 | if t < 0 { 69 | // the nearest point is on the center circle 70 | d = p.Length() - s.centerRadius 71 | } else if t <= s.l { 72 | // the nearest point is on the flank line 73 | d = v.Dot(v2.Vec{-s.u.Y, s.u.X}) 74 | } else { 75 | // the nearest point is on the side circle 76 | d = p.Sub(v2.Vec{s.distance, 0}).Length() - s.sideRadius 77 | } 78 | return d 79 | } 80 | 81 | // BoundingBox returns the bounding box for the flange. 82 | func (s *Flange1) BoundingBox() Box2 { 83 | return s.bb 84 | } 85 | 86 | //----------------------------------------------------------------------------- 87 | -------------------------------------------------------------------------------- /sdf/gyroid.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Gyroids 5 | 6 | https://en.wikipedia.org/wiki/Gyroid 7 | 8 | */ 9 | //----------------------------------------------------------------------------- 10 | 11 | package sdf 12 | 13 | import v3 "github.com/deadsy/sdfx/vec/v3" 14 | 15 | //----------------------------------------------------------------------------- 16 | 17 | // GyroidSDF3 is a 3d gyroid. 18 | type GyroidSDF3 struct { 19 | k v3.Vec // scaling factor 20 | } 21 | 22 | // Gyroid3D returns a 3d gyroid. 23 | func Gyroid3D(scale v3.Vec) (SDF3, error) { 24 | return &GyroidSDF3{ 25 | k: v3.Vec{Tau / scale.X, Tau / scale.Y, Tau / scale.Z}, 26 | }, nil 27 | } 28 | 29 | // Evaluate returns the minimum distance to a 3d gyroid. 30 | func (s *GyroidSDF3) Evaluate(p v3.Vec) float64 { 31 | p = p.Mul(s.k) 32 | return p.Sin().Dot(v3.Vec{p.Y, p.Z, p.X}.Cos()) 33 | } 34 | 35 | // BoundingBox returns the bounding box for a 3d gyroid. 36 | func (s *GyroidSDF3) BoundingBox() Box3 { 37 | // The surface is defined for all xyz, so the bounding box is a point at the origin. 38 | // To use the surface it needs to be intersected an external bounding volume. 39 | return Box3{} 40 | } 41 | 42 | //----------------------------------------------------------------------------- 43 | -------------------------------------------------------------------------------- /sdf/mesh3_test.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Mesh 3D Testing and Benchmarking 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package sdf 10 | 11 | import ( 12 | "testing" 13 | 14 | v3 "github.com/deadsy/sdfx/vec/v3" 15 | ) 16 | 17 | //----------------------------------------------------------------------------- 18 | 19 | func Test_Mesh3_minDistance2(t *testing.T) { 20 | 21 | testSet := []struct { 22 | t Triangle3 23 | p []v3.Vec 24 | d2 []float64 25 | }{ 26 | { 27 | Triangle3{{1, 2, 1}, {-4, -5, 1}, {17, -3, 1}}, 28 | []v3.Vec{{1, 2, 3}, {-4, -5, 6}, {17, -3, -2}}, 29 | []float64{4, 25, 9}, 30 | }, 31 | { 32 | Triangle3{{10, 0, 10}, {0, 0, -10}, {-10, 0, 10}}, 33 | []v3.Vec{{0, 4, 0}, {0, 0, 0}, {11, 4, 11}, {0, 3, -11}, {-11, 7, 11}}, 34 | []float64{16, 0, 18, 10, 51}, 35 | }, 36 | { 37 | Triangle3{{0, 0, 4}, {0, 6, -2}, {0, -6, -2}}, 38 | []v3.Vec{{0, 0, 5}, {0, 2, 2}, {4, 3, 3}, {3, -3, 3}, {-2, 0, -3}}, 39 | []float64{1, 0, 18, 11, 5}, 40 | }, 41 | } 42 | 43 | for i, test := range testSet { 44 | if len(test.p) != len(test.d2) { 45 | t.Errorf("test %d: len(p) != len(d2)", i) 46 | } 47 | triangle := test.t 48 | for j := 0; j < 3; j++ { 49 | triangle = triangle.rotateVertex() 50 | ti := newTriangleInfo(&triangle) 51 | for k, p := range test.p { 52 | d2 := ti.minDistance2(p) 53 | if !EqualFloat64(d2, test.d2[k], tolerance) { 54 | t.Errorf("test %d.%d: expected %f, got %f", i, k, test.d2[k], d2) 55 | } 56 | } 57 | } 58 | } 59 | 60 | // sanity test with random triangles 61 | const boxSize = 100.0 62 | const d2Max = 3.0 * (boxSize * boxSize) 63 | b := NewBox3(v3.Vec{0, 0, 0}, v3.Vec{100, 100, 100}) 64 | for i := 0; i < 10000; i++ { 65 | x := b.RandomTriangle() 66 | ti := newTriangleInfo(&x) 67 | p := b.Random() 68 | d2 := ti.minDistance2(p) 69 | if d2 < 0 { 70 | t.Errorf("test %d: expected >= 0, got %f", i, d2) 71 | } 72 | if d2 > d2Max { 73 | t.Errorf("test %d: expected <= %f, got %f", i, d2Max, d2) 74 | } 75 | } 76 | 77 | } 78 | 79 | //----------------------------------------------------------------------------- 80 | -------------------------------------------------------------------------------- /sdf/quadratic.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Quadratic Solver 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package sdf 10 | 11 | import "math" 12 | 13 | //----------------------------------------------------------------------------- 14 | 15 | type qSoln int 16 | 17 | const ( 18 | zeroSoln qSoln = iota 19 | oneSoln 20 | twoSoln 21 | infSoln 22 | ) 23 | 24 | // Return the real solutions of ax^2 + bx + c = 0 25 | func quadratic(a, b, c float64) ([]float64, qSoln) { 26 | // TODO Fix all comparisons to 0 27 | if a == 0 { 28 | if b == 0 { 29 | if c == 0 { 30 | // a = 0, b = 0, c = 0 31 | return nil, infSoln 32 | } 33 | // a = 0, b = 0, c != 0 34 | return nil, zeroSoln 35 | } 36 | // a =0, b != 0, c != 0 37 | return []float64{-c / b}, oneSoln 38 | } 39 | det := b*b - 4*a*c 40 | if det < 0 { 41 | return nil, zeroSoln 42 | } 43 | x := -b / (2 * a) 44 | if det == 0 { 45 | return []float64{x}, oneSoln 46 | } 47 | d := math.Sqrt(det) / (2 * a) 48 | return []float64{x + d, x - d}, twoSoln 49 | } 50 | 51 | //----------------------------------------------------------------------------- 52 | -------------------------------------------------------------------------------- /sdf/triangle2.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | 2D Triangles 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package sdf 10 | 11 | import ( 12 | "math" 13 | 14 | v2 "github.com/deadsy/sdfx/vec/v2" 15 | ) 16 | 17 | //----------------------------------------------------------------------------- 18 | 19 | // Triangle2 is a 2D triangle 20 | type Triangle2 [3]v2.Vec 21 | 22 | // Circumcenter returns the circumcenter of a triangle. 23 | func (t Triangle2) Circumcenter() (v2.Vec, error) { 24 | 25 | var m1, m2, mx1, mx2, my1, my2 float64 26 | var xc, yc float64 27 | 28 | x1 := t[0].X 29 | x2 := t[1].X 30 | x3 := t[2].X 31 | 32 | y1 := t[0].Y 33 | y2 := t[1].Y 34 | y3 := t[2].Y 35 | 36 | fabsy1y2 := math.Abs(y1 - y2) 37 | fabsy2y3 := math.Abs(y2 - y3) 38 | 39 | // Check for coincident points 40 | if fabsy1y2 < epsilon && fabsy2y3 < epsilon { 41 | return v2.Vec{}, ErrMsg("coincident points") 42 | } 43 | 44 | if fabsy1y2 < epsilon { 45 | m2 = -(x3 - x2) / (y3 - y2) 46 | mx2 = (x2 + x3) / 2.0 47 | my2 = (y2 + y3) / 2.0 48 | xc = (x2 + x1) / 2.0 49 | yc = m2*(xc-mx2) + my2 50 | } else if fabsy2y3 < epsilon { 51 | m1 = -(x2 - x1) / (y2 - y1) 52 | mx1 = (x1 + x2) / 2.0 53 | my1 = (y1 + y2) / 2.0 54 | xc = (x3 + x2) / 2.0 55 | yc = m1*(xc-mx1) + my1 56 | } else { 57 | m1 = -(x2 - x1) / (y2 - y1) 58 | m2 = -(x3 - x2) / (y3 - y2) 59 | mx1 = (x1 + x2) / 2.0 60 | mx2 = (x2 + x3) / 2.0 61 | my1 = (y1 + y2) / 2.0 62 | my2 = (y2 + y3) / 2.0 63 | xc = (m1*mx1 - m2*mx2 + my2 - my1) / (m1 - m2) 64 | if fabsy1y2 > fabsy2y3 { 65 | yc = m1*(xc-mx1) + my1 66 | } else { 67 | yc = m2*(xc-mx2) + my2 68 | } 69 | } 70 | 71 | return v2.Vec{xc, yc}, nil 72 | } 73 | 74 | // InCircumcircle return inside == true if the point is inside the circumcircle of the triangle. 75 | // Returns done == true if the vertex and the subsequent x-ordered vertices are outside the circumcircle. 76 | func (t Triangle2) InCircumcircle(p v2.Vec) (inside, done bool) { 77 | c, err := t.Circumcenter() 78 | if err != nil { 79 | inside = false 80 | done = true 81 | return 82 | } 83 | 84 | // radius squared of circumcircle 85 | dx := t[0].X - c.X 86 | dy := t[0].Y - c.Y 87 | r2 := dx*dx + dy*dy 88 | 89 | // distance squared from circumcenter to point 90 | dx = p.X - c.X 91 | dy = p.Y - c.Y 92 | d2 := dx*dx + dy*dy 93 | 94 | // is the point within the circumcircle? 95 | inside = d2-r2 <= epsilon 96 | 97 | // If this vertex has an x-value beyond the circumcenter and the distance based on the x-delta 98 | // is greater than the circumradius, then this triangle is done for this and all subsequent vertices 99 | // since the vertex list has been sorted by x-value. 100 | done = (dx > 0) && (dx*dx > r2) 101 | 102 | return 103 | } 104 | 105 | //----------------------------------------------------------------------------- 106 | -------------------------------------------------------------------------------- /sdf/triangle3_test.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | 3D Triangle Testing 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package sdf 10 | 11 | import ( 12 | "testing" 13 | 14 | v3 "github.com/deadsy/sdfx/vec/v3" 15 | ) 16 | 17 | //----------------------------------------------------------------------------- 18 | 19 | func Test_Triangle3_rotateToXY(t *testing.T) { 20 | 21 | testSet := []struct { 22 | t *Triangle3 23 | result v3.Vec // {0,0,0}, {a,0,0}, {b,c,0} 24 | }{ 25 | { 26 | &Triangle3{{1, 1, 1}, {2, 2, 2}, {3, 4, 5}}, 27 | v3.Vec{1.7320508075688776, 5.196152422706633, 1.4142135623730954}, 28 | }, 29 | { 30 | &Triangle3{{1, 1, 1}, {2, 2, 2}, {5, 4, 3}}, 31 | v3.Vec{1.7320508075688776, 5.196152422706633, 1.4142135623730954}, 32 | }, 33 | { 34 | &Triangle3{{0, 0, 5}, {1, 0, 5}, {4, 8, 5}}, 35 | v3.Vec{1, 4, 8}, 36 | }, 37 | { 38 | &Triangle3{{0, 0, 0}, {1, 0, 0}, {4, -8, 0}}, 39 | v3.Vec{1, 4, 8}, 40 | }, 41 | { 42 | &Triangle3{{0, 0, -3}, {10, 0, -3}, {4, 7, -3}}, 43 | v3.Vec{10, 4, 7}, 44 | }, 45 | { 46 | &Triangle3{{0, -3, 0}, {10, -3, 0}, {4, -3, 7}}, 47 | v3.Vec{10, 4, 7}, 48 | }, 49 | { 50 | &Triangle3{{-8.61, 1.80, 19.31}, {-5.99, -0.72, 21.51}, {-8.88, 5.05, 20.67}}, 51 | v3.Vec{4.249094021082613, -1.389802148575515, 3.2486073920704683}, 52 | }, 53 | { 54 | &Triangle3{{0, 0, -1}, {10, 0, -1}, {-10, -8, -1}}, 55 | v3.Vec{10, -10, 8}, 56 | }, 57 | { 58 | &Triangle3{{0, 0, 3}, {10, 0, 3}, {-10, 8, 3}}, 59 | v3.Vec{10, -10, 8}, 60 | }, 61 | } 62 | 63 | for i, test := range testSet { 64 | 65 | m := test.t.rotateToXY() 66 | x0 := m.MulPosition(test.t[0]) 67 | x1 := m.MulPosition(test.t[1]) 68 | x2 := m.MulPosition(test.t[2]) 69 | 70 | r := test.result 71 | r0 := v3.Vec{0, 0, 0} 72 | r1 := v3.Vec{r.X, 0, 0} 73 | r2 := v3.Vec{r.Y, r.Z, 0} 74 | 75 | if !x0.Equals(r0, tolerance) { 76 | t.Errorf("test %d: expected %v, got %v", i, r0, x0) 77 | } 78 | if !x1.Equals(r1, tolerance) { 79 | t.Errorf("test %d: expected %v, got %v", i, r1, x1) 80 | } 81 | if !x2.Equals(r2, tolerance) { 82 | t.Errorf("test %d: expected %v, got %v", i, r2, x2) 83 | } 84 | } 85 | 86 | // sanity test with random triangles 87 | b := NewBox3(v3.Vec{-1, 3, -7}, v3.Vec{7, 5, 3}) 88 | for i := 0; i < 10000; i++ { 89 | x := b.RandomTriangle() 90 | m := x.rotateToXY() 91 | 92 | x0 := m.MulPosition(x[0]) 93 | x1 := m.MulPosition(x[1]) 94 | x2 := m.MulPosition(x[2]) 95 | 96 | // x0 should be {0,0,0} 97 | if !x0.Equals(v3.Vec{0, 0, 0}, tolerance) { 98 | t.Errorf("test %d: expected {0,0,0}, got %v", i, x0) 99 | } 100 | // x1 should be {a,0,0}, where a > 0 101 | if x1.X <= 0 || !EqualFloat64(x1.Y, 0, tolerance) || !EqualFloat64(x1.Z, 0, tolerance) { 102 | t.Errorf("test %d: expected {a,0,0}, got %v", i, x1) 103 | } 104 | // x2 should be {b,c,0}, where c > 0 105 | if x2.Y <= 0 || !EqualFloat64(x2.Z, 0, tolerance) { 106 | t.Errorf("test %d: expected {b,c,0}, got %v", i, x2) 107 | } 108 | } 109 | 110 | } 111 | 112 | //----------------------------------------------------------------------------- 113 | -------------------------------------------------------------------------------- /tools/ply/__init__.py: -------------------------------------------------------------------------------- 1 | # PLY package 2 | # Author: David Beazley (dave@dabeaz.com) 3 | 4 | __version__ = '3.9' 5 | __all__ = ['lex','yacc'] 6 | -------------------------------------------------------------------------------- /tools/ply/ygen.py: -------------------------------------------------------------------------------- 1 | # ply: ygen.py 2 | # 3 | # This is a support program that auto-generates different versions of the YACC parsing 4 | # function with different features removed for the purposes of performance. 5 | # 6 | # Users should edit the method LParser.parsedebug() in yacc.py. The source code 7 | # for that method is then used to create the other methods. See the comments in 8 | # yacc.py for further details. 9 | 10 | import os.path 11 | import shutil 12 | 13 | def get_source_range(lines, tag): 14 | srclines = enumerate(lines) 15 | start_tag = '#--! %s-start' % tag 16 | end_tag = '#--! %s-end' % tag 17 | 18 | for start_index, line in srclines: 19 | if line.strip().startswith(start_tag): 20 | break 21 | 22 | for end_index, line in srclines: 23 | if line.strip().endswith(end_tag): 24 | break 25 | 26 | return (start_index + 1, end_index) 27 | 28 | def filter_section(lines, tag): 29 | filtered_lines = [] 30 | include = True 31 | tag_text = '#--! %s' % tag 32 | for line in lines: 33 | if line.strip().startswith(tag_text): 34 | include = not include 35 | elif include: 36 | filtered_lines.append(line) 37 | return filtered_lines 38 | 39 | def main(): 40 | dirname = os.path.dirname(__file__) 41 | shutil.copy2(os.path.join(dirname, 'yacc.py'), os.path.join(dirname, 'yacc.py.bak')) 42 | with open(os.path.join(dirname, 'yacc.py'), 'r') as f: 43 | lines = f.readlines() 44 | 45 | parse_start, parse_end = get_source_range(lines, 'parsedebug') 46 | parseopt_start, parseopt_end = get_source_range(lines, 'parseopt') 47 | parseopt_notrack_start, parseopt_notrack_end = get_source_range(lines, 'parseopt-notrack') 48 | 49 | # Get the original source 50 | orig_lines = lines[parse_start:parse_end] 51 | 52 | # Filter the DEBUG sections out 53 | parseopt_lines = filter_section(orig_lines, 'DEBUG') 54 | 55 | # Filter the TRACKING sections out 56 | parseopt_notrack_lines = filter_section(parseopt_lines, 'TRACKING') 57 | 58 | # Replace the parser source sections with updated versions 59 | lines[parseopt_notrack_start:parseopt_notrack_end] = parseopt_notrack_lines 60 | lines[parseopt_start:parseopt_end] = parseopt_lines 61 | 62 | lines = [line.rstrip()+'\n' for line in lines] 63 | with open(os.path.join(dirname, 'yacc.py'), 'w') as f: 64 | f.writelines(lines) 65 | 66 | print('Updated yacc.py') 67 | 68 | if __name__ == '__main__': 69 | main() 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /tools/sha1tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Output sha1 hashes for a set of matched files. 4 | """ 5 | 6 | import glob 7 | import hashlib 8 | 9 | 10 | def sha1sum(filename): 11 | """return the sha1 hash of a file""" 12 | with open(filename, mode="rb") as f: 13 | d = hashlib.sha1() 14 | while True: 15 | buf = f.read(8192) 16 | if not buf: 17 | break 18 | d.update(buf) 19 | return d.hexdigest() 20 | 21 | 22 | def main(): 23 | """entry point""" 24 | f = [] 25 | for ext in ("stl", "svg", "dxf", "png"): 26 | f.extend(glob.glob(f"*.{ext}")) 27 | for fname in f: 28 | print(f"{sha1sum(fname)} {fname}") 29 | 30 | 31 | main() 32 | -------------------------------------------------------------------------------- /vec/conv/conv.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Vector Conversions 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package conv 10 | 11 | import ( 12 | "math" 13 | 14 | "github.com/deadsy/sdfx/vec/p2" 15 | v2 "github.com/deadsy/sdfx/vec/v2" 16 | "github.com/deadsy/sdfx/vec/v2i" 17 | v3 "github.com/deadsy/sdfx/vec/v3" 18 | "github.com/deadsy/sdfx/vec/v3i" 19 | ) 20 | 21 | //----------------------------------------------------------------------------- 22 | // V2i to X 23 | 24 | // V2iToV2 converts a 2D integer vector to a float vector. 25 | func V2iToV2(a v2i.Vec) v2.Vec { 26 | return v2.Vec{float64(a.X), float64(a.Y)} 27 | } 28 | 29 | //----------------------------------------------------------------------------- 30 | // V3i to X 31 | 32 | // V3iToV3 converts a 3D integer vector to a float vector. 33 | func V3iToV3(a v3i.Vec) v3.Vec { 34 | return v3.Vec{float64(a.X), float64(a.Y), float64(a.Z)} 35 | } 36 | 37 | //----------------------------------------------------------------------------- 38 | // V2 to X 39 | 40 | // V2ToP2 converts a cartesian to a polar coordinate. 41 | func V2ToP2(a v2.Vec) p2.Vec { 42 | return p2.Vec{a.Length(), math.Atan2(a.Y, a.X)} 43 | } 44 | 45 | // V2ToV3 converts a 2D vector to a 3D vector with a specified Z value. 46 | func V2ToV3(a v2.Vec, z float64) v3.Vec { 47 | return v3.Vec{a.X, a.Y, z} 48 | } 49 | 50 | // V2ToV2i converts a 2D float vector to a 2D integer vector. 51 | func V2ToV2i(a v2.Vec) v2i.Vec { 52 | return v2i.Vec{int(a.X), int(a.Y)} 53 | } 54 | 55 | //----------------------------------------------------------------------------- 56 | // V3 to X 57 | 58 | // V3ToV3i converts a 3D float vector to a 3D integer vector. 59 | func V3ToV3i(a v3.Vec) v3i.Vec { 60 | return v3i.Vec{int(a.X), int(a.Y), int(a.Z)} 61 | } 62 | 63 | //----------------------------------------------------------------------------- 64 | // P2 to X 65 | 66 | // P2ToV2 converts a polar to a cartesian coordinate. 67 | func P2ToV2(a p2.Vec) v2.Vec { 68 | return v2.Vec{a.R * math.Cos(a.Theta), a.R * math.Sin(a.Theta)} 69 | } 70 | 71 | //----------------------------------------------------------------------------- 72 | -------------------------------------------------------------------------------- /vec/p2/p2.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Floating Point 2D Polar Vectors 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package p2 10 | 11 | //----------------------------------------------------------------------------- 12 | 13 | // Vec is a 2D float64 polar vector. 14 | type Vec struct { 15 | R, Theta float64 16 | } 17 | 18 | //----------------------------------------------------------------------------- 19 | -------------------------------------------------------------------------------- /vec/v2i/v2i.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Integer 2D Vectors 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package v2i 10 | 11 | //----------------------------------------------------------------------------- 12 | 13 | // Vec is a 2D integer vector. 14 | type Vec struct { 15 | X, Y int 16 | } 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | // AddScalar adds a scalar to each component of the vector. 21 | func (a Vec) AddScalar(b int) Vec { 22 | return Vec{a.X + b, a.Y + b} 23 | } 24 | 25 | // SubScalar subtracts a scalar from each component of the vector. 26 | func (a Vec) SubScalar(b int) Vec { 27 | return Vec{a.X - b, a.Y - b} 28 | } 29 | 30 | // Add adds two vectors. Return v = a + b. 31 | func (a Vec) Add(b Vec) Vec { 32 | return Vec{a.X + b.X, a.Y + b.Y} 33 | } 34 | 35 | //----------------------------------------------------------------------------- 36 | -------------------------------------------------------------------------------- /vec/v2i/v2i_test.go: -------------------------------------------------------------------------------- 1 | package v2i 2 | -------------------------------------------------------------------------------- /vec/v3i/v3i.go: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | /* 3 | 4 | Integer 3D Vectors 5 | 6 | */ 7 | //----------------------------------------------------------------------------- 8 | 9 | package v3i 10 | 11 | //----------------------------------------------------------------------------- 12 | 13 | // Vec is a 3D integer vector. 14 | type Vec struct { 15 | X, Y, Z int 16 | } 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | // AddScalar adds a scalar to each component of the vector. 21 | func (a Vec) AddScalar(b int) Vec { 22 | return Vec{a.X + b, a.Y + b, a.Z + b} 23 | } 24 | 25 | // SubScalar subtracts a scalar from each component of the vector. 26 | func (a Vec) SubScalar(b int) Vec { 27 | return Vec{a.X - b, a.Y - b, a.Z - b} 28 | } 29 | 30 | // Add adds two vectors. Return v = a + b. 31 | func (a Vec) Add(b Vec) Vec { 32 | return Vec{a.X + b.X, a.Y + b.Y, a.Z + b.Z} 33 | } 34 | 35 | //----------------------------------------------------------------------------- 36 | -------------------------------------------------------------------------------- /vec/v3i/v3i_test.go: -------------------------------------------------------------------------------- 1 | package v3i 2 | --------------------------------------------------------------------------------