mirror of https://github.com/gizak/termui.git
parent
0916e5ebce
commit
a2346d4ece
85
chart.go
85
chart.go
|
@ -29,6 +29,11 @@ var braillePatterns = map[[2]int]rune{
|
|||
[2]int{3, 3}: '⠉',
|
||||
}
|
||||
|
||||
var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'}
|
||||
var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
|
||||
|
||||
//var singleBraille = [4]rune{'⣀', '⠤', '⠒', '⠉'}
|
||||
|
||||
type LineChart struct {
|
||||
Block
|
||||
Data []float64
|
||||
|
@ -36,7 +41,7 @@ type LineChart struct {
|
|||
Mode string // braille | dot
|
||||
DotStyle rune
|
||||
LineColor Attribute
|
||||
scale float64
|
||||
scale float64 // data span per cell on y-axis
|
||||
AxesColor Attribute
|
||||
drawingX int
|
||||
drawingY int
|
||||
|
@ -65,31 +70,45 @@ func NewLineChart() *LineChart {
|
|||
}
|
||||
|
||||
// one cell contains two data points
|
||||
// so the capicity is 2x as dot-mode
|
||||
func (lc *LineChart) renderBraille() []Point {
|
||||
ps := []Point{}
|
||||
getBaseMod := func(d float64) (b, m int) {
|
||||
b = int((d - lc.minY) / lc.scale)
|
||||
m = int(((d-lc.minY)-float64(b)*lc.scale)/0.25 + 0.5)
|
||||
|
||||
// return: b -> which cell should the point be in
|
||||
// m -> in the cell, divided into 4 equal height levels, which subcell?
|
||||
getPos := func(d float64) (b, m int) {
|
||||
cnt4 := int((d-lc.bottomValue)/(lc.scale/4) + 0.5)
|
||||
b = cnt4 / 4
|
||||
m = cnt4 % 4
|
||||
return
|
||||
}
|
||||
for i := 0; i+1 < len(lc.Data) && i/2 < lc.axisXWidth; i += 2 {
|
||||
b0, m0 := getBaseMod(lc.Data[i])
|
||||
b1, m1 := getBaseMod(lc.Data[i+1])
|
||||
// plot points
|
||||
for i := 0; 2*i+1 < len(lc.Data) && i < lc.axisXWidth; i++ {
|
||||
b0, m0 := getPos(lc.Data[2*i])
|
||||
b1, m1 := getPos(lc.Data[2*i+1])
|
||||
|
||||
if b0 > b1 {
|
||||
m1 = 0
|
||||
}
|
||||
if b0 < b1 {
|
||||
m1 = 3
|
||||
if b0 == b1 {
|
||||
p := Point{}
|
||||
p.Ch = braillePatterns[[2]int{m0, m1}]
|
||||
p.Bg = lc.BgColor
|
||||
p.Fg = lc.LineColor
|
||||
p.Y = lc.innerY + lc.innerHeight - 3 - b0
|
||||
p.X = lc.innerX + lc.labelYSpace + 1 + i
|
||||
ps = append(ps, p)
|
||||
} else {
|
||||
p0 := newPointWithAttrs(lSingleBraille[m0],
|
||||
lc.innerX+lc.labelYSpace+1+i,
|
||||
lc.innerY+lc.innerHeight-3-b0,
|
||||
lc.LineColor,
|
||||
lc.BgColor)
|
||||
p1 := newPointWithAttrs(rSingleBraille[m1],
|
||||
lc.innerX+lc.labelYSpace+1+i,
|
||||
lc.innerY+lc.innerHeight-3-b1,
|
||||
lc.LineColor,
|
||||
lc.BgColor)
|
||||
ps = append(ps, p0, p1)
|
||||
}
|
||||
|
||||
p := Point{}
|
||||
p.Ch = braillePatterns[[2]int{m0, m1}]
|
||||
p.Bg = lc.BgColor
|
||||
p.Fg = lc.LineColor
|
||||
p.Y = lc.innerY + lc.innerHeight - 3 - b0
|
||||
p.X = lc.innerX + lc.labelYSpace + 1 + i/2
|
||||
ps = append(ps, p)
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
@ -102,7 +121,7 @@ func (lc *LineChart) renderDot() []Point {
|
|||
p.Fg = lc.LineColor
|
||||
p.Bg = lc.BgColor
|
||||
p.X = lc.innerX + lc.labelYSpace + 1 + i
|
||||
p.Y = lc.innerY + lc.innerHeight - 3 - int((lc.Data[i]-lc.minY)/lc.scale+0.5)
|
||||
p.Y = lc.innerY + lc.innerHeight - 3 - int((lc.Data[i]-lc.bottomValue)/lc.scale+0.5)
|
||||
ps = append(ps, p)
|
||||
}
|
||||
|
||||
|
@ -176,9 +195,21 @@ func (lc *LineChart) calcLayout() {
|
|||
}
|
||||
}
|
||||
|
||||
// lazy increase, to avoid y shaking frequently
|
||||
// update bound Y when drawing is gonna overflow
|
||||
lc.minY = lc.Data[0]
|
||||
lc.maxY = lc.Data[0]
|
||||
for _, v := range lc.Data {
|
||||
|
||||
// valid visible range
|
||||
vrange := lc.innerWidth
|
||||
if lc.Mode == "braille" {
|
||||
vrange = 2 * lc.innerWidth
|
||||
}
|
||||
if vrange > len(lc.Data) {
|
||||
vrange = len(lc.Data)
|
||||
}
|
||||
|
||||
for _, v := range lc.Data[:vrange] {
|
||||
if v > lc.maxY {
|
||||
lc.maxY = v
|
||||
}
|
||||
|
@ -187,10 +218,14 @@ func (lc *LineChart) calcLayout() {
|
|||
}
|
||||
}
|
||||
|
||||
lc.topValue = lc.maxY * 1.2
|
||||
lc.bottomValue = lc.minY * 0.8 //- 0.05*(lc.maxY-lc.minY)
|
||||
if lc.minY < 0 {
|
||||
lc.bottomValue = lc.minY * 1.2
|
||||
span := lc.maxY - lc.minY
|
||||
|
||||
if lc.minY < lc.bottomValue {
|
||||
lc.bottomValue = lc.minY - 0.2*span
|
||||
}
|
||||
|
||||
if lc.maxY > lc.topValue {
|
||||
lc.topValue = lc.maxY + 0.2*span
|
||||
}
|
||||
|
||||
lc.axisYHeight = lc.innerHeight - 2
|
||||
|
|
|
@ -61,17 +61,17 @@ func main() {
|
|||
sp.Y = 4
|
||||
sp.X = 25
|
||||
|
||||
lc := ui.NewLineChart()
|
||||
sinps := (func() []float64 {
|
||||
n := 100
|
||||
n := 220
|
||||
ps := make([]float64, n)
|
||||
for i := range ps {
|
||||
ps[i] = 1 + math.Sin(float64(i)/4)
|
||||
ps[i] = 1 + math.Sin(float64(i)/5)
|
||||
}
|
||||
return ps
|
||||
})()
|
||||
|
||||
lc.Border.Label = "Line Chart"
|
||||
lc := ui.NewLineChart()
|
||||
lc.Border.Label = "dot-mode Line Chart"
|
||||
lc.Data = sinps
|
||||
lc.Width = 50
|
||||
lc.Height = 11
|
||||
|
@ -94,21 +94,8 @@ func main() {
|
|||
bc.NumColor = ui.ColorBlack
|
||||
|
||||
lc1 := ui.NewLineChart()
|
||||
lc1.Border.Label = "Line Chart"
|
||||
rndwalk := (func() []float64 {
|
||||
n := 150
|
||||
d := make([]float64, n)
|
||||
for i := 1; i < n; i++ {
|
||||
if i < 20 {
|
||||
d[i] = d[i-1] + 0.01
|
||||
}
|
||||
if i > 20 {
|
||||
d[i] = d[i-1] - 0.05
|
||||
}
|
||||
}
|
||||
return d
|
||||
})()
|
||||
lc1.Data = rndwalk
|
||||
lc1.Border.Label = "braille-mode Line Chart"
|
||||
lc1.Data = sinps
|
||||
lc1.Width = 26
|
||||
lc1.Height = 11
|
||||
lc1.X = 51
|
||||
|
@ -130,7 +117,7 @@ func main() {
|
|||
sp.Lines[0].Data = spdata[t%10:]
|
||||
sp.Lines[1].Data = spdata[t/2%10:]
|
||||
lc.Data = sinps[t/2:]
|
||||
lc1.Data = rndwalk[t:]
|
||||
lc1.Data = sinps[2*t:]
|
||||
bc.Data = bcdata[t/2%10:]
|
||||
ui.Render(p, list, g, sp, lc, bc, lc1, p1)
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 844 KiB |
Loading…
Reference in New Issue