Skip to content

Commit

Permalink
add CircleArray (3D)
Browse files Browse the repository at this point in the history
  • Loading branch information
soypat committed Oct 5, 2024
1 parent 3c6083e commit 5cba178
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 34 deletions.
55 changes: 55 additions & 0 deletions cpu_evaluators.go
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,61 @@ func (s *annulus2D) Evaluate(pos []ms2.Vec, dist []float32, userData any) error
return nil
}

func (c *circarray) Evaluate(pos []ms3.Vec, dist []float32, userData any) error {
vp, err := gleval.GetVecPool(userData)
if err != nil {
return err
}
sdf, err := gleval.AssertSDF3(c.s)
if err != nil {
return err
}
pos0 := vp.V3.Acquire(len(pos))
pos1 := vp.V3.Acquire(len(pos))
defer vp.V3.Release(pos0)
defer vp.V3.Release(pos1)

angle := 2 * math32.Pi / float32(c.circleDiv)
ncirc := float32(c.circleDiv)
ninsm1 := float32(c.nInst - 1)
for i, p := range pos {

pangle := math32.Atan2(p.Y, p.X)
id := math32.Floor(pangle / angle)
if id < 0 {
id += ncirc
}
var i0, i1 float32
if id >= ninsm1 {
i0 = ninsm1
i1 = 0
} else {
i0 = id
i1 = id + 1
}
p2d := ms2.Vec{X: p.X, Y: p.Y}
p0 := ms2.MulMatVecTrans(ms2.RotationMat2(angle*i0), p2d)
p1 := ms2.MulMatVecTrans(ms2.RotationMat2(angle*i1), p2d)
pos0[i] = ms3.Vec{X: p0.X, Y: p0.Y, Z: p.Z}
pos1[i] = ms3.Vec{X: p1.X, Y: p1.Y, Z: p.Z}
}

dist1 := vp.Float.Acquire(len(pos))
defer vp.Float.Release(dist1)
err = sdf.Evaluate(pos1, dist1, userData)
if err != nil {
return err
}
err = sdf.Evaluate(pos0, dist, userData)
if err != nil {
return err
}
for i, d := range dist {
dist[i] = math32.Min(d, dist1[i])
}
return nil
}

