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

viewRender
Brian Picciano 6 years ago
parent ef48a2d708
commit 0b36e4ec37
  1. 4
      gim/box.go
  2. 23
      gim/geo/geo.go
  3. 24
      gim/geo/rect.go
  4. 34
      gim/geo/rect_test.go
  5. 49
      gim/geo/round.go
  6. 4
      gim/line.go
  7. 5
      gim/main.go
  8. 5
      gim/terminal/shape.go
  9. 4
      gim/view.go

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

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

@ -82,32 +82,30 @@ func (r Rect) Edge(dir, secDir XY) Edge {
}
// Midpoint returns the point which is the midpoint of the Edge
func (e Edge) Midpoint(rounder Rounder) XY {
return e[0].Midpoint(e[1], rounder)
func (e Edge) Midpoint() XY {
return e[0].Midpoint(e[1])
}
func (r Rect) halfSize(rounder Rounder) XY {
return r.Size.Div(XY{2, 2}, rounder)
func (r Rect) halfSize() XY {
return r.Size.Div(XY{2, 2})
}
// Center returns the centerpoint of the rectangle, using the given Rounder to
// resolve non-integers
func (r Rect) Center(rounder Rounder) XY {
return r.TopLeft.Add(r.halfSize(rounder))
// Center returns the centerpoint of the rectangle.
func (r Rect) Center() XY {
return r.TopLeft.Add(r.halfSize())
}
// 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 {
r.TopLeft = r.TopLeft.Add(by)
return r
}
// 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
// non-integers
func (r Rect) Centered(on XY, rounder Rounder) Rect {
r.TopLeft = on.Sub(r.halfSize(rounder))
// centered on the given point.
func (r Rect) Centered(on XY) Rect {
r.TopLeft = on.Sub(r.halfSize())
return r
}

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

@ -1,44 +1,33 @@
package geo
import (
"fmt"
"math"
)
// Rounder describes how a floating point number should be converted to an int
type Rounder int
// RounderFunc is a function which converts a floating point number into an
// integer.
type RounderFunc func(float64) int64
const (
// Round will round up or down depending on the number itself
Round Rounder = iota
// Floor will use the math.Floor function
Floor
// Ceil will use the math.Ceil function
Ceil
)
// Round is helper for calling the RounderFunc and converting the result to an
// int.
func (rf RounderFunc) Round(f float64) int {
return int(rf(f))
}
// 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:
// A few RounderFuncs which can be used. Set the Rounder global variable to pick
// one.
var (
Floor RounderFunc = func(f float64) int64 { return int64(math.Floor(f)) }
Ceil RounderFunc = func(f float64) int64 { return int64(math.Ceil(f)) }
Round RounderFunc = func(f float64) int64 {
if f < 0 {
f = math.Ceil(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
func (r Rounder) Round(f float64) int {
return int(r.Round64(f))
}
// Rounder is the RounderFunc which will be used by all functions and methods in
// this package when needed.
var Rounder = Ceil

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

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

@ -144,10 +144,7 @@ func (b *Buffer) DrawLine(start, end, dir geo.XY, ls LineStyle) {
var perpDir geo.XY
perpDir[0], perpDir[1] = dir[1], dir[0]
dirSec := end.Sub(start).Mul(perpDir.Abs()).Unit()
// 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)
mid := start.Midpoint(end)
along := func(xy, dir geo.XY) int {
if dir[0] != 0 {

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

Loading…
Cancel
Save