diff options
author | Matthew Heon <matthew.heon@gmail.com> | 2017-11-01 11:24:59 -0400 |
---|---|---|
committer | Matthew Heon <matthew.heon@gmail.com> | 2017-11-01 11:24:59 -0400 |
commit | a031b83a09a8628435317a03f199cdc18b78262f (patch) | |
tree | bc017a96769ce6de33745b8b0b1304ccf38e9df0 /vendor/github.com/buger/goterm/plot.go | |
parent | 2b74391cd5281f6fdf391ff8ad50fd1490f6bf89 (diff) | |
download | podman-a031b83a09a8628435317a03f199cdc18b78262f.tar.gz podman-a031b83a09a8628435317a03f199cdc18b78262f.tar.bz2 podman-a031b83a09a8628435317a03f199cdc18b78262f.zip |
Initial checkin from CRI-O repo
Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
Diffstat (limited to 'vendor/github.com/buger/goterm/plot.go')
-rw-r--r-- | vendor/github.com/buger/goterm/plot.go | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/vendor/github.com/buger/goterm/plot.go b/vendor/github.com/buger/goterm/plot.go new file mode 100644 index 000000000..77b9fb097 --- /dev/null +++ b/vendor/github.com/buger/goterm/plot.go @@ -0,0 +1,328 @@ +package goterm + +import ( + "fmt" + "math" + "strings" +) + +const ( + AXIS_LEFT = iota + AXIS_RIGHT +) + +const ( + DRAW_INDEPENDENT = 1 << iota + DRAW_RELATIVE +) + +type DataTable struct { + columns []string + + rows [][]float64 +} + +func (d *DataTable) AddColumn(name string) { + d.columns = append(d.columns, name) +} + +func (d *DataTable) AddRow(elms ...float64) { + d.rows = append(d.rows, elms) +} + +type Chart interface { + Draw(data DataTable, flags int) string +} + +type LineChart struct { + Buf []string + chartBuf []string + + data *DataTable + + Width int + Height int + + chartHeight int + chartWidth int + + paddingX int + + paddingY int + + Flags int +} + +func genBuf(size int) []string { + buf := make([]string, size) + + for i := 0; i < size; i++ { + buf[i] = " " + } + + return buf +} + +// Format float +func ff(num interface{}) string { + return fmt.Sprintf("%.1f", num) +} + +func NewLineChart(width, height int) *LineChart { + chart := new(LineChart) + chart.Width = width + chart.Height = height + chart.Buf = genBuf(width * height) + + // axis lines + axies text + chart.paddingY = 2 + + return chart +} + +func (c *LineChart) DrawAxes(maxX, minX, maxY, minY float64, index int) { + side := AXIS_LEFT + + if c.Flags&DRAW_INDEPENDENT != 0 { + if index%2 == 0 { + side = AXIS_RIGHT + } + + c.DrawLine(c.paddingX-1, 1, c.Width-c.paddingX, 1, "-") + } else { + c.DrawLine(c.paddingX-1, 1, c.Width-1, 1, "-") + } + + if side == AXIS_LEFT { + c.DrawLine(c.paddingX-1, 1, c.paddingX-1, c.Height-1, "│") + } else { + c.DrawLine(c.Width-c.paddingX, 1, c.Width-c.paddingX, c.Height-1, "│") + } + + left := 0 + if side == AXIS_RIGHT { + left = c.Width - c.paddingX + 1 + } + + if c.Flags&DRAW_RELATIVE != 0 { + c.writeText(ff(minY), left, 1) + } else { + if minY > 0 { + c.writeText("0", left, 1) + } else { + c.writeText(ff(minY), left, 1) + } + } + + c.writeText(ff(maxY), left, c.Height-1) + + c.writeText(ff(minX), c.paddingX, 0) + + x_col := c.data.columns[0] + c.writeText(c.data.columns[0], c.Width/2-len(x_col)/2, 1) + + if c.Flags&DRAW_INDEPENDENT != 0 || len(c.data.columns) < 3 { + col := c.data.columns[index] + + for idx, char := range strings.Split(col, "") { + start_from := c.Height/2 + len(col)/2 - idx + + if side == AXIS_LEFT { + c.writeText(char, c.paddingX-1, start_from) + } else { + c.writeText(char, c.Width-c.paddingX, start_from) + } + } + } + + if c.Flags&DRAW_INDEPENDENT != 0 { + c.writeText(ff(maxX), c.Width-c.paddingX-len(ff(maxX)), 0) + } else { + c.writeText(ff(maxX), c.Width-len(ff(maxX)), 0) + } +} + +func (c *LineChart) writeText(text string, x, y int) { + coord := y*c.Width + x + + for idx, char := range strings.Split(text, "") { + c.Buf[coord+idx] = char + } +} + +func (c *LineChart) Draw(data *DataTable) (out string) { + var scaleY, scaleX float64 + + c.data = data + + if c.Flags&DRAW_INDEPENDENT != 0 && len(data.columns) > 3 { + fmt.Println("Error: Can't use DRAW_INDEPENDENT for more then 2 graphs") + return "" + } + + charts := len(data.columns) - 1 + + prevPoint := [2]int{-1, -1} + + maxX, minX, maxY, minY := getBoundaryValues(data, -1) + + c.paddingX = int(math.Max(float64(len(ff(minY))), float64(len(ff(maxY))))) + 1 + + c.chartHeight = c.Height - c.paddingY + + if c.Flags&DRAW_INDEPENDENT != 0 { + c.chartWidth = c.Width - 2*c.paddingX + } else { + c.chartWidth = c.Width - c.paddingX - 1 + } + + scaleX = float64(c.chartWidth) / (maxX - minX) + + if c.Flags&DRAW_RELATIVE != 0 || minY < 0 { + scaleY = float64(c.chartHeight) / (maxY - minY) + } else { + scaleY = float64(c.chartHeight) / maxY + } + + for i := 1; i < charts+1; i++ { + if c.Flags&DRAW_INDEPENDENT != 0 { + maxX, minX, maxY, minY = getBoundaryValues(data, i) + + scaleX = float64(c.chartWidth-1) / (maxX - minX) + scaleY = float64(c.chartHeight) / maxY + + if c.Flags&DRAW_RELATIVE != 0 || minY < 0 { + scaleY = float64(c.chartHeight) / (maxY - minY) + } + } + + symbol := Color("•", i) + + c_data := getChartData(data, i) + + for _, point := range c_data { + x := int((point[0]-minX)*scaleX) + c.paddingX + y := int((point[1])*scaleY) + c.paddingY + + if c.Flags&DRAW_RELATIVE != 0 || minY < 0 { + y = int((point[1]-minY)*scaleY) + c.paddingY + } + + if prevPoint[0] == -1 { + prevPoint[0] = x + prevPoint[1] = y + } + + if prevPoint[0] <= x { + c.DrawLine(prevPoint[0], prevPoint[1], x, y, symbol) + } + + prevPoint[0] = x + prevPoint[1] = y + } + + c.DrawAxes(maxX, minX, maxY, minY, i) + } + + for row := c.Height - 1; row >= 0; row-- { + out += strings.Join(c.Buf[row*c.Width:(row+1)*c.Width], "") + "\n" + } + + return +} + +func (c *LineChart) DrawLine(x0, y0, x1, y1 int, symbol string) { + drawLine(x0, y0, x1, y1, func(x, y int) { + coord := y*c.Width + x + + if coord > 0 && coord < len(c.Buf) { + c.Buf[coord] = symbol + } + }) +} + +func getBoundaryValues(data *DataTable, index int) (maxX, minX, maxY, minY float64) { + maxX = data.rows[0][0] + minX = data.rows[0][0] + maxY = data.rows[0][1] + minY = data.rows[0][1] + + for _, r := range data.rows { + maxX = math.Max(maxX, r[0]) + minX = math.Min(minX, r[0]) + + for idx, c := range r { + if idx > 0 { + if index == -1 || index == idx { + maxY = math.Max(maxY, c) + minY = math.Min(minY, c) + } + } + } + } + + if maxY > 0 { + maxY = maxY * 1.1 + } else { + maxY = maxY * 0.9 + } + + if minY > 0 { + minY = minY * 0.9 + } else { + minY = minY * 1.1 + } + + return +} + +// DataTable can contain data for multiple graphs, we need to extract only 1 +func getChartData(data *DataTable, index int) (out [][]float64) { + for _, r := range data.rows { + out = append(out, []float64{r[0], r[index]}) + } + + return +} + +// Algorithm for drawing line between two points +// +// http://en.wikipedia.org/wiki/Bresenham's_line_algorithm +func drawLine(x0, y0, x1, y1 int, plot func(int, int)) { + dx := x1 - x0 + if dx < 0 { + dx = -dx + } + dy := y1 - y0 + if dy < 0 { + dy = -dy + } + var sx, sy int + if x0 < x1 { + sx = 1 + } else { + sx = -1 + } + if y0 < y1 { + sy = 1 + } else { + sy = -1 + } + err := dx - dy + + for { + plot(x0, y0) + if x0 == x1 && y0 == y1 { + break + } + e2 := 2 * err + if e2 > -dy { + err -= dy + x0 += sx + } + if e2 < dx { + err += dx + y0 += sy + } + } +} |