gim: make rounder a global in geo, kinda gross but simplifies a lot of things

This commit is contained in:
Brian Picciano 2018-06-07 22:50:01 +00:00
parent ef48a2d708
commit 0b36e4ec37
9 changed files with 56 additions and 96 deletions

View File

@ -63,8 +63,8 @@ func (b box) draw(buf *terminal.Buffer) {
buf.DrawRect(rect, terminal.SingleLine) buf.DrawRect(rect, terminal.SingleLine)
if b.bodyBuf != nil { if b.bodyBuf != nil {
center := rect.Center(rounder) center := rect.Center()
bodyBufRect := geo.Rect{Size: b.bodyBuf.Size()} bodyBufRect := geo.Rect{Size: b.bodyBuf.Size()}
buf.DrawBuffer(bodyBufRect.Centered(center, rounder).TopLeft, b.bodyBuf) buf.DrawBuffer(bodyBufRect.Centered(center).TopLeft, b.bodyBuf)
} }
} }

View File

@ -59,9 +59,8 @@ func (xy XY) Unit() XY {
return xy return xy
} }
// Len returns the length (aka magnitude) of the XY as a vector, using the // Len returns the length (aka magnitude) of the XY as a vector.
// Rounder to round to an int func (xy XY) Len() int {
func (xy XY) Len(r Rounder) int {
if xy[0] == 0 { if xy[0] == 0 {
return abs(xy[1]) return abs(xy[1])
} else if xy[1] == 0 { } else if xy[1] == 0 {
@ -70,7 +69,7 @@ func (xy XY) Len(r Rounder) int {
xyf := xy.toF64() xyf := xy.toF64()
lf := math.Sqrt((xyf[0] * xyf[0]) + (xyf[1] * xyf[1])) lf := math.Sqrt((xyf[0] * xyf[0]) + (xyf[1] * xyf[1]))
return r.Round(lf) return Rounder.Round(lf)
} }
// Add returns the result of adding the two XYs' fields individually // Add returns the result of adding the two XYs' fields individually
@ -87,13 +86,12 @@ func (xy XY) Mul(xy2 XY) XY {
return xy return xy
} }
// Div returns the results of dividing the two XYs' field individually, using // Div returns the results of dividing the two XYs' field individually.
// the Rounder to resolve floating results func (xy XY) Div(xy2 XY) XY {
func (xy XY) Div(xy2 XY, r Rounder) XY {
xyf, xy2f := xy.toF64(), xy2.toF64() xyf, xy2f := xy.toF64(), xy2.toF64()
return XY{ return XY{
r.Round(xyf[0] / xy2f[0]), Rounder.Round(xyf[0] / xy2f[0]),
r.Round(xyf[1] / xy2f[1]), Rounder.Round(xyf[1] / xy2f[1]),
} }
} }
@ -113,10 +111,9 @@ func (xy XY) Sub(xy2 XY) XY {
return xy.Add(xy2.Inv()) return xy.Add(xy2.Inv())
} }
// Midpoint returns the midpoint between the two XYs. The rounder indicates what // Midpoint returns the midpoint between the two XYs.
// to do about non-whole values when they're come across func (xy XY) Midpoint(xy2 XY) XY {
func (xy XY) Midpoint(xy2 XY, r Rounder) XY { return xy.Add(xy2.Sub(xy).Div(XY{2, 2}))
return xy.Add(xy2.Sub(xy).Div(XY{2, 2}, r))
} }
// Min returns an XY whose fields are the minimum values of the two XYs' // Min returns an XY whose fields are the minimum values of the two XYs'

View File

