xmtop/src/termui/sparkline.go

100 lines
2.5 KiB
Go
Raw Normal View History

2019-01-01 07:01:13 +08:00
package termui
import (
2019-01-01 08:55:50 +08:00
"image"
. "github.com/gizak/termui"
2019-01-01 07:01:13 +08:00
)
var SPARKS = [8]rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'}
// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃. The data points should be non-negative integers.
type Sparkline struct {
Data []int
Title1 string
Title2 string
2019-01-01 08:55:50 +08:00
TitleColor Attribute
LineColor Attribute
2019-01-01 07:01:13 +08:00
}
// Sparklines is a renderable widget which groups together the given sparklines.
type Sparklines struct {
*Block
Lines []*Sparkline
}
// Add appends a given Sparkline to the *Sparklines.
func (self *Sparklines) Add(sl Sparkline) {
self.Lines = append(self.Lines, &sl)
}
// NewSparkline returns an unrenderable single sparkline that intended to be added into a Sparklines.
func NewSparkline() *Sparkline {
2019-01-01 08:55:50 +08:00
return &Sparkline{}
2019-01-01 07:01:13 +08:00
}
// NewSparklines return a new *Sparklines with given Sparklines, you can always add a new Sparkline later.
func NewSparklines(ss ...*Sparkline) *Sparklines {
return &Sparklines{
Block: NewBlock(),
Lines: ss,
}
}
2019-01-01 08:55:50 +08:00
func (self *Sparklines) Draw(buf *Buffer) {
self.Block.Draw(buf)
2019-01-01 07:01:13 +08:00
lc := len(self.Lines) // lineCount
// renders each sparkline and its titles
for i, line := range self.Lines {
// prints titles
2019-01-01 18:29:14 +08:00
title1Y := self.Inner.Min.Y + 1 + (self.Inner.Dy()/lc)*i
title2Y := self.Inner.Min.Y + 2 + (self.Inner.Dy()/lc)*i
2019-01-01 08:55:50 +08:00
title1 := TrimString(line.Title1, self.Inner.Dx())
title2 := TrimString(line.Title2, self.Inner.Dx())
2019-01-01 18:29:14 +08:00
if self.Inner.Dy() > 5 {
buf.SetString(
title1,
image.Pt(self.Inner.Min.X, title1Y),
AttrPair{line.TitleColor | AttrBold, -1},
)
}
if self.Inner.Dy() > 6 {
buf.SetString(
title2,
image.Pt(self.Inner.Min.X, title2Y),
AttrPair{line.TitleColor | AttrBold, -1},
)
}
2019-01-01 07:01:13 +08:00
2019-01-01 08:55:50 +08:00
sparkY := (self.Inner.Dy() / lc) * (i + 1)
2019-01-01 07:01:13 +08:00
// finds max data in current view used for relative heights
max := 1
2019-01-01 08:55:50 +08:00
for i := len(line.Data) - 1; i >= 0 && self.Inner.Dx()-((len(line.Data)-1)-i) >= 1; i-- {
2019-01-01 07:01:13 +08:00
if line.Data[i] > max {
max = line.Data[i]
}
}
// prints sparkline
2019-01-01 08:55:50 +08:00
for x := self.Inner.Dx(); x >= 1; x-- {
2019-01-01 07:01:13 +08:00
char := SPARKS[0]
2019-01-01 08:55:50 +08:00
if (self.Inner.Dx() - x) < len(line.Data) {
offset := self.Inner.Dx() - x
2019-01-01 07:01:13 +08:00
cur_item := line.Data[(len(line.Data)-1)-offset]
percent := float64(cur_item) / float64(max)
index := int(percent * 7)
if index < 0 || index >= len(SPARKS) {
2019-01-01 08:55:50 +08:00
panic("TODO")
2019-01-01 07:01:13 +08:00
}
char = SPARKS[index]
}
2019-01-01 08:55:50 +08:00
buf.SetCell(
Cell{char, AttrPair{line.LineColor, -1}},
image.Pt(self.Inner.Min.X+x-1, self.Inner.Min.Y+sparkY-1),
)
2019-01-01 07:01:13 +08:00
}
}
}