xmtop/termui/linegraph.go
2018-02-19 02:00:21 -08:00

101 lines
2.3 KiB
Go

package termui
import (
"fmt"
"sort"
drawille "github.com/cjbassi/drawille-go"
)
// LineGraph implements a graph of data points.
type LineGraph struct {
*Block
Data map[string][]float64
LineColor map[string]Attribute
DefaultLineColor Attribute
}
// NewLineGraph returns a new LineGraph with current theme.
func NewLineGraph() *LineGraph {
return &LineGraph{
Block: NewBlock(),
Data: make(map[string][]float64),
LineColor: make(map[string]Attribute),
DefaultLineColor: Theme.LineGraph,
}
}
// renderPoints plots and interpolates data points.
func (lc *LineGraph) Buffer() *Buffer {
buf := lc.Block.Buffer()
c := drawille.NewCanvas()
colors := make([][]Attribute, lc.X+2)
for i := range colors {
colors[i] = make([]Attribute, lc.Y+2)
}
// Sort the series so that overlapping data will overlap the same way each time
seriesList := make([]string, len(lc.Data))
i := 0
for seriesName := range lc.Data {
seriesList[i] = seriesName
i++
}
sort.Strings(seriesList)
for j, seriesName := range seriesList {
seriesData := lc.Data[seriesName]
seriesLineColor, ok := lc.LineColor[seriesName]
if !ok {
seriesLineColor = lc.DefaultLineColor
}
lastY, lastX := -1, -1
// assign colors to `colors` and lines/points to the canvas
for i := len(seriesData) - 1; i >= 0; i-- {
x := ((lc.X + 1) * 2) - 1 - (((len(seriesData) - 1) - i) * 5)
y := ((lc.Y + 1) * 4) - 1 - int((float64((lc.Y)*4)-1)*(seriesData[i]/100))
// stop rendering at the left-most wall
if x < 0 {
break
}
if lastY == -1 { // if this is the first point
c.Set(x, y)
colors[x/2][y/4] = seriesLineColor
} else {
c.DrawLine(lastX, lastY, x, y)
for _, p := range drawille.Line(lastX, lastY, x, y) {
colors[p.X/2][p.Y/4] = seriesLineColor
}
}
lastX, lastY = x, y
}
// copy drawille and colors to buffer
for y, line := range c.Rows(c.MinX(), c.MinY(), c.MaxX(), c.MaxY()) {
for x, char := range line {
x /= 3
if x == 0 {
continue
}
if char != 10240 {
buf.SetCell(x, y, Cell{char, colors[x][y], lc.Bg})
}
}
}
// Render key
str := fmt.Sprintf("%s %3.0f%%", seriesName, seriesData[len(seriesData)-1])
for k, char := range str {
if char != ' ' {
buf.SetCell(3+k, j+2, Cell{char, seriesLineColor, lc.Bg})
}
}
}
return buf
}