1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
package dashboard
import "math"
var sparkChars = []rune("▁▂▃▄▅▆▇█")
func renderSparkline(data []float64, width int) string {
if len(data) == 0 || width <= 0 {
return ""
}
samples := sampleForWidth(data, width)
min, max := minMax(samples)
if min == max {
if min == 0 {
return repeatRune(' ', width)
}
return repeatRune('▁', width)
}
row := make([]rune, width)
scale := 16.0
denom := max - min
for i, value := range samples {
level := int(math.Round((value - min) / denom * scale))
if level < 0 {
level = 0
}
if level > 16 {
level = 16
}
// Collapse the previous two-row 0..16 scale to a single-row 0..7 scale.
oneRow := level / 2
if oneRow < 0 {
oneRow = 0
}
if oneRow > 7 {
oneRow = 7
}
row[i] = sparkChars[oneRow]
}
return string(row)
}
func renderLabeledSparkline(label string, data []float64, width int) string {
spark := renderSparkline(data, width)
if spark == "" {
return label
}
return label + " " + spark
}
func sampleForWidth(data []float64, width int) []float64 {
if width <= 0 || len(data) == 0 {
return nil
}
if width < len(data) {
start := len(data) - width
return append([]float64(nil), data[start:]...)
}
if width == len(data) {
return append([]float64(nil), data...)
}
if len(data) == 1 {
out := make([]float64, width)
for i := range out {
out[i] = data[0]
}
return out
}
out := make([]float64, width)
srcLast := len(data) - 1
dstLast := width - 1
for i := 0; i < width; i++ {
// Nearest-neighbor upsampling preserves the original series shape
// without introducing interpolated spikes between samples.
srcIdx := int(math.Round(float64(i) * float64(srcLast) / float64(dstLast)))
if srcIdx < 0 {
srcIdx = 0
}
if srcIdx > srcLast {
srcIdx = srcLast
}
out[i] = data[srcIdx]
}
return out
}
func minMax(values []float64) (float64, float64) {
min := values[0]
max := values[0]
for _, v := range values[1:] {
if v < min {
min = v
}
if v > max {
max = v
}
}
return min, max
}
func repeatRune(r rune, count int) string {
if count <= 0 {
return ""
}
out := make([]rune, count)
for i := range out {
out[i] = r
}
return string(out)
}
|