s/LineChart/Plot and add ScatterPlot mode to it
This commit is contained in:
parent
eeb94f8e14
commit
8c72139752
@ -1,5 +1,12 @@
|
||||
Feel free to search/open an issue if something is missing or confusing from the changelog, since many things have been in flux.
|
||||
|
||||
## 2019/01/24
|
||||
|
||||
Breaking changes:
|
||||
|
||||
- Change LineChart to Plot
|
||||
- Added ScatterPlot mode which plots points instead of lines between points
|
||||
|
||||
## 2019/01/23
|
||||
|
||||
Non breaking changes:
|
||||
|
@ -28,10 +28,10 @@ If you upgrade and notice something is missing or don't like a change, revert th
|
||||
- [BarChart](./_examples/barchart.go)
|
||||
- [Canvas](./_examples/canvas.go)
|
||||
- [Gauge](./_examples/gauge.go)
|
||||
- [LineChart](./_examples/linechart.go)
|
||||
- [List](./_examples/list.go)
|
||||
- [Paragraph](./_examples/paragraph.go)
|
||||
- [PieChart](./_examples/piechart.go)
|
||||
- [Plot](./_examples/plot.go)
|
||||
- [Sparkline](./_examples/sparkline.go)
|
||||
- [StackedBarChart](./_examples/stacked_barchart.go)
|
||||
- [Table](./_examples/table.go)
|
||||
|
@ -1,72 +0,0 @@
|
||||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
|
||||
ui "github.com/gizak/termui"
|
||||
"github.com/gizak/termui/widgets"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := ui.Init(); err != nil {
|
||||
log.Fatalf("failed to initialize termui: %v", err)
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
sinData := func() [][]float64 {
|
||||
n := 220
|
||||
data := make([][]float64, 2)
|
||||
data[0] = make([]float64, n)
|
||||
data[1] = make([]float64, n)
|
||||
for i := 0; i < n; i++ {
|
||||
data[0][i] = 1 + math.Sin(float64(i)/5)
|
||||
data[1][i] = 1 + math.Cos(float64(i)/5)
|
||||
}
|
||||
return data
|
||||
}()
|
||||
|
||||
lc0 := widgets.NewLineChart()
|
||||
lc0.Title = "braille-mode Line Chart"
|
||||
lc0.Data = sinData
|
||||
lc0.SetRect(0, 0, 50, 15)
|
||||
lc0.AxesColor = ui.ColorWhite
|
||||
lc0.LineColors[0] = ui.ColorGreen
|
||||
|
||||
lc1 := widgets.NewLineChart()
|
||||
lc1.Title = "custom Line Chart"
|
||||
lc1.LineType = widgets.DotLine
|
||||
lc1.Data = [][]float64{[]float64{1, 2, 3, 4, 5}}
|
||||
lc1.SetRect(50, 0, 75, 10)
|
||||
lc1.DotChar = '+'
|
||||
lc1.AxesColor = ui.ColorWhite
|
||||
lc1.LineColors[0] = ui.ColorYellow
|
||||
lc1.DrawDirection = widgets.DrawLeft
|
||||
|
||||
lc2 := widgets.NewLineChart()
|
||||
lc2.Title = "dot-mode Line Chart"
|
||||
lc2.LineType = widgets.DotLine
|
||||
lc2.Data = make([][]float64, 2)
|
||||
lc2.Data[0] = sinData[0][4:]
|
||||
lc2.Data[1] = sinData[1][4:]
|
||||
lc2.SetRect(0, 15, 50, 30)
|
||||
lc2.AxesColor = ui.ColorWhite
|
||||
lc2.LineColors[0] = ui.ColorCyan
|
||||
|
||||
ui.Render(lc0, lc1, lc2)
|
||||
|
||||
uiEvents := ui.PollEvents()
|
||||
for {
|
||||
e := <-uiEvents
|
||||
switch e.ID {
|
||||
case "q", "<C-c>":
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
84
_examples/plot.go
Normal file
84
_examples/plot.go
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
|
||||
ui "github.com/gizak/termui"
|
||||
"github.com/gizak/termui/widgets"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := ui.Init(); err != nil {
|
||||
log.Fatalf("failed to initialize termui: %v", err)
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
sinData := func() [][]float64 {
|
||||
n := 220
|
||||
data := make([][]float64, 2)
|
||||
data[0] = make([]float64, n)
|
||||
data[1] = make([]float64, n)
|
||||
for i := 0; i < n; i++ {
|
||||
data[0][i] = 1 + math.Sin(float64(i)/5)
|
||||
data[1][i] = 1 + math.Cos(float64(i)/5)
|
||||
}
|
||||
return data
|
||||
}()
|
||||
|
||||
p0 := widgets.NewPlot()
|
||||
p0.Title = "braille-mode Line Chart"
|
||||
p0.Data = sinData
|
||||
p0.SetRect(0, 0, 50, 15)
|
||||
p0.AxesColor = ui.ColorWhite
|
||||
p0.LineColors[0] = ui.ColorGreen
|
||||
|
||||
p1 := widgets.NewPlot()
|
||||
p1.Title = "custom Line Chart"
|
||||
p1.Marker = widgets.MarkerDot
|
||||
p1.Data = [][]float64{[]float64{1, 2, 3, 4, 5}}
|
||||
p1.SetRect(50, 0, 75, 10)
|
||||
p1.DotRune = '+'
|
||||
p1.AxesColor = ui.ColorWhite
|
||||
p1.LineColors[0] = ui.ColorYellow
|
||||
p1.DrawDirection = widgets.DrawLeft
|
||||
|
||||
p2 := widgets.NewPlot()
|
||||
p2.Title = "dot-mode Scatter Plot"
|
||||
p2.Marker = widgets.MarkerDot
|
||||
p2.Data = make([][]float64, 2)
|
||||
p2.Data[0] = []float64{1, 2, 3, 4, 5}
|
||||
p2.Data[1] = sinData[1][4:]
|
||||
p2.SetRect(0, 15, 50, 30)
|
||||
p2.AxesColor = ui.ColorWhite
|
||||
p2.LineColors[0] = ui.ColorCyan
|
||||
p2.Type = widgets.ScatterPlot
|
||||
|
||||
p3 := widgets.NewPlot()
|
||||
p3.Title = "dot-mode Scatter Plot"
|
||||
p3.Data = make([][]float64, 2)
|
||||
p3.Data[0] = []float64{1, 2, 3, 4, 5}
|
||||
p3.Data[1] = sinData[1][4:]
|
||||
p3.SetRect(45, 15, 80, 30)
|
||||
p3.AxesColor = ui.ColorWhite
|
||||
p3.LineColors[0] = ui.ColorCyan
|
||||
p3.Marker = widgets.MarkerBraille
|
||||
p3.Type = widgets.ScatterPlot
|
||||
|
||||
ui.Render(p0, p1, p2, p3)
|
||||
|
||||
uiEvents := ui.PollEvents()
|
||||
for {
|
||||
e := <-uiEvents
|
||||
switch e.ID {
|
||||
case "q", "<C-c>":
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
@ -54,6 +54,14 @@ func (self *Canvas) Line(p0, p1 image.Point, color Color) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Canvas) Point(p image.Point, color Color) {
|
||||
point := image.Pt(p.X/2, p.Y/4)
|
||||
self.CellMap[point] = Cell{
|
||||
self.CellMap[point].Rune | BRAILLE[p.X%4][p.Y%2],
|
||||
NewStyle(color),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Canvas) Draw(buf *Buffer) {
|
||||
for point, cell := range self.CellMap {
|
||||
if point.In(self.Rectangle) {
|
||||
|
6
theme.go
6
theme.go
@ -31,7 +31,7 @@ type RootTheme struct {
|
||||
|
||||
BarChart BarChartTheme
|
||||
Gauge GaugeTheme
|
||||
LineChart LineChartTheme
|
||||
Plot PlotTheme
|
||||
List ListTheme
|
||||
Paragraph ParagraphTheme
|
||||
PieChart PieChartTheme
|
||||
@ -57,7 +57,7 @@ type GaugeTheme struct {
|
||||
Label Style
|
||||
}
|
||||
|
||||
type LineChartTheme struct {
|
||||
type PlotTheme struct {
|
||||
Lines []Color
|
||||
Axes Color
|
||||
}
|
||||
@ -136,7 +136,7 @@ var Theme = RootTheme{
|
||||
Line: ColorWhite,
|
||||
},
|
||||
|
||||
LineChart: LineChartTheme{
|
||||
Plot: PlotTheme{
|
||||
Lines: StandardColors,
|
||||
Axes: ColorWhite,
|
||||
},
|
||||
|
@ -11,20 +11,25 @@ import (
|
||||
. "github.com/gizak/termui"
|
||||
)
|
||||
|
||||
// LineChart has two modes: braille(default) and dot.
|
||||
// Plot has two modes: line(default) and scatter.
|
||||
// Plot also has two marker types: braille(default) and dot.
|
||||
// A single braille character is a 2x4 grid of dots, so using braille
|
||||
// gives 2x X resolution and 4x Y resolution over dot mode.
|
||||
type LineChart struct {
|
||||
type Plot struct {
|
||||
Block
|
||||
Data [][]float64
|
||||
DataLabels []string
|
||||
|
||||
Data [][]float64
|
||||
DataLabels []string
|
||||
MaxVal float64
|
||||
|
||||
LineColors []Color
|
||||
AxesColor Color // TODO
|
||||
ShowAxes bool
|
||||
|
||||
Marker PlotMarker
|
||||
DotRune rune
|
||||
Type PlotType
|
||||
HorizontalScale int
|
||||
LineType LineType
|
||||
DotChar rune
|
||||
LineColors []Color
|
||||
AxesColor Color // TODO
|
||||
MaxVal float64
|
||||
ShowAxes bool
|
||||
DrawDirection DrawDirection // TODO
|
||||
}
|
||||
|
||||
@ -35,11 +40,18 @@ const (
|
||||
yAxisLabelsGap = 1
|
||||
)
|
||||
|
||||
type LineType int
|
||||
type PlotType uint
|
||||
|
||||
const (
|
||||
BrailleLine LineType = iota
|
||||
DotLine
|
||||
LineChart PlotType = iota
|
||||
ScatterPlot
|
||||
)
|
||||
|
||||
type PlotMarker uint
|
||||
|
||||
const (
|
||||
MarkerBraille PlotMarker = iota
|
||||
MarkerDot
|
||||
)
|
||||
|
||||
type DrawDirection uint
|
||||
@ -49,60 +61,93 @@ const (
|
||||
DrawRight
|
||||
)
|
||||
|
||||
func NewLineChart() *LineChart {
|
||||
return &LineChart{
|
||||
func NewPlot() *Plot {
|
||||
return &Plot{
|
||||
Block: *NewBlock(),
|
||||
LineColors: Theme.LineChart.Lines,
|
||||
AxesColor: Theme.LineChart.Axes,
|
||||
LineType: BrailleLine,
|
||||
DotChar: DOT,
|
||||
LineColors: Theme.Plot.Lines,
|
||||
AxesColor: Theme.Plot.Axes,
|
||||
Marker: MarkerBraille,
|
||||
DotRune: DOT,
|
||||
Data: [][]float64{},
|
||||
HorizontalScale: 1,
|
||||
DrawDirection: DrawRight,
|
||||
ShowAxes: true,
|
||||
Type: LineChart,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LineChart) renderBraille(buf *Buffer, drawArea image.Rectangle, maxVal float64) {
|
||||
func (self *Plot) renderBraille(buf *Buffer, drawArea image.Rectangle, maxVal float64) {
|
||||
canvas := NewCanvas()
|
||||
canvas.Rectangle = drawArea
|
||||
|
||||
for i, line := range self.Data {
|
||||
previousHeight := int((line[1] / maxVal) * float64(drawArea.Dy()-1))
|
||||
for j, val := range line[1:] {
|
||||
height := int((val / maxVal) * float64(drawArea.Dy()-1))
|
||||
canvas.Line(
|
||||
image.Pt(
|
||||
(drawArea.Min.X+(j*self.HorizontalScale))*2,
|
||||
(drawArea.Max.Y-previousHeight-1)*4,
|
||||
),
|
||||
image.Pt(
|
||||
(drawArea.Min.X+((j+1)*self.HorizontalScale))*2,
|
||||
(drawArea.Max.Y-height-1)*4,
|
||||
),
|
||||
SelectColor(self.LineColors, i),
|
||||
)
|
||||
previousHeight = height
|
||||
switch self.Type {
|
||||
case ScatterPlot:
|
||||
for i, line := range self.Data {
|
||||
for j, val := range line {
|
||||
height := int((val / maxVal) * float64(drawArea.Dy()-1))
|
||||
canvas.Point(
|
||||
image.Pt(
|
||||
(drawArea.Min.X+(j*self.HorizontalScale))*2,
|
||||
(drawArea.Max.Y-height-1)*4,
|
||||
),
|
||||
SelectColor(self.LineColors, i),
|
||||
)
|
||||
}
|
||||
}
|
||||
case LineChart:
|
||||
for i, line := range self.Data {
|
||||
previousHeight := int((line[1] / maxVal) * float64(drawArea.Dy()-1))
|
||||
for j, val := range line[1:] {
|
||||
height := int((val / maxVal) * float64(drawArea.Dy()-1))
|
||||
canvas.Line(
|
||||
image.Pt(
|
||||
(drawArea.Min.X+(j*self.HorizontalScale))*2,
|
||||
(drawArea.Max.Y-previousHeight-1)*4,
|
||||
),
|
||||
image.Pt(
|
||||
(drawArea.Min.X+((j+1)*self.HorizontalScale))*2,
|
||||
(drawArea.Max.Y-height-1)*4,
|
||||
),
|
||||
SelectColor(self.LineColors, i),
|
||||
)
|
||||
previousHeight = height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canvas.Draw(buf)
|
||||
}
|
||||
|
||||
func (self *LineChart) renderDot(buf *Buffer, drawArea image.Rectangle, maxVal float64) {
|
||||
for i, line := range self.Data {
|
||||
for j := 0; j < len(line) && j*self.HorizontalScale < drawArea.Dx(); j++ {
|
||||
val := line[j]
|
||||
height := int((val / maxVal) * float64(drawArea.Dy()-1))
|
||||
buf.SetCell(
|
||||
NewCell(self.DotChar, NewStyle(SelectColor(self.LineColors, i))),
|
||||
image.Pt(drawArea.Min.X+(j*self.HorizontalScale), drawArea.Max.Y-1-height),
|
||||
)
|
||||
func (self *Plot) renderDot(buf *Buffer, drawArea image.Rectangle, maxVal float64) {
|
||||
switch self.Type {
|
||||
case ScatterPlot:
|
||||
for i, line := range self.Data {
|
||||
for j, val := range line {
|
||||
height := int((val / maxVal) * float64(drawArea.Dy()-1))
|
||||
point := image.Pt(drawArea.Min.X+(j*self.HorizontalScale), drawArea.Max.Y-1-height)
|
||||
if point.In(drawArea) {
|
||||
buf.SetCell(
|
||||
NewCell(self.DotRune, NewStyle(SelectColor(self.LineColors, i))),
|
||||
point,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
case LineChart:
|
||||
for i, line := range self.Data {
|
||||
for j := 0; j < len(line) && j*self.HorizontalScale < drawArea.Dx(); j++ {
|
||||
val := line[j]
|
||||
height := int((val / maxVal) * float64(drawArea.Dy()-1))
|
||||
buf.SetCell(
|
||||
NewCell(self.DotRune, NewStyle(SelectColor(self.LineColors, i))),
|
||||
image.Pt(drawArea.Min.X+(j*self.HorizontalScale), drawArea.Max.Y-1-height),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LineChart) plotAxes(buf *Buffer, maxVal float64) {
|
||||
func (self *Plot) plotAxes(buf *Buffer, maxVal float64) {
|
||||
// draw origin cell
|
||||
buf.SetCell(
|
||||
NewCell(BOTTOM_LEFT, NewStyle(ColorWhite)),
|
||||
@ -153,7 +198,7 @@ func (self *LineChart) plotAxes(buf *Buffer, maxVal float64) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LineChart) Draw(buf *Buffer) {
|
||||
func (self *Plot) Draw(buf *Buffer) {
|
||||
self.Block.Draw(buf)
|
||||
|
||||
maxVal := self.MaxVal
|
||||
@ -173,9 +218,10 @@ func (self *LineChart) Draw(buf *Buffer) {
|
||||
)
|
||||
}
|
||||
|
||||
if self.LineType == BrailleLine {
|
||||
switch self.Marker {
|
||||
case MarkerBraille:
|
||||
self.renderBraille(buf, drawArea, maxVal)
|
||||
} else {
|
||||
case MarkerDot:
|
||||
self.renderDot(buf, drawArea, maxVal)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user