func (c *circarray2D) Evaluate(pos []ms2.Vec, dist []float32, userData any) error {
vp, err := gleval.GetVecPool(userData)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions gsdf2d.go
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,7 @@ func (s *annulus2D) AppendShaderBody(b []byte) []byte {
return b
}

// CircularArray2 is the circular domain repetition operation around the origin (x,y)=(0,0).
// CircularArray2D is the circular domain repetition operation around the origin (x,y)=(0,0).
// It repeats the shape numInstances times and the spacing angle is defined by circleDiv such that angle = 2*pi/circleDiv.
// The operation is defined this way so that the argument shape is evaluated only twice per circular array evaluation, regardless of instances.
func CircularArray2D(s glbuild.Shader2D, numInstances, circleDiv int) (glbuild.Shader2D, error) {
Expand All @@ -1007,7 +1007,7 @@ func CircularArray2D(s glbuild.Shader2D, numInstances, circleDiv int) (glbuild.S
} else if s == nil {
return nil, errors.New("nil argument to circarray")
} else if numInstances > circleDiv {
return nil, errors.New("bad circular array instances, must be less than circleDiv")
return nil, errors.New("bad circular array instances, must be less than or equal to circleDiv")
}
return &circarray2D{s: s, circleDiv: circleDiv, nInst: numInstances}, nil
}
Expand Down Expand Up @@ -1039,7 +1039,7 @@ func (ca *circarray2D) ForEach2DChild(userData any, fn func(userData any, s *glb
// func (ca *circarray2D) angle() float32 { return 2 * math32.Pi / float32(ca.n) }

func (ca *circarray2D) AppendShaderName(b []byte) []byte {
b = append(b, "circarray2D2d"...)
b = append(b, "circarray2D"...)
b = glbuild.AppendFloats(b, 0, 'n', 'p', float32(ca.nInst), float32(ca.circleDiv))
b = append(b, '_')
b = ca.s.AppendShaderName(b)
Expand Down
18 changes: 8 additions & 10 deletions gsdfaux/gsdfaux.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import (
type RenderConfig struct {
STLOutput io.Writer
VisualOutput io.Writer
Resolution float32
UseGPU bool
Silent bool
// Resolution decides the STL output resolution. It correlates with the minimum triangle size.
Resolution float32
UseGPU bool
Silent bool
// EnableCaching uses [gleval.BlockCachedSDF3] to omit potential evaluations.
// Can cut down on times for very complex SDFs, mainly when using CPU.
EnableCaching bool
Expand All @@ -37,14 +38,11 @@ type RenderConfig struct {
// RenderShader3D is an auxiliary function to aid users in getting setup in using gsdf quickly.
// Ideally users should implement their own rendering functions since applications may vary widely.
func RenderShader3D(s glbuild.Shader3D, cfg RenderConfig) (err error) {
const (
_ = 1 << (iota * 10)
kB
MB
GB
)
if cfg.Resolution <= 0 && !math.IsNaN(cfg.Resolution) && !math.IsInf(cfg.Resolution, 0) {
return errors.New("RenderConfig resolution must be positive, non-infinity")
}
if cfg.STLOutput == nil && cfg.VisualOutput == nil {
return errors.New("Render requires output parameter in config")
return errors.New("RenderShader3D requires output parameter in config")
}
log := func(args ...any) {
if !cfg.Silent {
Expand Down
72 changes: 51 additions & 21 deletions operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -699,37 +699,43 @@ func (s *shell) AppendShaderBody(b []byte) []byte {
return b
}

// circularArray is the circular domain repetition operation around Z axis.
// It repeats domain centered around (x,y)=(0,0) around the Z axis.
func circularArray(s glbuild.Shader3D, angle float32, n int) (glbuild.Shader3D, error) {
if n <= 0 {
// CircularArray is the circular domain repetition operation around the origin (x,y,z)=(0,0,0).
// It repeats the shape numInstances times and the spacing angle is defined by circleDiv such that angle = 2*pi/circleDiv.
// The operation is defined this way so that the argument shape is evaluated only twice per circular array evaluation, regardless of instances.
func CircularArray(s glbuild.Shader3D, numInstances, circleDiv int) (glbuild.Shader3D, error) {
if circleDiv <= 1 || numInstances <= 0 {
return nil, errors.New("invalid circarray repeat param")
} else if s == nil {
return nil, errors.New("nil argument to circarray")
} else if numInstances > circleDiv {
return nil, errors.New("bad circular array instances, must be less than or equal to circleDiv")
}
// return &circarray{s: s, n: n, angle: angle}, nil
return nil, errors.New("TODO")
return &circarray{s: s, circleDiv: circleDiv, nInst: numInstances}, nil
}

type circarray struct {
s glbuild.Shader3D
n int
angle float32
s glbuild.Shader3D
nInst int
circleDiv int
}

func (ca *circarray) Bounds() ms3.Box {
// Naive solution, place bounding box N times
// and take the union of all bounds.
bb := ca.s.Bounds()
size := bb.Size()
center := bb.Center()
v := ms2.Vec{X: center.X, Y: center.Y}
m := ms2.RotationMat2(ca.angle)
for i := 0; i < ca.n; i++ {
v = ms2.MulMatVec(m, v)
centerV := ms3.Vec{X: v.X, Y: v.Y, Z: center.Z}
bb = bb.Union(ms3.NewCenteredBox(centerV, size))
bb2 := ms2.Box{
Min: ms2.Vec{X: bb.Min.X, Y: bb.Min.Y},
Max: ms2.Vec{X: bb.Max.X, Y: bb.Max.Y},
}
verts := bb2.Vertices()
angle := 2 * math32.Pi / float32(ca.circleDiv)
m := ms2.RotationMat2(angle)
for i := 0; i < ca.nInst-1; i++ {
for i := range verts {
verts[i] = ms2.MulMatVec(m, verts[i])
bb2 = bb2.IncludePoint(verts[i])
}
}
bb.Max.X, bb.Max.Y = bb2.Max.X, bb2.Max.Y
bb.Min.X, bb.Min.Y = bb2.Min.X, bb2.Min.Y
return bb
}

Expand All @@ -741,13 +747,37 @@ func (ca *circarray) ForEachChild(userData any, fn func(userData any, s *glbuild

func (ca *circarray) AppendShaderName(b []byte) []byte {
b = append(b, "circarray"...)
b = glbuild.AppendFloats(b, 0, 'n', 'p', float32(ca.n), ca.angle)
b = glbuild.AppendFloats(b, 0, 'n', 'p', float32(ca.nInst), float32(ca.circleDiv))
b = append(b, '_')
b = ca.s.AppendShaderName(b)
return b
}

func (ca *circarray) AppendShaderBody(b []byte) []byte {

angle := 2 * math32.Pi / float32(ca.circleDiv)
b = glbuild.AppendFloatDecl(b, "ncirc", float32(ca.circleDiv))
b = glbuild.AppendFloatDecl(b, "angle", angle)
b = glbuild.AppendFloatDecl(b, "ninsm1", float32(ca.nInst-1))
b = append(b, `float pangle=atan(p.y, p.x);
float i=floor(pangle/angle);
if (i<0.0) i=ncirc+i;
float i0,i1;
if (i>=ninsm1) {
i0=ninsm1;
i1=0.0;
} else {
i0=i;
i1=i+1.0;
}
float c0 = cos(angle*i0);
float s0 = sin(angle*i0);
vec2 p0 = mat2(c0,-s0,s0,c0)*p.xy;
float c1 = cos(angle*i1);
float s1 = sin(angle*i1);
vec2 p1 = mat2(c1,-s1,s1,c1)*p.xy;
`...)
b = glbuild.AppendDistanceDecl(b, "d0", "vec3(p0.x,p0.y,p.z)", ca.s)
b = glbuild.AppendDistanceDecl(b, "d1", "vec3(p1.x,p1.y,p.z)", ca.s)
b = append(b, "return min(d0, d1);"...)
return b
}

0 comments on commit 5cba178

Please sign in to comment.