mirror of https://github.com/mum4k/termdash.git
Design APIs of the HeatMap widget
This commit is contained in:
parent
29d7e65c6a
commit
65debbb18f
|
@ -47,6 +47,10 @@ type HeatMap struct {
|
|||
// YLabels are the labels on the Y axis in an increasing order.
|
||||
YLabels []string
|
||||
|
||||
// MinValue and MaxValue are the Min and Max values in the values,
|
||||
// which will be used to calculate the color of each cell.
|
||||
MinValue, MaxValue float64
|
||||
|
||||
// opts are the provided options.
|
||||
opts *options
|
||||
|
||||
|
@ -72,19 +76,20 @@ func (hp *HeatMap) axesDetails(cvs *canvas.Canvas) (*axes.XDetails, *axes.YDetai
|
|||
return nil, nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// Draw draws the values as HeatMap.
|
||||
// Draw draws cells, X labels and Y labels as HeatMap.
|
||||
// Implements widgetapi.Widget.Draw.
|
||||
func (hp *HeatMap) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// drawCells draws the graph representing the stored values.
|
||||
// drawCells draws m*n cells (rectangles) representing the stored values.
|
||||
// The height of each cell is 1 and the default width is 3.
|
||||
func (hp *HeatMap) drawCells(cvs *canvas.Canvas, xd *axes.XDetails, yd *axes.YDetails) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// drawAxes draws the X,Y axes and their labels.
|
||||
func (hp *HeatMap) drawAxes(cvs *canvas.Canvas, xd *axes.XDetails, yd *axes.YDetails) error {
|
||||
// drawAxes draws X labels (under the cells) and Y Labels (on the left side of the cell).
|
||||
func (hp *HeatMap) drawLabels(cvs *canvas.Canvas, xd *axes.XDetails, yd *axes.YDetails) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
|
@ -112,8 +117,8 @@ func (hp *HeatMap) Options() widgetapi.Options {
|
|||
|
||||
// getCellColor returns the color of the cell according to its value.
|
||||
// The larger the value, the darker the color.
|
||||
// The color range is in Xterm color, from 232 to 255.
|
||||
// Refer to https://jonasjacek.github.io/colors/.
|
||||
// The color range is in Xterm color [232, 255].
|
||||
func (hp *HeatMap) getCellColor(value float64) cell.Color {
|
||||
return cell.ColorDefault
|
||||
}
|
||||
|
|
|
@ -27,47 +27,18 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
xLabels := []string{
|
||||
"12:00",
|
||||
"12:05",
|
||||
"12:10",
|
||||
"12:15",
|
||||
"12:20",
|
||||
}
|
||||
yLabels := []string{
|
||||
"10",
|
||||
"20",
|
||||
"30",
|
||||
"40",
|
||||
"50",
|
||||
"60",
|
||||
"70",
|
||||
"80",
|
||||
"90",
|
||||
"100",
|
||||
}
|
||||
values := map[string][]int64{
|
||||
"12:00": {10, 20, 30, 40, 50, 50, 40, 30, 20, 10},
|
||||
"12:05": {50, 40, 30, 20, 10, 10, 20, 30, 40, 50},
|
||||
"12:10": {10, 20, 30, 40, 50, 50, 40, 30, 20, 10},
|
||||
"12:15": {50, 40, 30, 20, 10, 10, 20, 30, 40, 50},
|
||||
"12:20": {10, 20, 30, 40, 50, 50, 40, 30, 0, 0},
|
||||
}
|
||||
|
||||
t, err := termbox.New()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer t.Close()
|
||||
|
||||
hp, err := heatmap.NewHeatMap()
|
||||
hp, err := heatmap.New()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := hp.SetColumns(xLabels, values); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
hp.SetYLabels(yLabels)
|
||||
|
||||
// TODO: set heatmap's data
|
||||
|
||||
c, err := container.New(
|
||||
t,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package axes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image"
|
||||
|
||||
"github.com/mum4k/termdash/private/runewidth"
|
||||
|
@ -24,7 +25,7 @@ import (
|
|||
const AxisWidth = 1
|
||||
|
||||
// YDetails contain information about the Y axis
|
||||
// that will be drawn onto the canvas.
|
||||
// that will NOT be drawn onto the canvas, but will take up space.
|
||||
type YDetails struct {
|
||||
// Width in character cells of the Y axis and its character labels.
|
||||
Width int
|
||||
|
@ -42,30 +43,15 @@ type YDetails struct {
|
|||
|
||||
// RequiredWidth calculates the minimum width required
|
||||
// in order to draw the Y axis and its labels.
|
||||
func RequiredWidth(max string) int {
|
||||
return runewidth.StringWidth(max) + AxisWidth
|
||||
// The parameter ls is the longest string in YLabels.
|
||||
func RequiredWidth(ls string) int {
|
||||
return runewidth.StringWidth(ls) + AxisWidth
|
||||
}
|
||||
|
||||
// NewYDetails retrieves details about the Y axis required
|
||||
// to draw it on a canvas of the provided area.
|
||||
func NewYDetails(stringLabels []string) (*YDetails, error) {
|
||||
graphHeight := len(stringLabels)
|
||||
|
||||
// See how the labels would look like on the entire maxWidth.
|
||||
maxLabelWidth := LongestString(stringLabels)
|
||||
labels, err := yLabels(graphHeight, maxLabelWidth, stringLabels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
width := maxLabelWidth + 1
|
||||
|
||||
return &YDetails{
|
||||
Width: width,
|
||||
Start: image.Point{X: width - 1, Y: 0},
|
||||
End: image.Point{X: width - 1, Y: graphHeight},
|
||||
Labels: labels,
|
||||
}, nil
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// LongestString returns the length of the longest string in the string array.
|
||||
|
@ -80,7 +66,7 @@ func LongestString(strings []string) int {
|
|||
}
|
||||
|
||||
// XDetails contain information about the X axis
|
||||
// that will be drawn onto the canvas.
|
||||
// that will NOT be drawn onto the canvas.
|
||||
type XDetails struct {
|
||||
// Start is the point where the X axis starts.
|
||||
// Both coordinates of Start are less than End.
|
||||
|
@ -94,23 +80,6 @@ type XDetails struct {
|
|||
|
||||
// NewXDetails retrieves details about the X axis required to draw it on a canvas
|
||||
// of the provided area. The yStart is the point where the Y axis starts.
|
||||
// The numPoints is the number of points in the largest series that will be
|
||||
// plotted.
|
||||
// customLabels are the desired labels for the X axis, these are preferred if
|
||||
// provided.
|
||||
func NewXDetails(cvsAr image.Rectangle, yEnd image.Point, stringLabels []string, cellWidth int) (*XDetails, error) {
|
||||
// The space between the start of the axis and the end of the canvas.
|
||||
// graphWidth := cvsAr.Dx() - yEnd.X - 1
|
||||
graphWidth := len(stringLabels) * cellWidth
|
||||
|
||||
labels, err := xLabels(yEnd, graphWidth, stringLabels, cellWidth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &XDetails{
|
||||
Start: image.Point{yEnd.X, yEnd.Y - 1},
|
||||
End: image.Point{yEnd.X + graphWidth, yEnd.Y - 1},
|
||||
Labels: labels,
|
||||
}, nil
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
|
|
@ -17,11 +17,8 @@ package axes
|
|||
// label.go contains code that calculates the positions of labels on the axes.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"image"
|
||||
|
||||
"github.com/mum4k/termdash/align"
|
||||
"github.com/mum4k/termdash/private/alignfor"
|
||||
)
|
||||
|
||||
// Label is one text label on an axis.
|
||||
|
@ -33,74 +30,27 @@ type Label struct {
|
|||
Pos image.Point
|
||||
}
|
||||
|
||||
// yLabels returns labels that should be placed next to the Y axis.
|
||||
// yLabels returns labels that should be placed next to the cells.
|
||||
// The labelWidth is the width of the area from the left-most side of the
|
||||
// canvas until the Y axis (not including the Y axis). This is the area where
|
||||
// the labels will be placed and aligned.
|
||||
// Labels are returned with Y coordinates in ascending order.
|
||||
// Y coordinates grow down.
|
||||
func yLabels(graphHeight, labelWidth int, stringLabels []string) ([]*Label, error) {
|
||||
if min := 2; graphHeight < min {
|
||||
return nil, fmt.Errorf("cannot place labels on a canvas with height %d, minimum is %d", graphHeight, min)
|
||||
}
|
||||
if min := 0; labelWidth < min {
|
||||
return nil, fmt.Errorf("cannot place labels in label area width %d, minimum is %d", labelWidth, min)
|
||||
}
|
||||
|
||||
var labels []*Label
|
||||
for row, l := range stringLabels {
|
||||
label, err := rowLabel(row, l, labelWidth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels = append(labels, label)
|
||||
}
|
||||
|
||||
return labels, nil
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// rowLabel returns one label for the specified row.
|
||||
// The row is the Y coordinate of the row, Y coordinates grow down.
|
||||
func rowLabel(row int, label string, labelWidth int) (*Label, error) {
|
||||
// The area available for the label
|
||||
ar := image.Rect(0, row, labelWidth, row+1)
|
||||
|
||||
pos, err := alignfor.Text(ar, label, align.HorizontalRight, align.VerticalMiddle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to align the label value: %v", err)
|
||||
}
|
||||
|
||||
return &Label{
|
||||
Text: label,
|
||||
Pos: pos,
|
||||
}, nil
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// xLabels returns labels that should be placed under the X axis.
|
||||
// xLabels returns labels that should be placed under the cells.
|
||||
// Labels are returned with X coordinates in ascending order.
|
||||
// X coordinates grow right.
|
||||
func xLabels(yEnd image.Point, graphWidth int, stringLabels []string, cellWidth int) ([]*Label, error) {
|
||||
var ret []*Label
|
||||
|
||||
length, index := paddedLabelLength(graphWidth, LongestString(stringLabels), cellWidth)
|
||||
|
||||
for x := yEnd.X + 1; x <= graphWidth && index < len(stringLabels); x += length {
|
||||
ar := image.Rect(x, yEnd.Y, x+length, yEnd.Y+1)
|
||||
pos, err := alignfor.Text(ar, stringLabels[index], align.HorizontalCenter, align.VerticalMiddle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to align the label value: %v", err)
|
||||
}
|
||||
|
||||
l := &Label{
|
||||
Text: stringLabels[index],
|
||||
Pos: pos,
|
||||
}
|
||||
index += length / cellWidth
|
||||
ret = append(ret, l)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// paddedLabelLength calculates the length of the padded label and
|
||||
|
@ -110,13 +60,5 @@ func xLabels(yEnd image.Point, graphWidth int, stringLabels []string, cellWidth
|
|||
// the label belongs to the middle column of the three columns,
|
||||
// and the padded length is 3*3, which is 9.
|
||||
func paddedLabelLength(graphWidth, longest, cellWidth int) (l, index int) {
|
||||
l, index = 0, 0
|
||||
for i := longest/cellWidth + 1; i < graphWidth/cellWidth; i++ {
|
||||
if (i*cellWidth-longest)%2 == 0 {
|
||||
l = i * cellWidth
|
||||
index = i / 2
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ type Option interface {
|
|||
|
||||
// options stores the provided options.
|
||||
type options struct {
|
||||
// The default value is 3
|
||||
cellWidth int
|
||||
xLabelCellOpts []cell.Option
|
||||
yLabelCellOpts []cell.Option
|
||||
|
|
Loading…
Reference in New Issue