Merge pull request #31 from dhilipkumars/MultiColorBarGraph

Implement Multi-colored/Stacked barGraph
This commit is contained in:
Zack Guo 2015-04-22 04:02:39 -04:00
commit fc47847f39
4 changed files with 253 additions and 0 deletions

48
example/mbarchart.go Normal file
View File

@ -0,0 +1,48 @@
// 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.
// +build ignore
package main
import "github.com/gizak/termui"
func main() {
err := termui.Init()
if err != nil {
panic(err)
}
defer termui.Close()
termui.UseTheme("helloworld")
bc := termui.NewMBarChart()
math := []int{45, 75, 34, 62}
english := []int{50, 45, 25, 57}
science := []int{75, 60, 15, 50}
compsci := []int{90, 95, 100, 100}
bc.Data[0] = math
bc.Data[1] = english
bc.Data[2] = science
bc.Data[3] = compsci
studentsName := []string{"Ken", "Rob", "Dennis", "Linus"}
bc.Border.Label = "Student's Marks X-Axis=Name Y-Axis=Marks[Math,English,Science,ComputerScience] in %"
bc.Width = 100
bc.Height = 50
bc.Y = 10
bc.BarWidth = 10
bc.DataLabels = studentsName
bc.TextColor = termui.ColorGreen //this is color for label (x-axis)
bc.BarColor[3] = termui.ColorGreen //BarColor for computerscience
bc.BarColor[1] = termui.ColorYellow //Bar Color for english
bc.NumColor[3] = termui.ColorRed // Num color for computerscience
bc.NumColor[1] = termui.ColorRed // num color for english
//Other colors are automatically populated, btw All the students seems do well in computerscience. :p
termui.Render(bc)
<-termui.EventCh()
}

View File

