mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-22 15:11:02 +00:00
165 lines
3.8 KiB
Go
165 lines
3.8 KiB
Go
package asciigraph
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
)
|
|
|
|
// Plot returns ascii graph for a series.
|
|
func Plot(series []float64, options ...Option) string {
|
|
var logMaximum float64
|
|
config := configure(config{
|
|
Offset: 3,
|
|
}, options)
|
|
|
|
if config.Width > 0 {
|
|
series = interpolateArray(series, config.Width)
|
|
}
|
|
|
|
minimum, maximum := minMaxFloat64Slice(series)
|
|
if config.Min != nil && *config.Min < minimum {
|
|
minimum = *config.Min
|
|
}
|
|
if config.Max != nil && *config.Max > maximum {
|
|
maximum = *config.Max
|
|
}
|
|
|
|
interval := math.Abs(maximum - minimum)
|
|
|
|
if config.Height <= 0 {
|
|
if int(interval) <= 0 {
|
|
config.Height = int(interval * math.Pow10(int(math.Ceil(-math.Log10(interval)))))
|
|
} else {
|
|
config.Height = int(interval)
|
|
}
|
|
}
|
|
|
|
if config.Offset <= 0 {
|
|
config.Offset = 3
|
|
}
|
|
|
|
var min2 float64
|
|
var max2 float64
|
|
var ratio float64
|
|
padding := 0.0 // if we set height to 10 but min and max are both 0, we need 10 units of padding
|
|
if interval != 0 {
|
|
ratio = float64(config.Height) / interval
|
|
} else {
|
|
ratio = 1
|
|
if config.Height > 0 {
|
|
padding = float64(config.Height)
|
|
}
|
|
}
|
|
|
|
min2 = round(minimum * ratio)
|
|
max2 = round((maximum + padding) * ratio)
|
|
|
|
intmin2 := int(min2)
|
|
intmax2 := int(max2)
|
|
|
|
rows := int(math.Abs(float64(intmax2 - intmin2)))
|
|
width := len(series) + config.Offset
|
|
|
|
var plot [][]string
|
|
|
|
// initialise empty 2D grid
|
|
for i := 0; i < rows+1; i++ {
|
|
var line []string
|
|
for j := 0; j < width; j++ {
|
|
line = append(line, " ")
|
|
}
|
|
plot = append(plot, line)
|
|
}
|
|
|
|
precision := 2
|
|
logMaximum = math.Log10(math.Max(math.Abs(maximum), math.Abs(minimum))) //to find number of zeros after decimal
|
|
if minimum == float64(0) && maximum == float64(0) {
|
|
logMaximum = float64(-1)
|
|
}
|
|
|
|
if logMaximum < 0 {
|
|
// negative log
|
|
if math.Mod(logMaximum, 1) != 0 {
|
|
// non-zero digits after decimal
|
|
precision = precision + int(math.Abs(logMaximum))
|
|
} else {
|
|
precision = precision + int(math.Abs(logMaximum)-1.0)
|
|
}
|
|
} else if logMaximum > 2 {
|
|
precision = 0
|
|
}
|
|
|
|
maxNumLength := len(fmt.Sprintf("%0.*f", precision, maximum))
|
|
minNumLength := len(fmt.Sprintf("%0.*f", precision, minimum))
|
|
maxWidth := int(math.Max(float64(maxNumLength), float64(minNumLength)))
|
|
|
|
// axis and labels
|
|
for y := intmin2; y < intmax2+1; y++ {
|
|
var magnitude float64
|
|
if rows > 0 {
|
|
magnitude = maximum - (float64(y-intmin2) * interval / float64(rows))
|
|
} else {
|
|
magnitude = float64(y)
|
|
}
|
|
|
|
label := fmt.Sprintf("%*.*f", maxWidth+1, precision, magnitude)
|
|
w := y - intmin2
|
|
h := int(math.Max(float64(config.Offset)-float64(len(label)), 0))
|
|
|
|
plot[w][h] = label
|
|
if y == 0 {
|
|
plot[w][config.Offset-1] = "┼"
|
|
} else {
|
|
plot[w][config.Offset-1] = "┤"
|
|
}
|
|
}
|
|
|
|
y0 := int(round(series[0]*ratio) - min2)
|
|
var y1 int
|
|
|
|
plot[rows-y0][config.Offset-1] = "┼" // first value
|
|
|
|
for x := 0; x < len(series)-1; x++ { // plot the line
|
|
y0 = int(round(series[x+0]*ratio) - float64(intmin2))
|
|
y1 = int(round(series[x+1]*ratio) - float64(intmin2))
|
|
if y0 == y1 {
|
|
plot[rows-y0][x+config.Offset] = "─"
|
|
} else {
|
|
if y0 > y1 {
|
|
plot[rows-y1][x+config.Offset] = "╰"
|
|
plot[rows-y0][x+config.Offset] = "╮"
|
|
} else {
|
|
plot[rows-y1][x+config.Offset] = "╭"
|
|
plot[rows-y0][x+config.Offset] = "╯"
|
|
}
|
|
|
|
start := int(math.Min(float64(y0), float64(y1))) + 1
|
|
end := int(math.Max(float64(y0), float64(y1)))
|
|
for y := start; y < end; y++ {
|
|
plot[rows-y][x+config.Offset] = "│"
|
|
}
|
|
}
|
|
}
|
|
|
|
// join columns
|
|
var lines bytes.Buffer
|
|
for h, horizontal := range plot {
|
|
if h != 0 {
|
|
lines.WriteRune('\n')
|
|
}
|
|
for _, v := range horizontal {
|
|
lines.WriteString(v)
|
|
}
|
|
}
|
|
|
|
// add caption if not empty
|
|
if config.Caption != "" {
|
|
lines.WriteRune('\n')
|
|
lines.WriteString(strings.Repeat(" ", config.Offset+maxWidth+2))
|
|
lines.WriteString(config.Caption)
|
|
}
|
|
|
|
return lines.String()
|
|
}
|