986681ef8a
Replace misleading relative-only sparkline with dual-channel design: bar height uses relative scaling (shows stability and anomalies), color+brightness uses absolute thresholds (shows fast vs slow). - Add brightness gradient within color bands (dim→bright as latency increases toward the next threshold) - Pass row background through sparkline rendering so zebra stripes and selection highlights carry through ANSI sequences - Cap sparkline width to 60 (matches maxHistoryLen) and column width to 62 to eliminate trailing dead space - Quiet group sparkline: subtle dots for healthy, bold red for down - Add braille subpixel canvas (ported from meridian) for future multi-row graph use
104 lines
1.9 KiB
Go
104 lines
1.9 KiB
Go
package tui
|
||
|
||
// braillePlane is a subpixel canvas where each terminal cell maps to a 2×4
|
||
// dot grid, rendered via Unicode braille (U+2800..U+28FF).
|
||
type braillePlane struct {
|
||
wCells, hCells int
|
||
wDots, hDots int
|
||
dots []bool
|
||
}
|
||
|
||
func newBraillePlane(wCells, hCells int) *braillePlane {
|
||
wd, hd := wCells*2, hCells*4
|
||
return &braillePlane{
|
||
wCells: wCells, hCells: hCells,
|
||
wDots: wd, hDots: hd,
|
||
dots: make([]bool, wd*hd),
|
||
}
|
||
}
|
||
|
||
func (p *braillePlane) set(dx, dy int) {
|
||
if dx < 0 || dy < 0 || dx >= p.wDots || dy >= p.hDots {
|
||
return
|
||
}
|
||
p.dots[dy*p.wDots+dx] = true
|
||
}
|
||
|
||
// line draws a Bresenham line between two dot coordinates.
|
||
func (p *braillePlane) line(x0, y0, x1, y1 int) {
|
||
dx := intAbs(x1 - x0)
|
||
sx := 1
|
||
if x0 >= x1 {
|
||
sx = -1
|
||
}
|
||
dy := -intAbs(y1 - y0)
|
||
sy := 1
|
||
if y0 >= y1 {
|
||
sy = -1
|
||
}
|
||
err := dx + dy
|
||
for {
|
||
p.set(x0, y0)
|
||
if x0 == x1 && y0 == y1 {
|
||
return
|
||
}
|
||
e2 := 2 * err
|
||
if e2 >= dy {
|
||
err += dy
|
||
x0 += sx
|
||
}
|
||
if e2 <= dx {
|
||
err += dx
|
||
y0 += sy
|
||
}
|
||
}
|
||
}
|
||
|
||
// fillBelow fills all dots below the topmost lit dot in each column,
|
||
// producing an area-chart effect.
|
||
func (p *braillePlane) fillBelow() {
|
||
for x := 0; x < p.wDots; x++ {
|
||
topY := -1
|
||
for y := 0; y < p.hDots; y++ {
|
||
if p.dots[y*p.wDots+x] {
|
||
topY = y
|
||
break
|
||
}
|
||
}
|
||
if topY >= 0 {
|
||
for y := topY + 1; y < p.hDots; y++ {
|
||
p.dots[y*p.wDots+x] = true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// cellMask builds the U+2800-relative bitmask for one terminal cell.
|
||
func (p *braillePlane) cellMask(cx, cy int) byte {
|
||
type bit struct {
|
||
dx, dy int
|
||
m byte
|
||
}
|
||
bits := [...]bit{
|
||
{0, 0, 0x01}, {0, 1, 0x02}, {0, 2, 0x04},
|
||
{1, 0, 0x08}, {1, 1, 0x10}, {1, 2, 0x20},
|
||
{0, 3, 0x40}, {1, 3, 0x80},
|
||
}
|
||
var mask byte
|
||
for _, b := range bits {
|
||
dx := cx*2 + b.dx
|
||
dy := cy*4 + b.dy
|
||
if dx >= 0 && dx < p.wDots && dy >= 0 && dy < p.hDots && p.dots[dy*p.wDots+dx] {
|
||
mask |= b.m
|
||
}
|
||
}
|
||
return mask
|
||
}
|
||
|
||
func intAbs(n int) int {
|
||
if n < 0 {
|
||
return -n
|
||
}
|
||
return n
|
||
}
|