@ -24,6 +24,7 @@ const (
ColorWhite
)
const NumberofColors = 8 //Have a constant that defines number of colors
const (
AttrBold Attribute = 1 << (iota + 9)
AttrUnderline

198
mbar.go Normal file
View File

@ -0,0 +1,198 @@
// 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.
package termui
import (
"fmt"
)
// This is the implemetation of multi-colored or stacked bar graph. This is different from default barGraph which is implemented in bar.go
// Multi-Colored-BarChart creates multiple bars in a widget:
/*
bc := termui.NewMBarChart()
data := make([][]int, 2)
data[0] := []int{3, 2, 5, 7, 9, 4}
data[1] := []int{7, 8, 5, 3, 1, 6}
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
*/
type MBarChart struct {
Block
BarColor [NumberofColors]Attribute
TextColor Attribute
NumColor [NumberofColors]Attribute
Data [NumberofColors][]int
DataLabels []string
BarWidth int
BarGap int
labels [][]rune
dataNum [NumberofColors][][]rune
numBar int
scale float64
max int
minDataLen int
numStack int
}
// NewBarChart returns a new *BarChart with current theme.
func NewMBarChart() *MBarChart {
bc := &MBarChart{Block: *NewBlock()}
bc.BarColor[0] = theme.MBarChartBar
bc.NumColor[0] = theme.MBarChartNum
bc.TextColor = theme.MBarChartText
bc.BarGap = 1
bc.BarWidth = 3
return bc
}
func (bc *MBarChart) layout() {
bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth)
bc.labels = make([][]rune, bc.numBar)
DataLen := 0
LabelLen := len(bc.DataLabels)
bc.minDataLen = 9999 //Set this to some very hight value so that we find the minimum one We want to know which array among data[][] has got the least lenght
// We need to know how many stack/data array data[0] , data[1] are there
for i := 0; i < len(bc.Data); i++ {
if bc.Data[i] == nil {
break
}
DataLen++
}
bc.numStack = DataLen
//We need to what is the mimimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs
for i := 0; i < DataLen; i++ {
if bc.minDataLen > len(bc.Data[i]) {
bc.minDataLen = len(bc.Data[i])
}
}
if LabelLen > bc.minDataLen {
LabelLen = bc.minDataLen
}
for i := 0; i < LabelLen && i < bc.numBar; i++ {
bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
}
for i := 0; i < bc.numStack; i++ {
bc.dataNum[i] = make([][]rune, len(bc.Data[i]))
//For each stack of bar calcualte the rune
for j := 0; j < LabelLen && i < bc.numBar; j++ {
n := bc.Data[i][j]
s := fmt.Sprint(n)
bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth)
}
//If color is not defined by default then populate a color that is different from the prevous bar
if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault {
if i == 0 {
bc.BarColor[i] = ColorBlack
} else {
bc.BarColor[i] = bc.BarColor[i-1] + 1
if bc.BarColor[i] > NumberofColors {
bc.BarColor[i] = ColorBlack
}
}
bc.NumColor[i] = (NumberofColors + 1) - bc.BarColor[i] //Make NumColor opposite of barColor for visibility
}
}
//If Max value is not set then we have to populate, this time the max value will be max(sum(d1[0],d2[0],d3[0]) .... sum(d1[n], d2[n], d3[n]))
if bc.max == 0 {
bc.max = -1
}
for i := 0; i < bc.minDataLen && i < LabelLen; i++ {
var dsum int
for j := 0; j < bc.numStack; j++ {
dsum += bc.Data[j][i]
}
if dsum > bc.max {
bc.max = dsum
}
}
bc.scale = float64(bc.max) / float64(bc.innerHeight-1)
}
func (bc *MBarChart) SetMax(max int) {
if max > 0 {
bc.max = max
}
}
// Buffer implements Bufferer interface.
func (bc *MBarChart) Buffer() []Point {
ps := bc.Block.Buffer()
bc.layout()
var oftX int
for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ {
ph := 0 //Previous Height to stack up
for i1 := 0; i1 < bc.numStack; i1++ {
h := int(float64(bc.Data[i1][i]) / bc.scale)
oftX = i * (bc.BarWidth + bc.BarGap)
// plot bars
for j := 0; j < bc.BarWidth; j++ {
for k := 0; k < h; k++ {
p := Point{}
p.Ch = ' '
p.Bg = bc.BarColor[i1]
if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent!
p.Bg |= AttrReverse
}
p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j
p.Y = bc.innerY + bc.innerHeight - 2 - k - ph
ps = append(ps, p)
}
}
ph += h
}
// plot text
for j, k := 0, 0; j < len(bc.labels[i]); j++ {
w := charWidth(bc.labels[i][j])
p := Point{}
p.Ch = bc.labels[i][j]
p.Bg = bc.BgColor
p.Fg = bc.TextColor
p.Y = bc.innerY + bc.innerHeight - 1
p.X = bc.innerX + oftX + k
ps = append(ps, p)
k += w
}
// plot num
ph = 0 //re-initialize previous height
for i1 := 0; i1 < bc.numStack; i1++ {
h := int(float64(bc.Data[i1][i]) / bc.scale)
for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ {
p := Point{}
p.Ch = bc.dataNum[i1][i][j]
p.Fg = bc.NumColor[i1]
p.Bg = bc.BarColor[i1]
if bc.BarColor[i1] == ColorDefault { // the same as above
p.Bg |= AttrReverse
}
if h == 0 {
p.Bg = bc.BgColor
}
p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j
p.Y = bc.innerY + bc.innerHeight - 2 - ph
ps = append(ps, p)
}
ph += h
}
}
return bc.Block.chopOverflow(ps)
}

View File

@ -26,6 +26,9 @@ type ColorScheme struct {
BarChartBar Attribute
BarChartText Attribute
BarChartNum Attribute
MBarChartBar Attribute
MBarChartText Attribute
MBarChartNum Attribute
}
// default color scheme depends on the user's terminal setting.
@ -52,6 +55,9 @@ var themeHelloWorld = ColorScheme{
BarChartBar: ColorRed,
BarChartNum: ColorWhite,
BarChartText: ColorCyan,
MBarChartBar: ColorRed,
MBarChartNum: ColorWhite,
MBarChartText: ColorCyan,
}
var theme = themeDefault // global dep