2015-03-20 14:21:50 -06:00
|
|
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
|
|
|
// Use of this source code is governed by a MIT license that can
|
|
|
|
// be found in the LICENSE file.
|
|
|
|
|
2015-02-07 18:19:16 -07:00
|
|
|
package termui
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
|
2015-03-24 15:16:43 -06:00
|
|
|
// BarChart creates multiple bars in a widget:
|
|
|
|
/*
|
|
|
|
bc := termui.NewBarChart()
|
|
|
|
data := []int{3, 2, 5, 3, 9, 5}
|
|
|
|
bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
|
|
|
|
bc.Border.Label = "Bar Chart"
|
|
|
|
bc.Data = data
|
|
|
|
bc.Width = 26
|
|
|
|
bc.Height = 10
|
|
|
|
bc.DataLabels = bclabels
|
|
|
|
bc.TextColor = termui.ColorGreen
|
|
|
|
bc.BarColor = termui.ColorRed
|
|
|
|
bc.NumColor = termui.ColorYellow
|
|
|
|
*/
|
2015-02-07 18:19:16 -07:00
|
|
|
type BarChart struct {
|
|
|
|
Block
|
|
|
|
BarColor Attribute
|
|
|
|
TextColor Attribute
|
|
|
|
NumColor Attribute
|
|
|
|
Data []int
|
|
|
|
DataLabels []string
|
|
|
|
BarWidth int
|
|
|
|
BarGap int
|
|
|
|
labels [][]rune
|
|
|
|
dataNum [][]rune
|
|
|
|
numBar int
|
|
|
|
scale float64
|
|
|
|
max int
|
|
|
|
}
|
|
|
|
|
2015-03-24 15:16:43 -06:00
|
|
|
// NewBarChart returns a new *BarChart with current theme.
|
2015-02-07 18:19:16 -07:00
|
|
|
func NewBarChart() *BarChart {
|
|
|
|
bc := &BarChart{Block: *NewBlock()}
|
2015-03-11 14:15:59 -06:00
|
|
|
bc.BarColor = theme.BarChartBar
|
|
|
|
bc.NumColor = theme.BarChartNum
|
|
|
|
bc.TextColor = theme.BarChartText
|
2015-02-07 18:19:16 -07:00
|
|
|
bc.BarGap = 1
|
|
|
|
bc.BarWidth = 3
|
|
|
|
return bc
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bc *BarChart) layout() {
|
|
|
|
bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth)
|
|
|
|
bc.labels = make([][]rune, bc.numBar)
|
|
|
|
bc.dataNum = make([][]rune, len(bc.Data))
|
|
|
|
|
|
|
|
for i := 0; i < bc.numBar && i < len(bc.DataLabels) && i < len(bc.Data); i++ {
|
|
|
|
bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
|
|
|
|
n := bc.Data[i]
|
|
|
|
s := fmt.Sprint(n)
|
|
|
|
bc.dataNum[i] = trimStr2Runes(s, bc.BarWidth)
|
|
|
|
}
|
|
|
|
|
2015-04-15 14:50:49 -06:00
|
|
|
//bc.max = bc.Data[0] // what if Data is nil? Sometimes when bar graph is nill it produces panic with panic: runtime error: index out of range
|
|
|
|
// Asign a negative value to get maxvalue auto-populates
|
|
|
|
if bc.max == 0 {
|
|
|
|
bc.max = -1
|
|
|
|
}
|
2015-02-07 18:19:16 -07:00
|
|
|
for i := 0; i < len(bc.Data); i++ {
|
|
|
|
if bc.max < bc.Data[i] {
|
|
|
|
bc.max = bc.Data[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bc.scale = float64(bc.max) / float64(bc.innerHeight-1)
|
|
|
|
}
|
|
|
|
|
2015-04-15 14:50:49 -06:00
|
|
|
func (bc *BarChart) SetMax(max int) {
|
|
|
|
|
|
|
|
if max > 0 {
|
|
|
|
bc.max = max
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-24 15:16:43 -06:00
|
|
|
// Buffer implements Bufferer interface.
|
2015-02-07 18:19:16 -07:00
|
|
|
func (bc *BarChart) Buffer() []Point {
|
|
|
|
ps := bc.Block.Buffer()
|
|
|
|
bc.layout()
|
|
|
|
|
|
|
|
for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ {
|
|
|
|
h := int(float64(bc.Data[i]) / bc.scale)
|
|
|
|
oftX := i * (bc.BarWidth + bc.BarGap)
|
|
|
|
// plot bar
|
|
|
|
for j := 0; j < bc.BarWidth; j++ {
|
|
|
|
for k := 0; k < h; k++ {
|
|
|
|
p := Point{}
|
2015-03-03 11:28:09 -07:00
|
|
|
p.Ch = ' '
|
|
|
|
p.Bg = bc.BarColor
|
2015-03-11 14:15:59 -06:00
|
|
|
if bc.BarColor == ColorDefault { // when color is default, space char treated as transparent!
|
|
|
|
p.Bg |= AttrReverse
|
|
|
|
}
|
2015-02-07 18:19:16 -07:00
|
|
|
p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j
|
|
|
|
p.Y = bc.innerY + bc.innerHeight - 2 - k
|
|
|
|
ps = append(ps, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// plot text
|
2015-03-25 16:04:15 -06:00
|
|
|
for j, k := 0, 0; j < len(bc.labels[i]); j++ {
|
|
|
|
w := charWidth(bc.labels[i][j])
|
2015-02-07 18:19:16 -07:00
|
|
|
p := Point{}
|
2015-03-03 11:28:09 -07:00
|
|
|
p.Ch = bc.labels[i][j]
|
|
|
|
p.Bg = bc.BgColor
|
|
|
|
p.Fg = bc.TextColor
|
2015-02-07 18:19:16 -07:00
|
|
|
p.Y = bc.innerY + bc.innerHeight - 1
|
2015-03-25 16:04:15 -06:00
|
|
|
p.X = bc.innerX + oftX + k
|
2015-02-07 18:19:16 -07:00
|
|
|
ps = append(ps, p)
|
2015-03-25 16:04:15 -06:00
|
|
|
k += w
|
2015-02-07 18:19:16 -07:00
|
|
|
}
|
|
|
|
// plot num
|
|
|
|
for j := 0; j < len(bc.dataNum[i]); j++ {
|
|
|
|
p := Point{}
|
2015-03-03 11:28:09 -07:00
|
|
|
p.Ch = bc.dataNum[i][j]
|
|
|
|
p.Fg = bc.NumColor
|
|
|
|
p.Bg = bc.BarColor
|
2015-03-11 14:15:59 -06:00
|
|
|
if bc.BarColor == ColorDefault { // the same as above
|
|
|
|
p.Bg |= AttrReverse
|
|
|
|
}
|
2015-02-07 18:19:16 -07:00
|
|
|
if h == 0 {
|
2015-03-03 11:28:09 -07:00
|
|
|
p.Bg = bc.BgColor
|
2015-02-07 18:19:16 -07:00
|
|
|
}
|
|
|
|
p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j
|
|
|
|
p.Y = bc.innerY + bc.innerHeight - 2
|
|
|
|
ps = append(ps, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-09 14:40:49 -06:00
|
|
|
return bc.Block.chopOverflow(ps)
|
2015-02-07 18:19:16 -07:00
|
|
|
}
|