gim: make rounder a global in geo, kinda gross but simplifies a lot of things
This commit is contained in:
parent
ef48a2d708
commit
0b36e4ec37
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user