2019-01-23 21:12:10 -07:00
|
|
|
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
|
|
|
// Use of this source code is governed by a MIT license that can
|
|
|
|
// be found in the LICENSE file.
|
|
|
|
|
|
|
|
package widgets
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"image"
|
|
|
|
|
|
|
|
rw "github.com/mattn/go-runewidth"
|
|
|
|
|
2022-08-28 10:37:35 -06:00
|
|
|
. "git.tebitea.media/sashakoshka/termui/v3"
|
2019-01-23 21:12:10 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
type StackedBarChart struct {
|
|
|
|
Block
|
2019-02-23 18:15:42 -07:00
|
|
|
BarColors []Color
|
|
|
|
LabelStyles []Style
|
|
|
|
NumStyles []Style // only Fg and Modifier are used
|
|
|
|
NumFormatter func(float64) string
|
|
|
|
Data [][]float64
|
|
|
|
Labels []string
|
|
|
|
BarWidth int
|
|
|
|
BarGap int
|
|
|
|
MaxVal float64
|
2019-01-23 21:12:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewStackedBarChart() *StackedBarChart {
|
|
|
|
return &StackedBarChart{
|
2019-02-23 18:15:42 -07:00
|
|
|
Block: *NewBlock(),
|
|
|
|
BarColors: Theme.StackedBarChart.Bars,
|
|
|
|
LabelStyles: Theme.StackedBarChart.Labels,
|
|
|
|
NumStyles: Theme.StackedBarChart.Nums,
|
|
|
|
NumFormatter: func(n float64) string { return fmt.Sprint(n) },
|
|
|
|
BarGap: 1,
|
|
|
|
BarWidth: 3,
|
2019-01-23 21:12:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *StackedBarChart) Draw(buf *Buffer) {
|
|
|
|
self.Block.Draw(buf)
|
|
|
|
|
|
|
|
maxVal := self.MaxVal
|
|
|
|
if maxVal == 0 {
|
|
|
|
for _, data := range self.Data {
|
|
|
|
maxVal = MaxFloat64(maxVal, SumFloat64Slice(data))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
barXCoordinate := self.Inner.Min.X
|
|
|
|
|
|
|
|
for i, bar := range self.Data {
|
|
|
|
// draw stacked bars
|
|
|
|
stackedBarYCoordinate := 0
|
|
|
|
for j, data := range bar {
|
|
|
|
// draw each stacked bar
|
|
|
|
height := int((data / maxVal) * float64(self.Inner.Dy()-1))
|
|
|
|
for x := barXCoordinate; x < MinInt(barXCoordinate+self.BarWidth, self.Inner.Max.X); x++ {
|
|
|
|
for y := (self.Inner.Max.Y - 2) - stackedBarYCoordinate; y > (self.Inner.Max.Y-2)-stackedBarYCoordinate-height; y-- {
|
|
|
|
c := NewCell(' ', NewStyle(ColorClear, SelectColor(self.BarColors, j)))
|
|
|
|
buf.SetCell(c, image.Pt(x, y))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw number
|
|
|
|
numberXCoordinate := barXCoordinate + int((float64(self.BarWidth) / 2)) - 1
|
|
|
|
buf.SetString(
|
2019-02-23 18:15:42 -07:00
|
|
|
self.NumFormatter(data),
|
2019-01-23 21:12:10 -07:00
|
|
|
NewStyle(
|
|
|
|
SelectStyle(self.NumStyles, j+1).Fg,
|
|
|
|
SelectColor(self.BarColors, j),
|
|
|
|
SelectStyle(self.NumStyles, j+1).Modifier,
|
|
|
|
),
|
|
|
|
image.Pt(numberXCoordinate, (self.Inner.Max.Y-2)-stackedBarYCoordinate),
|
|
|
|
)
|
|
|
|
|
|
|
|
stackedBarYCoordinate += height
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw label
|
2019-01-24 06:38:28 -07:00
|
|
|
if i < len(self.Labels) {
|
|
|
|
labelXCoordinate := barXCoordinate + MaxInt(
|
|
|
|
int((float64(self.BarWidth)/2))-int((float64(rw.StringWidth(self.Labels[i]))/2)),
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
buf.SetString(
|
|
|
|
TrimString(self.Labels[i], self.BarWidth),
|
|
|
|
SelectStyle(self.LabelStyles, i),
|
|
|
|
image.Pt(labelXCoordinate, self.Inner.Max.Y-1),
|
|
|
|
)
|
|
|
|
}
|
2019-01-23 21:12:10 -07:00
|
|
|
|
|
|
|
barXCoordinate += (self.BarWidth + self.BarGap)
|
|
|
|
}
|
|
|
|
}
|