Skip to content

Commit

Permalink
add CircleArray2D
Browse files Browse the repository at this point in the history
  • Loading branch information
soypat committed Oct 5, 2024
1 parent 772b964 commit 3c6083e
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 7 deletions.
53 changes: 52 additions & 1 deletion cpu_evaluators.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ func (t *equilateralTri2d) Evaluate(pos []ms2.Vec, dist []float32, userData any)
}

func (c *rect2D) Evaluate(pos []ms2.Vec, dist []float32, userData any) error {
b := c.d
b := ms2.Scale(0.5, c.d)
for i, p := range pos {
d := ms2.Sub(ms2.AbsElem(p), b)
dist[i] = ms2.Norm(ms2.MaxElem(d, ms2.Vec{})) + math32.Min(0, math32.Max(d.X, d.Y))
Expand Down Expand Up @@ -907,3 +907,54 @@ func (s *annulus2D) Evaluate(pos []ms2.Vec, dist []float32, userData any) error
}
return nil
}

func (c *circarray2D) Evaluate(pos []ms2.Vec, dist []float32, userData any) error {
vp, err := gleval.GetVecPool(userData)
if err != nil {
return err
}
sdf, err := gleval.AssertSDF2(c.s)
if err != nil {
return err
}
pos0 := vp.V2.Acquire(len(pos))
pos1 := vp.V2.Acquire(len(pos))
defer vp.V2.Release(pos0)
defer vp.V2.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
}
pos0[i] = ms2.MulMatVecTrans(ms2.RotationMat2(angle*i0), p)
pos1[i] = ms2.MulMatVecTrans(ms2.RotationMat2(angle*i1), p)
}

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
}
63 changes: 60 additions & 3 deletions examples/test/glsdf3test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,15 @@ var _ = npt.SetFromNominal(1.0 / 2.0)