@ -82,32 +82,30 @@ func (r Rect) Edge(dir, secDir XY) Edge {
} }
// Midpoint returns the point which is the midpoint of the Edge // Midpoint returns the point which is the midpoint of the Edge
func (e Edge) Midpoint(rounder Rounder) XY { func (e Edge) Midpoint() XY {
return e[0].Midpoint(e[1], rounder) return e[0].Midpoint(e[1])
} }
func (r Rect) halfSize(rounder Rounder) XY { func (r Rect) halfSize() XY {
return r.Size.Div(XY{2, 2}, rounder) return r.Size.Div(XY{2, 2})
} }
// Center returns the centerpoint of the rectangle, using the given Rounder to // Center returns the centerpoint of the rectangle.
// resolve non-integers func (r Rect) Center() XY {
func (r Rect) Center(rounder Rounder) XY { return r.TopLeft.Add(r.halfSize())
return r.TopLeft.Add(r.halfSize(rounder))
} }
// Translate returns an instance of Rect which is the same as this one but // Translate returns an instance of Rect which is the same as this one but
// translated by the given amount // translated by the given amount.
func (r Rect) Translate(by XY) Rect { func (r Rect) Translate(by XY) Rect {
r.TopLeft = r.TopLeft.Add(by) r.TopLeft = r.TopLeft.Add(by)
return r return r
} }
// Centered returns an instance of Rect which is this one but translated to be // Centered returns an instance of Rect which is this one but translated to be
// centered on the given point. It will use the given Rounder to resolve // centered on the given point.
// non-integers func (r Rect) Centered(on XY) Rect {
func (r Rect) Centered(on XY, rounder Rounder) Rect { r.TopLeft = on.Sub(r.halfSize())
r.TopLeft = on.Sub(r.halfSize(rounder))
return r return r
} }

View File

@ -38,30 +38,20 @@ func TestRect(t *T) {
} }
func TestRectCenter(t *T) { func TestRectCenter(t *T) {
assertCentered := func(exp, given Rect, center XY, rounder Rounder) { assertCentered := func(exp, given Rect, center XY) {
got := given.Centered(center, rounder) got := given.Centered(center)
assert.Equal(t, exp, got) assert.Equal(t, exp, got)
assert.Equal(t, center, got.Center(rounder)) assert.Equal(t, center, got.Center())
} }
{ {
r := Rect{ r := Rect{
Size: XY{4, 4}, Size: XY{4, 4},
} }
assert.Equal(t, XY{2, 2}, r.Center(Round)) assert.Equal(t, XY{2, 2}, r.Center())
assert.Equal(t, XY{2, 2}, r.Center(Floor))
assert.Equal(t, XY{2, 2}, r.Center(Ceil))
assertCentered( assertCentered(
Rect{TopLeft: XY{1, 1}, Size: XY{4, 4}}, Rect{TopLeft: XY{1, 1}, Size: XY{4, 4}},
r, XY{3, 3}, Round, r, XY{3, 3},
)
assertCentered(
Rect{TopLeft: XY{1, 1}, Size: XY{4, 4}},
r, XY{3, 3}, Floor,
)
assertCentered(
Rect{TopLeft: XY{1, 1}, Size: XY{4, 4}},
r, XY{3, 3}, Ceil,
) )
} }
@ -69,20 +59,10 @@ func TestRectCenter(t *T) {
r := Rect{ r := Rect{
Size: XY{5, 5}, Size: XY{5, 5},
} }
assert.Equal(t, XY{3, 3}, r.Center(Round)) assert.Equal(t, XY{3, 3}, r.Center())
assert.Equal(t, XY{2, 2}, r.Center(Floor))
assert.Equal(t, XY{3, 3}, r.Center(Ceil))
assertCentered( assertCentered(
Rect{TopLeft: XY{0, 0}, Size: XY{5, 5}}, Rect{TopLeft: XY{0, 0}, Size: XY{5, 5}},
r, XY{3, 3}, Round, r, XY{3, 3},
)
assertCentered(
Rect{TopLeft: XY{1, 1}, Size: XY{5, 5}},
r, XY{3, 3}, Floor,
)
assertCentered(
Rect{TopLeft: XY{0, 0}, Size: XY{5, 5}},
r, XY{3, 3}, Ceil,
) )
} }
} }

View File

