diff options
Diffstat (limited to 'pkg/plot/barchart.go')
-rw-r--r-- | pkg/plot/barchart.go | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/pkg/plot/barchart.go b/pkg/plot/barchart.go new file mode 100644 index 0000000..cb6c95c --- /dev/null +++ b/pkg/plot/barchart.go @@ -0,0 +1,87 @@ +package plot + +import ( + "bytes" + "math" + + "github.com/sgreben/jp/pkg/draw" +) + +// BarChart is a bar chart +type BarChart struct { + draw.Canvas + BarPaddingX int +} + +// NewBarChart returns a new bar chart +func NewBarChart(canvas draw.Canvas) *BarChart { + chart := new(BarChart) + chart.Canvas = canvas + chart.BarPaddingX = 1 + return chart +} + +// Draw implements Chart +func (c *BarChart) Draw(data *DataTable) string { + minY := math.Inf(1) + maxY := math.Inf(-1) + for _, row := range data.Rows { + for _, y := range row { + if y < minY { + minY = y + } + if y > maxY { + maxY = y + } + } + } + paddingX := 4 + paddingY := 3 + chartHeight := c.Size().Height - paddingY*c.RuneSize().Height + chartWidth := c.Size().Width - 2*paddingX*c.RuneSize().Width + scaleY := float64(chartHeight) / maxY + barPaddedWidth := chartWidth / len(data.Columns) + barWidth := barPaddedWidth - (c.BarPaddingX * c.RuneSize().Width) + if barPaddedWidth < c.RuneSize().Width { + barPaddedWidth = c.RuneSize().Width + } + if barWidth < c.RuneSize().Width { + barWidth = c.RuneSize().Width + } + + scaleY = float64(chartHeight) / maxY + + for i, group := range data.Columns { + barLeft := paddingX*c.RuneSize().Width + barPaddedWidth*i + barRight := barLeft + barWidth + + y := data.Rows[0][i] + barHeight := y * scaleY + barBottom := (paddingY - 1) * c.RuneSize().Height + barTop := barBottom + int(barHeight) + + for x := barLeft; x < barRight; x++ { + for y := barBottom; y < barTop; y++ { + c.Set(y, x) + } + } + + // Group label + barMiddle := (barLeft + barRight) / 2 + c.GetBuffer().WriteCenter(0, barMiddle/c.RuneSize().Width, []rune(group)) + + // Count label + countLabelY := int(math.Ceil(float64(barTop)/float64(c.RuneSize().Height))) * c.RuneSize().Height + + if countLabelY <= barBottom && y > 0 { + c.GetBuffer().SetRow(barTop/c.RuneSize().Height, barLeft/c.RuneSize().Width, barRight/c.RuneSize().Width, '▁') + countLabelY = 3 * c.RuneSize().Height + } + + c.GetBuffer().WriteCenter(countLabelY/c.RuneSize().Height, barMiddle/c.RuneSize().Width, Ff(y)) + } + + b := bytes.NewBuffer(nil) + c.GetBuffer().Render(b) + return b.String() +} |