Skip to content
This repository has been archived by the owner on Aug 12, 2024. It is now read-only.

Commit

Permalink
add logo
Browse files Browse the repository at this point in the history
  • Loading branch information
soypat committed May 12, 2022
1 parent 361f63c commit 9d4c6e7
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 35 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# sdf (originally sdfx)

<img align="right" width="190px" src="https://user-images.githubusercontent.com/26156425/167976351-ae377891-a215-4711-b202-e973b815b6bb.png">

A rewrite of the original CAD package [`sdfx`](https://github.com/deadsy/sdfx) for generating 2D and 3D geometry using [Go](https://go.dev/).

* Objects are modelled with 2d and 3d signed distance functions (SDFs).
Expand Down Expand Up @@ -123,3 +125,8 @@ This presented a few problems:
None planned.

My understanding is the `sdfx` author has a very different design goal to what I envision. See the bullet-list of issues at the start of [Questionable API design](#questionable-api-design).

## Logo work
Gopher rendition by [Juliette Whittingslow](https://www.instagram.com/artewitty/).
Gopher design authored by [Renée French](https://www.instagram.com/reneefrench)
is licensed by the Creative Commons Attribution 3.0 licensed.
11 changes: 11 additions & 0 deletions internal/d3/triangle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package d3

import "gonum.org/v1/gonum/spatial/r3"

type r3Triangle [3]r3.Vec

// Closest returns closest point on the triangle to argument point p.
func (t r3Triangle) Closest(p r3.Vec) r3.Vec {
// Calculate transformation matrix so that
return r3.Vec{}
}
64 changes: 49 additions & 15 deletions render/kdrender.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,25 @@ import (
var (
_ sdf.SDF3 = kdSDF{}
_ kdtree.Interface = kdTriangles{}
_ kdtree.Bounder = kdTriangles{}
)

func NewKDSDF(model []Triangle3) sdf.SDF3 {
mykd := make(kdTriangles, len(model))
var min, max r3.Vec
// var min, max r3.Vec
for i := range mykd {
tri := kdTriangle(model[i])
mykd[i] = tri
triMin := d3.MinElem(tri.V[2], d3.MinElem(tri.V[0], tri.V[1]))
triMax := d3.MaxElem(tri.V[2], d3.MaxElem(tri.V[0], tri.V[1]))
min = d3.MinElem(triMin, min)
max = d3.MaxElem(triMax, max)
}
tree := kdtree.New(mykd, false)
tree.Root.Bounding = &kdtree.Bounding{
Min: kdTriangle{V: [3]r3.Vec{min, min, min}},
Max: kdTriangle{V: [3]r3.Vec{max, max, max}},
// triMin := d3.MinElem(tri.V[2], d3.MinElem(tri.V[0], tri.V[1]))
// triMax := d3.MaxElem(tri.V[2], d3.MaxElem(tri.V[0], tri.V[1]))
// min = d3.MinElem(triMin, min)
// max = d3.MaxElem(triMax, max)
}
tree := kdtree.New(mykd, true)
// tree.Root.Bounding = &kdtree.Bounding{
// Min: kdTriangle{V: [3]r3.Vec{min, min, min}},
// Max: kdTriangle{V: [3]r3.Vec{max, max, max}},
// }
return kdSDF{
tree: *tree,
}
Expand All @@ -40,16 +41,24 @@ type kdSDF struct {
}

func (s kdSDF) Evaluate(v r3.Vec) float64 {
const eps = 1e-3
// do some ad-hoc math with the triangle normal ????
triangle := s.Nearest(v)
minDist := math.MaxFloat64
// Find closest vertex
closest := r3.Vec{}
for i := 0; i < 3; i++ {
minDist = math.Min(minDist, r3.Norm(r3.Sub(v, triangle.V[i])))
vDist := r3.Norm(r3.Sub(v, triangle.V[i]))
if vDist < minDist {
closest = triangle.V[i]
minDist = vDist
}
}
if minDist < eps {
return 0
}
c := kdCentroid(triangle)
pointDir := r3.Sub(v, c)
pointDir := r3.Sub(v, closest)
n := triangle.Normal()
// return math.Copysign(minDist, math.Pi/2-alpha)
alpha := math.Acos(r3.Cos(n, pointDir))
return math.Copysign(minDist, math.Pi/2-alpha)
}
Expand Down Expand Up @@ -90,7 +99,7 @@ func (k kdTriangles) Len() int { return len(k) }
// Pivot partitions the list based on the dimension specified.
func (k kdTriangles) Pivot(d kdtree.Dim) int {
p := kdPlane{dim: int(d), triangles: k}
kdtree.Partition(p, kdtree.MedianOfMedians(p))
return kdtree.Partition(p, kdtree.MedianOfMedians(p))
return 0
}

Expand All @@ -100,6 +109,22 @@ func (k kdTriangles) Slice(start, end int) kdtree.Interface {
return k[start:end]
}

func (k kdTriangles) Bounds() *kdtree.Bounding {
max := r3.Vec{-math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64}
min := r3.Vec{math.MaxFloat64, math.MaxFloat64, math.MaxFloat64}
for _, tri := range k {
tbounds := tri.Bounds()
tmin := tbounds.Min.(kdTriangle)
tmax := tbounds.Max.(kdTriangle)
min = d3.MinElem(min, tmin.V[0])
max = d3.MaxElem(max, tmax.V[0])
}
return &kdtree.Bounding{
Min: kdTriangle{V: [3]r3.Vec{min, min, min}},
Max: kdTriangle{V: [3]r3.Vec{max, max, max}},
}
}

// Compare returns the signed distance of a from the plane passing through
// b and perpendicular to the dimension d.
//
Expand All @@ -120,6 +145,15 @@ func (a kdTriangle) Distance(b kdtree.Comparable) float64 {
return kdDist(a, b.(kdTriangle))
}

func (a kdTriangle) Bounds() *kdtree.Bounding {
min := d3.MinElem(a.V[2], d3.MinElem(a.V[0], a.V[1]))
max := d3.MaxElem(a.V[2], d3.MaxElem(a.V[0], a.V[1]))
return &kdtree.Bounding{
Min: kdTriangle{V: [3]r3.Vec{min, min, min}},
Max: kdTriangle{V: [3]r3.Vec{max, max, max}},
}
}

func (a kdTriangle) Normal() r3.Vec {
v := Triangle3(a)
return v.Normal()
Expand Down
44 changes: 24 additions & 20 deletions render/kdrender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,41 @@ package render_test

import (
"testing"
"time"

"github.com/soypat/sdf/form3"
"github.com/soypat/sdf/internal/d3"
"github.com/soypat/sdf/render"
"gonum.org/v1/gonum/spatial/r3"
)

func TestKDSDF(t *testing.T) {
var defaultView = viewConfig{
up: r3.Vec{Z: 1},
eyepos: d3.Elem(3),
near: 1,
far: 10,
}
// var defaultView = viewConfig{
// up: r3.Vec{Z: 1},
// eyepos: d3.Elem(3),
// near: 1,
// far: 10,
// }
const quality = 20
s, _ := form3.Sphere(1)
err := render.CreateSTL("kd_before.stl", render.NewOctreeRenderer(s, quality))
if err != nil {
t.Fatal(err)
}
stlToPNG(t, "kd_before.stl", "kd_before.png", defaultView)
// err := render.CreateSTL("kd_before.stl", render.NewOctreeRenderer(s, quality))
// if err != nil {
// t.Fatal(err)
// }
// stlToPNG(t, "kd_before.stl", "kd_before.png", defaultView)
model, err := render.RenderAll(render.NewOctreeRenderer(s, quality))
if err != nil {
t.Fatal(err)
}
sdf := render.NewKDSDF(model)
t.Error(sdf.BoundingBox())
outside := sdf.Evaluate(r3.Vec{2, 0, 0}) // evaluate point outside bounds
inside := sdf.Evaluate(r3.Vec{0, 0, 0}) // evaluate point inside bounds
surface := sdf.Evaluate(r3.Vec{1, 0, 0}) // evaluate point on surface
t.Errorf("outside:%.2g, inside:%.2g, surface:%.2g", outside, inside, surface)
render.CreateSTL("kd_after.stl", render.NewOctreeRenderer(sdf, quality))
stlToPNG(t, "kd_after.stl", "kd_after.png", defaultView)
t.Error(len(model), "triangles")
kdf := render.NewKDSDF(model)
t.Error(kdf.BoundingBox())
start := time.Now()
outside := kdf.Evaluate(r3.Vec{2, 0, 0}) // evaluate point outside bounds
inside := kdf.Evaluate(r3.Vec{0, 0, 0}) // evaluate point inside bounds

surface := kdf.Evaluate(r3.Vec{1, 0, 0}) // evaluate point on surface

t.Errorf("outside:%.2g, inside:%.2g, surface:%.2g in %s", outside, inside, surface, time.Since(start))
// render.CreateSTL("kd_after.stl", render.NewOctreeRenderer(sdf, quality/6))
// stlToPNG(t, "kd_after.stl", "kd_after.png", defaultView)
}

0 comments on commit 9d4c6e7

Please sign in to comment.