@ -1,44 +1,33 @@
package geo package geo
import ( import (
"fmt"
"math" "math"
) )
// Rounder describes how a floating point number should be converted to an int // RounderFunc is a function which converts a floating point number into an
type Rounder int // integer.
type RounderFunc func(float64) int64
const ( // Round is helper for calling the RounderFunc and converting the result to an
// Round will round up or down depending on the number itself // int.
Round Rounder = iota func (rf RounderFunc) Round(f float64) int {
return int(rf(f))
}
// Floor will use the math.Floor function // A few RounderFuncs which can be used. Set the Rounder global variable to pick
Floor // one.
var (
// Ceil will use the math.Ceil function Floor RounderFunc = func(f float64) int64 { return int64(math.Floor(f)) }
Ceil Ceil RounderFunc = func(f float64) int64 { return int64(math.Ceil(f)) }
) Round RounderFunc = func(f float64) int64 {
// Round64 converts a float to an in64 based on the rounding function indicated
// by the Rounder's value
func (r Rounder) Round64(f float64) int64 {
switch r {
case Round:
if f < 0 { if f < 0 {
f = math.Ceil(f - 0.5) f = math.Ceil(f - 0.5)
} }
f = math.Floor(f + 0.5) f = math.Floor(f + 0.5)
case Floor:
f = math.Floor(f)
case Ceil:
f = math.Ceil(f)
default:
panic(fmt.Sprintf("invalid Rounder: %#v", r))
}
return int64(f) return int64(f)
} }
)
// Round is like Round64 but convers the int64 to an int // Rounder is the RounderFunc which will be used by all functions and methods in
func (r Rounder) Round(f float64) int { // this package when needed.
return int(r.Round64(f)) var Rounder = Ceil
}

View File

@ -26,8 +26,8 @@ func (l line) draw(buf *terminal.Buffer, flowDir, secFlowDir geo.XY) {
// draw the body // draw the body
if l.bodyBuf != nil { if l.bodyBuf != nil {
mid := start.Midpoint(end, rounder) mid := start.Midpoint(end)
bodyBufRect := geo.Rect{Size: l.bodyBuf.Size()} bodyBufRect := geo.Rect{Size: l.bodyBuf.Size()}
buf.DrawBuffer(bodyBufRect.Centered(mid, rounder).TopLeft, l.bodyBuf) buf.DrawBuffer(bodyBufRect.Centered(mid).TopLeft, l.bodyBuf)
} }
} }

View File

@ -23,7 +23,6 @@ import (
const ( const (
framerate = 10 framerate = 10
frameperiod = time.Second / time.Duration(framerate) frameperiod = time.Second / time.Duration(framerate)
rounder = geo.Ceil
) )
func debugf(str string, args ...interface{}) { func debugf(str string, args ...interface{}) {
@ -81,7 +80,7 @@ func main() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
term := terminal.New() term := terminal.New()
wSize := term.WindowSize() wSize := term.WindowSize()
center := geo.Zero.Midpoint(wSize, rounder) center := geo.Zero.Midpoint(wSize)
g, start := mkGraph() g, start := mkGraph()
v := view{ v := view{
@ -93,7 +92,7 @@ func main() {
buf := terminal.NewBuffer() buf := terminal.NewBuffer()
v.draw(buf) v.draw(buf)
bufRect := geo.Rect{Size: buf.Size()}.Centered(center, rounder) bufRect := geo.Rect{Size: buf.Size()}.Centered(center)
term.Clear() term.Clear()
term.WriteBuffer(bufRect.TopLeft, buf) term.WriteBuffer(bufRect.TopLeft, buf)

View File

@ -144,10 +144,7 @@ func (b *Buffer) DrawLine(start, end, dir geo.XY, ls LineStyle) {
var perpDir geo.XY var perpDir geo.XY
perpDir[0], perpDir[1] = dir[1], dir[0] perpDir[0], perpDir[1] = dir[1], dir[0]
dirSec := end.Sub(start).Mul(perpDir.Abs()).Unit() dirSec := end.Sub(start).Mul(perpDir.Abs()).Unit()
mid := start.Midpoint(end)
// TODO gross that this doesn't have some way of discovering the rounder.
// Maybe rounder should just be a global? ugh...
mid := start.Midpoint(end, geo.Round)
along := func(xy, dir geo.XY) int { along := func(xy, dir geo.XY) int {
if dir[0] != 0 { if dir[0] != 0 {

View File

@ -120,8 +120,8 @@ func (view *view) draw(buf *terminal.Buffer) {
boxesMr[v] = &b boxesMr[v] = &b
bSize := b.rect().Size bSize := b.rect().Size
primBoxLen := bSize.Mul(view.primFlowDir).Len(rounder) primBoxLen := bSize.Mul(view.primFlowDir).Len()
secBoxLen := bSize.Mul(view.secFlowDir).Len(rounder) secBoxLen := bSize.Mul(view.secFlowDir).Len()
if primBoxLen > maxPrim { if primBoxLen > maxPrim {
maxPrim = primBoxLen maxPrim = primBoxLen
} }