Fix some off-by-one errors in Y axis calculations

Y axis label printing stopped before the top line
Sometimes large data values would land on the border or beyond the box
fix typo
This commit is contained in:
Matt Ranney 2016-02-04 10:56:53 -08:00
parent e74935dded
commit 562ca47996
2 changed files with 17 additions and 57 deletions

View File

@ -7,9 +7,7 @@ package termui
import ( import (
"fmt" "fmt"
"math" "math"
"os"
"sort" "sort"
"time"
) )
// only 16 possible combinations, why bother // only 16 possible combinations, why bother
@ -38,39 +36,9 @@ var braillePatterns = map[[2]int]rune{
var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'} var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'}
var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'} var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
// set this filename to have debug logging written here // LineChart has two modes: braille(default) and dot.
var DebugFilename string // A single braille character is a 2x4 grid of dots, so Using braille
var debugFile *os.File // gives 2x X resolution and 4x Y resolution over dot mode.
func debugLog(str string) {
if DebugFilename == "" {
return
}
var err error
if debugFile == nil {
debugFile, err = os.OpenFile(DebugFilename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
panic(err)
}
}
stamp := time.Now().Format(time.StampMilli)
_, err = fmt.Fprintln(debugFile, stamp, str)
if err != nil {
panic(err)
}
}
func Debug(a ...interface{}) {
debugLog(fmt.Sprint(a))
}
func Debugf(format string, a ...interface{}) {
debugLog(fmt.Sprintf(format, a...))
}
// LineChart has two modes: braille(default) and dot. Using braille gives 2x capicity as dot mode,
// because one braille char can represent two data points.
/* /*
lc := termui.NewLineChart() lc := termui.NewLineChart()
lc.Border.Label = "braille-mode Line Chart" lc.Border.Label = "braille-mode Line Chart"
@ -95,8 +63,8 @@ type LineChart struct {
drawingY int drawingY int
axisYHeight int axisYHeight int
axisXWidth int axisXWidth int
axisYLebelGap int axisYLabelGap int
axisXLebelGap int axisXLabelGap int
topValue float64 topValue float64
bottomValue float64 bottomValue float64
labelX [][]rune labelX [][]rune
@ -118,8 +86,8 @@ func NewLineChart() *LineChart {
lc.DotStyle = '•' lc.DotStyle = '•'
lc.Data = make(map[string][]float64) lc.Data = make(map[string][]float64)
lc.LineColor = make(map[string]Attribute) lc.LineColor = make(map[string]Attribute)
lc.axisXLebelGap = 2 lc.axisXLabelGap = 2
lc.axisYLebelGap = 1 lc.axisYLabelGap = 1
lc.bottomValue = math.Inf(1) lc.bottomValue = math.Inf(1)
lc.topValue = math.Inf(-1) lc.topValue = math.Inf(-1)
lc.YPadding = 0.2 lc.YPadding = 0.2
@ -222,7 +190,6 @@ func (lc *LineChart) renderDot() Buffer {
minCell := lc.innerArea.Min.X + lc.labelYSpace minCell := lc.innerArea.Min.X + lc.labelYSpace
cellPos := lc.innerArea.Max.X - 1 cellPos := lc.innerArea.Max.X - 1
for dataPos := len(seriesData) - 1; dataPos >= 0 && cellPos > minCell; { for dataPos := len(seriesData) - 1; dataPos >= 0 && cellPos > minCell; {
Debug(seriesName, " ", dataPos, cellPos, seriesData[dataPos])
c := Cell{ c := Cell{
Ch: lc.DotStyle, Ch: lc.DotStyle,
Fg: thisLineColor, Fg: thisLineColor,
@ -254,7 +221,7 @@ func (lc *LineChart) calcLabelX() {
if l+w <= lc.axisXWidth { if l+w <= lc.axisXWidth {
lc.labelX = append(lc.labelX, s) lc.labelX = append(lc.labelX, s)
} }
l += w + lc.axisXLebelGap l += w + lc.axisXLabelGap
} else { // braille } else { // braille
if 2*l >= len(lc.DataLabels) { if 2*l >= len(lc.DataLabels) {
break break
@ -265,7 +232,7 @@ func (lc *LineChart) calcLabelX() {
if l+w <= lc.axisXWidth { if l+w <= lc.axisXWidth {
lc.labelX = append(lc.labelX, s) lc.labelX = append(lc.labelX, s)
} }
l += w + lc.axisXLebelGap l += w + lc.axisXLabelGap
} }
} }
@ -285,9 +252,10 @@ func shortenFloatVal(x float64) string {
func (lc *LineChart) calcLabelY() { func (lc *LineChart) calcLabelY() {
span := lc.topValue - lc.bottomValue span := lc.topValue - lc.bottomValue
lc.scale = span / float64(lc.axisYHeight) // where does -2 come from? Without it, we might draw on the top border or past the block
lc.scale = span / float64(lc.axisYHeight-2)
n := (1 + lc.axisYHeight) / (lc.axisYLebelGap + 1) n := (1 + lc.axisYHeight) / (lc.axisYLabelGap + 1)
lc.labelY = make([][]rune, n) lc.labelY = make([][]rune, n)
maxLen := 0 maxLen := 0
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
@ -354,7 +322,7 @@ func (lc *LineChart) calcLayout() {
} }
} }
lc.axisYHeight = lc.innerArea.Dy() - 2 lc.axisYHeight = lc.innerArea.Dy() - 1
lc.calcLabelY() lc.calcLabelY()
lc.axisXWidth = lc.innerArea.Dx() - 1 - lc.labelYSpace lc.axisXWidth = lc.innerArea.Dx() - 1 - lc.labelYSpace
@ -362,8 +330,6 @@ func (lc *LineChart) calcLayout() {
lc.drawingX = lc.innerArea.Min.X + 1 + lc.labelYSpace lc.drawingX = lc.innerArea.Min.X + 1 + lc.labelYSpace
lc.drawingY = lc.innerArea.Min.Y lc.drawingY = lc.innerArea.Min.Y
Debugf("calcLayout bottom=%f top=%f min=%f max=%f axisYHeight=%d", lc.bottomValue, lc.topValue, lc.minY, lc.maxY, lc.axisYHeight)
} }
func (lc *LineChart) plotAxes() Buffer { func (lc *LineChart) plotAxes() Buffer {
@ -378,8 +344,8 @@ func (lc *LineChart) plotAxes() Buffer {
buf.Set(x, origY, Cell{Ch: HDASH, Fg: lc.AxesColor, Bg: lc.Bg}) buf.Set(x, origY, Cell{Ch: HDASH, Fg: lc.AxesColor, Bg: lc.Bg})
} }
for dy := 1; dy <= lc.axisYHeight; dy++ { for y := origY - 1; y > origY-lc.axisYHeight; y-- {
buf.Set(origX, origY-dy, Cell{Ch: VDASH, Fg: lc.AxesColor, Bg: lc.Bg}) buf.Set(origX, y, Cell{Ch: VDASH, Fg: lc.AxesColor, Bg: lc.Bg})
} }
// x label // x label
@ -398,7 +364,7 @@ func (lc *LineChart) plotAxes() Buffer {
y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 1 y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 1
buf.Set(x, y, c) buf.Set(x, y, c)
} }
oft += len(rs) + lc.axisXLebelGap oft += len(rs) + lc.axisXLabelGap
} }
// y labels // y labels
@ -406,7 +372,7 @@ func (lc *LineChart) plotAxes() Buffer {
for j, r := range rs { for j, r := range rs {
buf.Set( buf.Set(
lc.innerArea.Min.X+j, lc.innerArea.Min.X+j,
origY-i*(lc.axisYLebelGap+1), origY-i*(lc.axisYLabelGap+1),
Cell{Ch: r, Fg: lc.AxesColor, Bg: lc.Bg}) Cell{Ch: r, Fg: lc.AxesColor, Bg: lc.Bg})
} }
} }
@ -425,17 +391,14 @@ func (lc *LineChart) Buffer() Buffer {
} }
} }
if seriesCount == 0 { if seriesCount == 0 {
Debug("lc render no data")
return buf return buf
} }
lc.calcLayout() lc.calcLayout()
buf.Merge(lc.plotAxes()) buf.Merge(lc.plotAxes())
if lc.Mode == "dot" { if lc.Mode == "dot" {
Debug("lc render start dot")
buf.Merge(lc.renderDot()) buf.Merge(lc.renderDot())
} else { } else {
Debug("lc render start braille")
buf.Merge(lc.renderBraille()) buf.Merge(lc.renderBraille())
} }

View File

@ -63,9 +63,6 @@ func Init() error {
// should be called after successful initialization when termui's functionality isn't required anymore. // should be called after successful initialization when termui's functionality isn't required anymore.
func Close() { func Close() {
tm.Close() tm.Close()
if debugFile != nil {
debugFile.Close()
}
} }
var renderLock sync.Mutex var renderLock sync.Mutex