var PremadePrimitives2D = []glbuild.Shader2D{
mustShader2D(gsdf.NewLine2D(-2.3, 1, 13, 12, .1)),
mustShader2D(gsdf.NewRectangle(1, 2)),
mustShader2D(gsdf.NewArc(2.3, math.Pi/3, 0.1)),
mustShader2D(gsdf.NewCircle(1)),
mustShader2D(gsdf.NewHexagon(1)),
mustShader2D(gsdf.NewPolygon([]ms2.Vec{
{X: -1, Y: -1}, {X: -1, Y: 0}, {X: 0.5, Y: 2},
})),
mustShader2D(npt.Thread()),

// mustShader2D(gsdf.NewEllipse(1, 2)), // Ellipse seems to be very sensitive to position.
}
var BinaryOps = []func(a, b glbuild.Shader3D) glbuild.Shader3D{
Expand All @@ -143,7 +145,7 @@ var SmoothBinaryOps = []func(k float32, a, b glbuild.Shader3D) glbuild.Shader3D{
gsdf.SmoothIntersect,
}

var OtherUnaryRandomizedOps = []func(a glbuild.Shader3D, rng *rand.Rand) glbuild.Shader3D{
var UnaryRandomizedOps = []func(a glbuild.Shader3D, rng *rand.Rand) glbuild.Shader3D{
randomRotation,
randomShell,
randomElongate,
Expand All @@ -154,6 +156,10 @@ var OtherUnaryRandomizedOps = []func(a glbuild.Shader3D, rng *rand.Rand) glbuild
// randomArray, // round() differs from go's math.Round()
}

var UnaryRandomizedOps2D = []func(a glbuild.Shader2D, rng *rand.Rand) glbuild.Shader2D{
// randomCircArray2D,
}

var OtherUnaryRandomizedOps2D3D = []func(a glbuild.Shader2D, rng *rand.Rand) glbuild.Shader3D{
randomExtrude,
randomRevolve,
Expand Down Expand Up @@ -305,7 +311,7 @@ func test_sdf_gpu_cpu() error {
}
}
rng := rand.New(rand.NewSource(1))
for _, op := range OtherUnaryRandomizedOps {
for _, op := range UnaryRandomizedOps {
log.Printf("begin evaluating %s\n", getFnName(op))
for i := 0; i < 50; i++ {
primitive := PremadePrimitives[rng.Intn(len(PremadePrimitives))]
Expand Down Expand Up @@ -345,6 +351,47 @@ func test_sdf_gpu_cpu() error {
}
}
}
for _, op := range UnaryRandomizedOps2D {
log.Printf("begin evaluating %s\n", getFnName(op))
for i := 0; i < 50; i++ {
primitive := PremadePrimitives2D[rng.Intn(len(PremadePrimitives2D))]
obj := op(primitive, rng)
bounds := obj.Bounds()
pos := appendMeshgrid2D(scratchPos2[:0], bounds, nx, ny)
distCPU := scratchDistCPU[:len(pos)]
distGPU := scratchDistGPU[:len(pos)]
sdfcpu, err := gleval.AssertSDF2(obj)
if err != nil {
return err
}
err = sdfcpu.Evaluate(pos, distCPU, vp)
if err != nil {
return err
}
sdfgpu := makeGPUSDF2(obj)
err = sdfgpu.Evaluate(pos, distGPU, nil)
if err != nil {
return err
}
if getBaseTypename(primitive) == "poly2d" ||
(getBaseTypename(primitive) == "tri" && getFnName(op) == "randomRotation") {
log.Println("omit 2d dist checks")
continue
}
err = cmpDist(pos, distCPU, distGPU)
if err != nil {
description := sprintOpPrimitive(op, primitive)
return fmt.Errorf("%d %s: %s", i, description, err)
}
// log.Printf("allocated v3=%dMB v2=%dMB f32=%dMB", vp.V3.TotalAlloc()/MB, vp.V2.TotalAlloc()/MB, vp.Float.TotalAlloc()/MB)

// err = test_bounds(sdfcpu, scratchDist, vp)
// if err != nil {
// description := sprintOpPrimitive(op, primitive)
// return fmt.Errorf("%s: %s", description, err)
// }
}
}

for _, op := range OtherUnaryRandomizedOps2D3D {
log.Printf("begin evaluating %s\n", getFnName(op))
Expand Down Expand Up @@ -530,7 +577,7 @@ func test_union3D() error {
}

func test_polygongpu() error {
const Nvertices = 1600
const Nvertices = 13
var polybuilder ms2.PolygonBuilder
polybuilder.Nagon(Nvertices, 2)
vecs, err := polybuilder.AppendVecs(nil)
Expand Down Expand Up @@ -919,6 +966,16 @@ func cmpDist[T any](pos []T, dcpu, dgpu []float32) error {
return mismatchErr
}

func randomCircArray2D(a glbuild.Shader2D, rng *rand.Rand) glbuild.Shader2D {
circleDiv := rng.Intn(16) + 3
nInst := rng.Intn(circleDiv) + 1
s, err := gsdf.CircularArray2D(a, nInst, circleDiv)
if err != nil {
panic(err)
}
return s
}

func randomRotation(a glbuild.Shader3D, rng *rand.Rand) glbuild.Shader3D {
var axis ms3.Vec
for ms3.Norm(axis) < .5 {
Expand Down
87 changes: 84 additions & 3 deletions gsdf2d.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,12 @@ func NewRectangle(x, y float32) (glbuild.Shader2D, error) {
}

func (c *rect2D) Bounds() ms2.Box {
min := ms2.Scale(-0.5, c.d)
return ms2.Box{Min: min, Max: ms2.AbsElem(min)}
xd2 := c.d.X / 2
yd2 := c.d.Y / 2
return ms2.Box{
Min: ms2.Vec{X: -xd2, Y: -yd2},
Max: ms2.Vec{X: xd2, Y: yd2},
}
}

func (c *rect2D) AppendShaderName(b []byte) []byte {
Expand All @@ -298,7 +302,7 @@ func (c *rect2D) AppendShaderName(b []byte) []byte {
}

func (c *rect2D) AppendShaderBody(b []byte) []byte {
b = glbuild.AppendVec2Decl(b, "b", c.d)
b = glbuild.AppendVec2Decl(b, "b", ms2.Scale(0.5, c.d))
b = append(b, `vec2 d = abs(p)-b;
return length(max(d,0.0)) + min(max(d.x,d.y),0.0);`...)
return b
Expand Down Expand Up @@ -993,3 +997,80 @@ func (s *annulus2D) AppendShaderBody(b []byte) []byte {
b = append(b, "return abs(d)-r;"...)
return b
}

// CircularArray2 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) {
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 circleDiv")
}
return &circarray2D{s: s, circleDiv: circleDiv, nInst: numInstances}, nil
}

type circarray2D struct {
s glbuild.Shader2D
nInst int
circleDiv int
}

func (ca *circarray2D) Bounds() ms2.Box {
bb := ca.s.Bounds()
verts := bb.Vertices()
angle := 2 * math.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])
bb = bb.IncludePoint(verts[i])
}
}
return bb
}

func (ca *circarray2D) ForEach2DChild(userData any, fn func(userData any, s *glbuild.Shader2D) error) error {
return fn(userData, &ca.s)
}

// func (ca *circarray2D) angle() float32 { return 2 * math32.Pi / float32(ca.n) }

func (ca *circarray2D) AppendShaderName(b []byte) []byte {
b = append(b, "circarray2D2d"...)
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 *circarray2D) AppendShaderBody(b []byte) []byte {
angle := 2 * math.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;
float c1 = cos(angle*i1);
float s1 = sin(angle*i1);
vec2 p1 = mat2(c1,-s1,s1,c1)*p;
`...)
b = glbuild.AppendDistanceDecl(b, "d0", "p0", ca.s)
b = glbuild.AppendDistanceDecl(b, "d1", "p1", ca.s)
b = append(b, "return min(d0, d1);"...)
return b
}

0 comments on commit 3c6083e

Please sign in to comment.