diff --git a/README.md b/README.md index fcecebe..95f58b0 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,10 @@ Here is a rendered bolt from one of the unit tests under [form3_test.go](./rende ![renderedBolt](./render/testdata/defactoBolt.png) ## Roadmap +- [x] Clean up thread API mess - [ ] Add a 2D renderer and it's respective `Renderer2` interface. - [ ] Make 3D renderer multicore -- [ ] Clean up thread API mess + ## Comparison diff --git a/examples/atx-bench-supply/atx.go b/examples/atx-bench-supply/atx.go index 3488e46..d0dd616 100644 --- a/examples/atx-bench-supply/atx.go +++ b/examples/atx-bench-supply/atx.go @@ -52,28 +52,28 @@ func main() { var outputs, regOut, regBlock sdf.SDF2 // Outputs banana plugs outputs = sdf.Array2D(bananaPlugBig, sdf.V2i{4, 2}, r2.Vec{-bananaSpacing, bananaSpacing}) - outputs = sdf.Transform2D(outputs, sdf.Translate2d(r2.Vec{atxW/2 - 15, -atxH/2 + 15})) + outputs = sdf.Transform2D(outputs, sdf.Translate2D(r2.Vec{atxW/2 - 15, -atxH/2 + 15})) - panel = sdf.Difference2D(panel, sdf.Transform2D(onButton, sdf.Translate2d(r2.Vec{atxW/2 - 25, atxH/2 - 15}))) + panel = sdf.Difference2D(panel, sdf.Transform2D(onButton, sdf.Translate2D(r2.Vec{atxW/2 - 25, atxH/2 - 15}))) panel = sdf.Difference2D(panel, outputs) // Begin working on regulated step-down block regOut = sdf.Array2D(bananaPlugSmall, sdf.V2i{2, 1}, r2.Vec{bananaSpacing, bananaSpacing}) bplugX := bbSize(regOut.Bounds()).X - vDisp := sdf.Transform2D(voltageDisplay, sdf.Translate2d(r2.Vec{bplugX / 2, vDispH/2 + bananaSpacing/2})) + vDisp := sdf.Transform2D(voltageDisplay, sdf.Translate2D(r2.Vec{bplugX / 2, vDispH/2 + bananaSpacing/2})) regOut = sdf.Union2D(regOut, vDisp) - regOut = sdf.Transform2D(regOut, sdf.Translate2d(r2.Vec{-atxW/2 - bplugX/2 + vDispW/2 + 12, atxH/2 - 12 - vDispH/2 - bananaSpacing})) + regOut = sdf.Transform2D(regOut, sdf.Translate2D(r2.Vec{-atxW/2 - bplugX/2 + vDispW/2 + 12, atxH/2 - 12 - vDispH/2 - bananaSpacing})) // Create mound for step up outputs. regSz := bbSize(regOut.Bounds()) regBlock = form2.Box(r2.Vec{regSz.X + regBlockMargin, regSz.Y + regBlockMargin}, regBlockMargin/2) - regBlock = sdf.Transform2D(regBlock, sdf.Translate2d(bbCenter(regOut.Bounds()))) + regBlock = sdf.Transform2D(regBlock, sdf.Translate2D(bbCenter(regOut.Bounds()))) regBlock = sdf.Difference2D(regBlock, regOut) regBlock3 := sdf.Extrude3D(regBlock, panelThickness+regBlockDepth) // extrude does it both ways. regBlock3 = sdf.Transform3D(regBlock3, sdf.Translate3D(r3.Vec{0, 0, regBlockDepth / 2})) panel = sdf.Difference2D(panel, regOut) // Speaker clamps - scHole := sdf.Transform2D(speakerClamps, sdf.Translate2d(r2.Vec{-atxW/2 + spkClampW/2 + 12, -atxH/2 + spkClampH/2 + 12})) + scHole := sdf.Transform2D(speakerClamps, sdf.Translate2D(r2.Vec{-atxW/2 + spkClampW/2 + 12, -atxH/2 + spkClampH/2 + 12})) panel = sdf.Difference2D(panel, scHole) // Generate model diff --git a/matrix.go b/matrix.go index c3bbc4a..f17b1f9 100644 --- a/matrix.go +++ b/matrix.go @@ -114,8 +114,8 @@ func Translate3D(v r3.Vec) m44 { 0, 0, 0, 1} } -// Translate2d returns a 3x3 translation matrix. -func Translate2d(v r2.Vec) m33 { +// Translate2D returns a 3x3 translation matrix. +func Translate2D(v r2.Vec) m33 { return m33{ 1, 0, v.X, 0, 1, v.Y, @@ -284,14 +284,14 @@ func (a m22) equals(b m22, tolerance float64) bool { // MulPosition multiplies a V2 position with a rotate/translate matrix. func (a m33) MulPosition(b r2.Vec) r2.Vec { - return r2.Vec{a.x00*b.X + a.x01*b.Y + a.x02, - a.x10*b.X + a.x11*b.Y + a.x12} + return r2.Vec{X: a.x00*b.X + a.x01*b.Y + a.x02, + Y: a.x10*b.X + a.x11*b.Y + a.x12} } // MulPosition multiplies a V2 position with a rotate matrix. func (a m22) MulPosition(b r2.Vec) r2.Vec { - return r2.Vec{a.x00*b.X + a.x01*b.Y, - a.x10*b.X + a.x11*b.Y} + return r2.Vec{X: a.x00*b.X + a.x01*b.Y, + Y: a.x10*b.X + a.x11*b.Y} } // MulVertices multiples a set of V2 vertices by a rotate/translate matrix. @@ -414,14 +414,14 @@ func (a m44) MulBox(box r3.Box) r3.Box { za, zb = d3.MinElem(za, zb), d3.MaxElem(za, zb) min := xa.Add(ya).Add(za).Add(t) max := xb.Add(yb).Add(zb).Add(t) - return r3.Box{min, max} + return r3.Box{Min: min, Max: max} } // MulBox rotates/translates a 2d bounding box and resizes for axis-alignment. func (a m33) MulBox(box r2.Box) r2.Box { - r := r2.Vec{a.x00, a.x10} - u := r2.Vec{a.x01, a.x11} - t := r2.Vec{a.x02, a.x12} + r := r2.Vec{X: a.x00, Y: a.x10} + u := r2.Vec{X: a.x01, Y: a.x11} + t := r2.Vec{X: a.x02, Y: a.x12} xa := r2.Scale(box.Min.X, r) xb := r2.Scale(box.Max.X, r) ya := r2.Scale(box.Min.Y, u) @@ -430,7 +430,7 @@ func (a m33) MulBox(box r2.Box) r2.Box { ya, yb = d2.MinElem(ya, yb), d2.MaxElem(ya, yb) min := xa.Add(ya).Add(t) max := xb.Add(yb).Add(t) - return r2.Box{min, max} + return r2.Box{Min: min, Max: max} } // Determinant returns the determinant of a 4x4 matrix. diff --git a/sdf2.go b/sdf2.go index 86c4f4f..a8795a7 100644 --- a/sdf2.go +++ b/sdf2.go @@ -68,7 +68,7 @@ func Cut2D(sdf SDF2, a, v r2.Vec) SDF2 { s.sdf = sdf s.a = a v = r2.Unit(v) - s.n = r2.Vec{-v.Y, v.X} + s.n = r2.Vec{X: -v.Y, Y: v.X} // TODO - cut the bounding box s.bb = sdf.Bounds() return &s @@ -127,7 +127,7 @@ type ScaleUniformSDF2 struct { // ScaleUniform2D scales an SDF2 by k on each axis. // Distance is correct with scaling. func ScaleUniform2D(sdf SDF2, k float64) SDF2 { - m := Scale2d(r2.Vec{k, k}) + m := Scale2d(r2.Vec{X: k, Y: k}) return &ScaleUniformSDF2{ sdf: sdf, k: k, @@ -150,14 +150,14 @@ func (s *ScaleUniformSDF2) Bounds() r2.Box { // Center2D centers the origin of an SDF2 on it's bounding box. func Center2D(s SDF2) SDF2 { ofs := r2.Scale(-1, d2.Box(s.Bounds()).Center()) - return Transform2D(s, Translate2d(ofs)) + return Transform2D(s, Translate2D(ofs)) } // CenterAndScale2D centers the origin of an SDF2 on it's bounding box, and then scales it. // Distance is correct with scaling. func CenterAndScale2D(s SDF2, k float64) SDF2 { ofs := r2.Scale(-1, d2.Box(s.Bounds()).Center()) - s = Transform2D(s, Translate2d(ofs)) + s = Transform2D(s, Translate2D(ofs)) return ScaleUniform2D(s, k) } @@ -201,7 +201,7 @@ func (s *array2) Evaluate(p r2.Vec) float64 { d := math.MaxFloat64 for j := 0; j < s.num[0]; j++ { for k := 0; k < s.num[1]; k++ { - x := p.Sub(r2.Vec{float64(j) * s.step.X, float64(k) * s.step.Y}) + x := p.Sub(r2.Vec{X: float64(j) * s.step.X, Y: float64(k) * s.step.Y}) d = s.min(d, s.sdf.Evaluate(x)) } } @@ -243,7 +243,7 @@ func RotateUnion2D(sdf SDF2, num int, step m33) SDF2 { bbMax = d2.MaxElem(bbMax, vset.Max()) MulVertices2(vset, step) } - s.bb = r2.Box{bbMin, bbMax} + s.bb = r2.Box{Min: bbMin, Max: bbMax} return &s } @@ -295,7 +295,8 @@ func RotateCopy2D(sdf SDF2, n int) SDF2 { rmax = l } } - s.bb = r2.Box{r2.Vec{-rmax, -rmax}, r2.Vec{rmax, rmax}} + max := r2.Vec{X: rmax, Y: rmax} + s.bb = r2.Box{Min: r2.Scale(-1, max), Max: max} return &s } @@ -351,9 +352,9 @@ func Slice2D(sdf SDF3, a, n r3.Vec) SDF2 { va := v.Sub(s.a) pa := va.Sub(r3.Scale(r3.Dot(n, va), n)) // work out the 3d point in terms of the 2d unit vectors - vec[i] = r2.Vec{pa.Dot(s.u), pa.Dot(s.v)} + vec[i] = r2.Vec{X: pa.Dot(s.u), Y: pa.Dot(s.v)} } - s.bb = r2.Box{vec.Min(), vec.Max()} + s.bb = r2.Box{Min: vec.Min(), Max: vec.Max()} return &s } @@ -558,7 +559,7 @@ func LineOf2D(s SDF2, p0, p1 r2.Vec, pattern string) SDF2 { dx = r2.Scale(1/float64(len(pattern)), dx) for _, c := range pattern { if c == 'x' { - objects = append(objects, Transform2D(s, Translate2d(x))) + objects = append(objects, Transform2D(s, Translate2D(x))) } x = x.Add(dx) } @@ -579,7 +580,7 @@ func Multi2D(s SDF2, positions d2.Set) SDF2 { } objects := make([]SDF2, len(positions)) for i, p := range positions { - objects[i] = Transform2D(s, Translate2d(p)) + objects[i] = Transform2D(s, Translate2D(p)) } return Union2D(objects...) } diff --git a/sdf3.go b/sdf3.go index cce01c3..5cacef9 100644 --- a/sdf3.go +++ b/sdf3.go @@ -61,39 +61,39 @@ func Revolve3D(sdf SDF2, theta float64) SDF3 { sin := math.Sin(s.theta) cos := math.Cos(s.theta) // pre-calculate the normal to the theta line - s.norm = r2.Vec{-sin, cos} + s.norm = r2.Vec{X: -sin, Y: cos} // work out the bounding box var vset d2.Set if theta == 0 { - vset = []r2.Vec{{1, 1}, {-1, -1}} + vset = []r2.Vec{{X: 1, Y: 1}, {X: -1, Y: -1}} } else { - vset = []r2.Vec{{0, 0}, {1, 0}, {cos, sin}} + vset = []r2.Vec{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: cos, Y: sin}} if s.theta > 0.5*pi { - vset = append(vset, r2.Vec{0, 1}) + vset = append(vset, r2.Vec{X: 0, Y: 1}) } if s.theta > pi { - vset = append(vset, r2.Vec{-1, 0}) + vset = append(vset, r2.Vec{X: -1, Y: 0}) } if s.theta > 1.5*pi { - vset = append(vset, r2.Vec{0, -1}) + vset = append(vset, r2.Vec{X: 0, Y: -1}) } } bb := s.sdf.Bounds() l := math.Max(math.Abs(bb.Min.X), math.Abs(bb.Max.X)) vmin := r2.Scale(l, vset.Min()) vmax := r2.Scale(l, vset.Max()) - s.bb = r3.Box{r3.Vec{vmin.X, vmin.Y, bb.Min.Y}, r3.Vec{vmax.X, vmax.Y, bb.Max.Y}} + s.bb = r3.Box{Min: r3.Vec{X: vmin.X, Y: vmin.Y, Z: bb.Min.Y}, Max: r3.Vec{X: vmax.X, Y: vmax.Y, Z: bb.Max.Y}} return &s } // Evaluate returns the minimum distance to a solid of revolution. func (s *revolution3) Evaluate(p r3.Vec) float64 { x := math.Sqrt(p.X*p.X + p.Y*p.Y) - a := s.sdf.Evaluate(r2.Vec{x, p.Z}) + a := s.sdf.Evaluate(r2.Vec{X: x, Y: p.Z}) b := a if s.theta != 0 { // combine two vertical planes to give an intersection wedge - d := s.norm.Dot(r2.Vec{p.X, p.Y}) + d := s.norm.Dot(r2.Vec{X: p.X, Y: p.Y}) if s.theta < pi { b = math.Max(-p.Y, d) // intersect } else { @@ -125,7 +125,7 @@ func Extrude3D(sdf SDF2, height float64) SDF3 { s.extrude = NormalExtrude // work out the bounding box bb := sdf.Bounds() - s.bb = r3.Box{r3.Vec{bb.Min.X, bb.Min.Y, -s.height}, r3.Vec{bb.Max.X, bb.Max.Y, s.height}} + s.bb = r3.Box{Min: r3.Vec{X: bb.Min.X, Y: bb.Min.Y, Z: -s.height}, Max: r3.Vec{X: bb.Max.X, Y: bb.Max.Y, Z: s.height}} return &s } @@ -138,7 +138,7 @@ func TwistExtrude3D(sdf SDF2, height, twist float64) SDF3 { // work out the bounding box bb := sdf.Bounds() l := r2.Norm(bb.Max) - s.bb = r3.Box{r3.Vec{-l, -l, -s.height}, r3.Vec{l, l, s.height}} + s.bb = r3.Box{Min: r3.Vec{X: -l, Y: -l, Z: -s.height}, Max: r3.Vec{X: l, Y: l, Z: s.height}} return &s } @@ -150,8 +150,8 @@ func ScaleExtrude3D(sdf SDF2, height float64, scale r2.Vec) SDF3 { s.extrude = ScaleExtrude(height, scale) // work out the bounding box bb := d2.Box(sdf.Bounds()) - bb = bb.Extend(d2.Box{d2.MulElem(bb.Min, scale), d2.MulElem(bb.Max, scale)}) - s.bb = r3.Box{r3.Vec{bb.Min.X, bb.Min.Y, -s.height}, r3.Vec{bb.Max.X, bb.Max.Y, s.height}} + bb = bb.Extend(d2.Box{Min: d2.MulElem(bb.Min, scale), Max: d2.MulElem(bb.Max, scale)}) + s.bb = r3.Box{Min: r3.Vec{X: bb.Min.X, Y: bb.Min.Y, Z: -s.height}, Max: r3.Vec{X: bb.Max.X, Y: bb.Max.Y, Z: s.height}} return &s } @@ -163,9 +163,9 @@ func ScaleTwistExtrude3D(sdf SDF2, height, twist float64, scale r2.Vec) SDF3 { s.extrude = ScaleTwistExtrude(height, twist, scale) // work out the bounding box bb := d2.Box(sdf.Bounds()) - bb = bb.Extend(d2.Box{d2.MulElem(bb.Min, scale), d2.MulElem(bb.Max, scale)}) + bb = bb.Extend(d2.Box{Min: d2.MulElem(bb.Min, scale), Max: d2.MulElem(bb.Max, scale)}) l := r2.Norm(bb.Max) - s.bb = r3.Box{r3.Vec{-l, -l, -s.height}, r3.Vec{l, l, s.height}} + s.bb = r3.Box{Min: r3.Vec{X: -l, Y: -l, Z: -s.height}, Max: r3.Vec{X: l, Y: l, Z: s.height}} return &s } @@ -223,8 +223,8 @@ func ExtrudeRounded3D(sdf SDF2, height, round float64) SDF3 { // work out the bounding box bb := sdf.Bounds() s.bb = r3.Box{ - Min: r3.Sub(r3.Vec{bb.Min.X, bb.Min.Y, -s.height}, d3.Elem(round)), - Max: r3.Add(r3.Vec{bb.Max.X, bb.Max.Y, s.height}, d3.Elem(round)), + Min: r3.Sub(r3.Vec{X: bb.Min.X, Y: bb.Min.Y, Z: -s.height}, d3.Elem(round)), + Max: r3.Add(r3.Vec{X: bb.Max.X, Y: bb.Max.Y, Z: s.height}, d3.Elem(round)), } return &s } @@ -232,7 +232,7 @@ func ExtrudeRounded3D(sdf SDF2, height, round float64) SDF3 { // Evaluate returns the minimum distance to a rounded extrusion. func (s *extrudeRounded) Evaluate(p r3.Vec) float64 { // sdf for the projected 2d surface - a := s.sdf.Evaluate(r2.Vec{p.X, p.Y}) + a := s.sdf.Evaluate(r2.Vec{X: p.X, Y: p.Y}) b := math.Abs(p.Z) - s.height var d float64 if b > 0 { @@ -296,8 +296,8 @@ func Loft3D(sdf0, sdf1 SDF2, height, round float64) SDF3 { bb1 := d2.Box(sdf1.Bounds()) bb := bb0.Extend(bb1) s.bb = r3.Box{ - Min: r3.Sub(r3.Vec{bb.Min.X, bb.Min.Y, -s.height}, d3.Elem(round)), - Max: r3.Add(r3.Vec{bb.Max.X, bb.Max.Y, s.height}, d3.Elem(round))} + Min: r3.Sub(r3.Vec{X: bb.Min.X, Y: bb.Min.Y, Z: -s.height}, d3.Elem(round)), + Max: r3.Add(r3.Vec{X: bb.Max.X, Y: bb.Max.Y, Z: s.height}, d3.Elem(round))} return &s } @@ -306,8 +306,8 @@ func (s *loft3) Evaluate(p r3.Vec) float64 { // work out the mix value as a function of height k := clamp((0.5*p.Z/s.height)+0.5, 0, 1) // mix the 2D SDFs - a0 := s.sdf0.Evaluate(r2.Vec{p.X, p.Y}) - a1 := s.sdf1.Evaluate(r2.Vec{p.X, p.Y}) + a0 := s.sdf0.Evaluate(r2.Vec{X: p.X, Y: p.Y}) + a1 := s.sdf1.Evaluate(r2.Vec{X: p.X, Y: p.Y}) a := mix(a0, a1, k) b := math.Abs(p.Z) - s.height @@ -384,7 +384,7 @@ type scaleUniform3 struct { // ScaleUniform3D uniformly scales an SDF3 on all axes. func ScaleUniform3D(sdf SDF3, k float64) SDF3 { - m := Scale3d(r3.Vec{k, k, k}) + m := Scale3d(r3.Vec{X: k, Y: k, Z: k}) return &scaleUniform3{ sdf: sdf, k: k, @@ -637,7 +637,7 @@ func (s *array3) Evaluate(p r3.Vec) float64 { for j := 0; j < s.num[0]; j++ { for k := 0; k < s.num[1]; k++ { for l := 0; l < s.num[2]; l++ { - x := p.Sub(r3.Vec{float64(j) * s.step.X, float64(k) * s.step.Y, float64(l) * s.step.Z}) + x := p.Sub(r3.Vec{X: float64(j) * s.step.X, Y: float64(k) * s.step.Y, Z: float64(l) * s.step.Z}) d = s.min(d, s.sdf.Evaluate(x)) } } @@ -681,7 +681,7 @@ func RotateUnion3D(sdf SDF3, num int, step m44) SDF3Union { mulVertices3(v, step) // v.MulVertices(step) } - s.bb = r3.Box{bbMin, bbMax} + s.bb = r3.Box{Min: bbMin, Max: bbMax} return &s } @@ -737,16 +737,16 @@ func RotateCopy3D(sdf SDF3, num int) SDF3 { rmax = l } } - s.bb = r3.Box{r3.Vec{-rmax, -rmax, zmin}, r3.Vec{rmax, rmax, zmax}} + s.bb = r3.Box{Min: r3.Vec{X: -rmax, Y: -rmax, Z: zmin}, Max: r3.Vec{X: rmax, Y: rmax, Z: zmax}} return &s } // Evaluate returns the minimum distance to a rotate/copy SDF3. func (s *rotateCopy3) Evaluate(p r3.Vec) float64 { // Map p to a point in the first copy sector. - p2 := r2.Vec{p.X, p.Y} + p2 := r2.Vec{X: p.X, Y: p.Y} p2 = d2.PolarToXY(r2.Norm(p2), sawTooth(math.Atan2(p2.Y, p2.X), s.theta)) - return s.sdf.Evaluate(r3.Vec{p2.X, p2.Y, p.Z}) + return s.sdf.Evaluate(r3.Vec{X: p2.X, Y: p2.Y, Z: p.Z}) } // BoundingBox returns the bounding box of a rotate/copy SDF3. @@ -842,7 +842,7 @@ func Shell3D(sdf SDF3, thickness float64) SDF3 { return &shell3{ sdf: sdf, delta: 0.5 * thickness, - bb: r3.Box(bb.Enlarge(r3.Vec{thickness, thickness, thickness})), + bb: r3.Box(bb.Enlarge(r3.Vec{X: thickness, Y: thickness, Z: thickness})), } } diff --git a/utils.go b/utils.go index 1ccb1e1..4b710b9 100644 --- a/utils.go +++ b/utils.go @@ -49,14 +49,14 @@ func R3ToI(a r3.Vec) V3i { // //Deprecated: R3FromI is deprecated. func R3FromI(a V3i) r3.Vec { - return r3.Vec{float64(a[0]), float64(a[1]), float64(a[2])} + return r3.Vec{X: float64(a[0]), Y: float64(a[1]), Z: float64(a[2])} } // R2FromI temporary home for this function. // // Deprecated: R2FromI is deprecated. func R2FromI(a V2i) r2.Vec { - return r2.Vec{float64(a[0]), float64(a[1])} + return r2.Vec{X: float64(a[0]), Y: float64(a[1])} } // clamp x between a and b, assume a <= b @@ -96,7 +96,7 @@ func sawTooth(x, period float64) float64 { // MinRound returns a minimum function that uses a quarter-circle to join the two objects smoothly. func MinRound(k float64) MinFunc { return func(a, b float64) float64 { - u := d2.MaxElem(r2.Vec{k - a, k - b}, r2.Vec{0, 0}) + u := d2.MaxElem(r2.Vec{X: k - a, Y: k - b}, r2.Vec{X: 0, Y: 0}) return math.Max(k, math.Min(a, b)) - r2.Norm(u) } } @@ -205,7 +205,7 @@ type ExtrudeFunc func(p r3.Vec) r2.Vec // NormalExtrude returns an extrusion function. func NormalExtrude(p r3.Vec) r2.Vec { - return r2.Vec{p.X, p.Y} + return r2.Vec{X: p.X, Y: p.Y} } // TwistExtrude returns an extrusion function that twists with z. @@ -213,34 +213,34 @@ func TwistExtrude(height, twist float64) ExtrudeFunc { k := twist / height return func(p r3.Vec) r2.Vec { m := Rotate(p.Z * k) - return m.MulPosition(r2.Vec{p.X, p.Y}) + return m.MulPosition(r2.Vec{X: p.X, Y: p.Y}) } } // ScaleExtrude returns an extrusion functions that scales with z. func ScaleExtrude(height float64, scale r2.Vec) ExtrudeFunc { - inv := r2.Vec{1 / scale.X, 1 / scale.Y} + inv := r2.Vec{X: 1 / scale.X, Y: 1 / scale.Y} // TODO verify - m := d2.DivElem(inv.Sub(r2.Vec{1, 1}), d2.Elem(height)) // slope + m := d2.DivElem(inv.Sub(r2.Vec{X: 1, Y: 1}), d2.Elem(height)) // slope b := r2.Add(d2.DivElem(inv, d2.Elem(2)), d2.Elem(0.5)) // b := inv.DivScalar(2).AddScalar(0.5) // intercept return func(p r3.Vec) r2.Vec { - return d2.MulElem(r2.Vec{p.X, p.Y}, r2.Scale(p.Z, m).Add(b)) + return d2.MulElem(r2.Vec{X: p.X, Y: p.Y}, r2.Scale(p.Z, m).Add(b)) } } // ScaleTwistExtrude returns an extrusion function that scales and twists with z. func ScaleTwistExtrude(height, twist float64, scale r2.Vec) ExtrudeFunc { k := twist / height - inv := r2.Vec{1 / scale.X, 1 / scale.Y} - m := r2.Sub(inv, d2.DivElem(r2.Vec{1, 1}, d2.Elem(height))) // slope + inv := r2.Vec{X: 1 / scale.X, Y: 1 / scale.Y} + m := r2.Sub(inv, d2.DivElem(r2.Vec{X: 1, Y: 1}, d2.Elem(height))) // slope // m := inv.Sub(r2.Vec{1, 1}).DivScalar(height) // slope b := r2.Add(d2.DivElem(inv, d2.Elem(2)), d2.Elem(0.5)) // b := inv.DivScalar(2).AddScalar(0.5) // intercept return func(p r3.Vec) r2.Vec { // Scale and then Twist // pnew := r2.Vec{p.X, p.Y}.Mul(m.MulScalar(p.Z).Add(b)) // Scale - pnew := d2.MulElem(r2.Vec{p.X, p.Y}, r2.Add(r2.Scale(p.Z, m), b)) + pnew := d2.MulElem(r2.Vec{X: p.X, Y: p.Y}, r2.Add(r2.Scale(p.Z, m), b)) return Rotate(p.Z * k).MulPosition(pnew) // Twist // Twist and then scale @@ -295,8 +295,8 @@ func raycast3(s SDF3, from, dir r3.Vec, scaleAndSigmoid, stepScale, epsilon, max // raycast2 see Raycast3. NOTE: implementation using Raycast3 (inefficient?) func raycast2(s SDF2, from, dir r2.Vec, scaleAndSigmoid, stepScale, epsilon, maxDist float64, maxSteps int) (r2.Vec, float64, int) { - collision, t, steps := raycast3(Extrude3D(s, 1), r3.Vec{from.X, from.Y, 0}, r3.Vec{dir.X, dir.Y, 0}, scaleAndSigmoid, stepScale, epsilon, maxDist, maxSteps) - return r2.Vec{collision.X, collision.Y}, t, steps + collision, t, steps := raycast3(Extrude3D(s, 1), r3.Vec{X: from.X, Y: from.Y, Z: 0}, r3.Vec{X: dir.X, Y: dir.Y, Z: 0}, scaleAndSigmoid, stepScale, epsilon, maxDist, maxSteps) + return r2.Vec{X: collision.X, Y: collision.Y}, t, steps } // Normals diff --git a/veci.go b/veci.go index 1868fdf..b378750 100644 --- a/veci.go +++ b/veci.go @@ -32,7 +32,7 @@ func (a V3i) AddScalar(b int) V3i { // Tor3.Vec converts V3i (integer) to r3.Vec (float). func (a V3i) ToV3() r3.Vec { - return r3.Vec{float64(a[0]), float64(a[1]), float64(a[2])} + return r3.Vec{X: float64(a[0]), Y: float64(a[1]), Z: float64(a[2])} } // Add adds two vectors. Return v = a + b.