new feature - plot widget (linechart, scatter)

Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
This commit is contained in:
Navid Yaghoobi 2022-12-12 21:36:06 +11:00
parent f9a1efe3f5
commit b1e54eab00
8 changed files with 646 additions and 47 deletions

View File

@ -5,10 +5,20 @@
[![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/navidys/tvxwidgets)
tvxwidgets provides extra widgets for [tview](https://github.com/rivo/tview).
`NOTE:` The project is at its early stages and under development, feel free to contribute and report bugs.
![Screenshot](demo.gif)
## Widgets
* [bar chart](./demos/barchart/)
* [activity mode gauge](./demos/gauge_am/)
* [percentage mode gauge](./demos/gauge_pm/)
* [utilisation mode gauge](./demos/gauge_um/)
* [message dialog (info and error)](./demos/dialog/)
* [spinner](./demos/spinner/)
* [plot (linechart, scatter)](./demos/plot/)
## Example
```go
@ -48,12 +58,3 @@ func main() {
}
```
## Widgets
* bar chart
* activity mode gauge
* percentage mode gauge
* utilisation mode gauge
* message dialog (info and error)
* spinner

BIN
demo.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 277 KiB

View File

@ -2,6 +2,7 @@
package main
import (
"math"
"math/rand"
"time"
@ -14,40 +15,34 @@ func main() {
app := tview.NewApplication()
// spinners
spinners := [][]*tvxwidgets.Spinner{
{
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerDotsCircling),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerDotsUpDown),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerBounce),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerLine),
},
{
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerCircleQuarters),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerSquareCorners),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerCircleHalves),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerCorners),
},
{
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerArrows),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerHamburger),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerStack),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerStar),
},
{
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerGrowHorizontal),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerGrowVertical),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerBoxBounce),
tvxwidgets.NewSpinner().SetCustomStyle([]rune{'🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚'}),
},
spinners := []*tvxwidgets.Spinner{
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerDotsCircling),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerDotsUpDown),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerBounce),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerLine),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerCircleQuarters),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerSquareCorners),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerCircleHalves),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerCorners),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerArrows),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerHamburger),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerStack),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerStar),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerGrowHorizontal),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerGrowVertical),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerBoxBounce),
tvxwidgets.NewSpinner().SetCustomStyle([]rune{'🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚'}),
}
spinnerGrid := tview.NewGrid()
spinnerGrid.SetBorder(true).SetTitle("Spinners")
spinnerRow := tview.NewFlex().SetDirection(tview.FlexColumn)
spinnerRow.SetBorder(true).SetTitle("Spinners")
for rowIdx, row := range spinners {
for colIdx, spinner := range row {
spinnerGrid.AddItem(spinner, rowIdx, colIdx, 1, 1, 1, 1, false)
}
for _, spinner := range spinners {
spinnerRow.AddItem(spinner, 0, 1, false)
}
// bar graph
@ -96,18 +91,87 @@ func main() {
utilFlex.SetTitle("utilisation mode gauge")
utilFlex.SetBorder(true)
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
}()
bmLineChart := tvxwidgets.NewPlot()
bmLineChart.SetBorder(true)
bmLineChart.SetTitle("line chart (braille mode)")
bmLineChart.SetLineColor([]tcell.Color{
tcell.ColorSteelBlue,
tcell.ColorGreen,
})
bmLineChart.SetMarker(tvxwidgets.PlotMarkerBraille)
bmLineChart.SetData(sinData)
dmLineChart := tvxwidgets.NewPlot()
dmLineChart.SetBorder(true)
dmLineChart.SetTitle("line chart (dot mode)")
dmLineChart.SetLineColor([]tcell.Color{
tcell.ColorDarkOrange,
})
dmLineChart.SetAxesLabelColor(tcell.ColorGold)
dmLineChart.SetAxesColor(tcell.ColorGold)
dmLineChart.SetMarker(tvxwidgets.PlotMarkerDot)
dmLineChart.SetDotMarkerRune('\u25c9')
sampleData1 := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sampleData2 := []float64{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
dotChartData := [][]float64{sampleData1}
dotChartData[0] = append(dotChartData[0], sampleData2...)
dotChartData[0] = append(dotChartData[0], sampleData1[:5]...)
dotChartData[0] = append(dotChartData[0], sampleData2[5:]...)
dotChartData[0] = append(dotChartData[0], sampleData1[:7]...)
dotChartData[0] = append(dotChartData[0], sampleData2[3:]...)
dmLineChart.SetData(dotChartData)
firstCol := tview.NewFlex().SetDirection(tview.FlexRow)
firstCol.AddItem(barGraph, 11, 0, false)
firstCol.AddItem(bmLineChart, 15, 0, false)
firstCol.AddItem(spinnerRow, 3, 0, false)
secondCol := tview.NewFlex().SetDirection(tview.FlexRow)
secondCol.AddItem(amGauge, 3, 0, false)
secondCol.AddItem(pmGauge, 3, 0, false)
secondCol.AddItem(utilFlex, 5, 0, false)
secondCol.AddItem(dmLineChart, 15, 0, false)
screenLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
screenLayout.AddItem(firstCol, 50, 0, false)
screenLayout.AddItem(secondCol, 50, 0, false)
screenLayout.AddItem(spinnerGrid, 15, 0, false)
moveDotChartData := func() {
newData := append(dotChartData[0], dotChartData[0][0])
dotChartData[0] = newData[1:]
}
moveSinData := func(data [][]float64) [][]float64 {
n := 220
newData := make([][]float64, 2)
newData[0] = make([]float64, n)
newData[1] = make([]float64, n)
for i := 0; i < n; i++ {
if i+1 < len(data[0]) {
newData[0][i] = data[0][i+1]
}
if i+1 < len(data[1]) {
newData[1][i] = data[1][i+1]
}
}
return newData
}
updateSpinner := func() {
spinnerTick := time.NewTicker(100 * time.Millisecond)
@ -115,10 +179,8 @@ func main() {
select {
case <-spinnerTick.C:
// update spinners
for _, row := range spinners {
for _, spinner := range row {
spinner.Pulse()
}
for _, spinner := range spinners {
spinner.Pulse()
}
// update gauge
amGauge.Pulse()
@ -158,6 +220,14 @@ func main() {
swapGauge.SetValue(float64(randomNum))
randomNum = rangeLower + rand.Intn(rangeUpper-rangeLower+1)
barGraph.SetBarValue("eth3", randomNum)
// move line charts
sinData = moveSinData(sinData)
bmLineChart.SetData(sinData)
moveDotChartData()
dmLineChart.SetData(dotChartData)
app.Draw()
}
}

1
demos/plot/README.md Normal file
View File

@ -0,0 +1 @@
![Screenshot](screenshot.png)

103
demos/plot/main.go Normal file
View File

@ -0,0 +1,103 @@
package main
import (
"math"
"github.com/gdamore/tcell/v2"
"github.com/navidys/tvxwidgets"
"github.com/rivo/tview"
)
func main() {
app := tview.NewApplication()
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
}()
bmLineChart := tvxwidgets.NewPlot()
bmLineChart.SetBorder(true)
bmLineChart.SetTitle("line chart (braille mode)")
bmLineChart.SetLineColor([]tcell.Color{
tcell.ColorSteelBlue,
tcell.ColorGreen,
})
bmLineChart.SetMarker(tvxwidgets.PlotMarkerBraille)
bmLineChart.SetData(sinData)
dmLineChart := tvxwidgets.NewPlot()
dmLineChart.SetBorder(true)
dmLineChart.SetTitle("line chart (dot mode)")
dmLineChart.SetLineColor([]tcell.Color{
tcell.ColorDarkOrange,
})
dmLineChart.SetAxesLabelColor(tcell.ColorGold)
dmLineChart.SetAxesColor(tcell.ColorGold)
dmLineChart.SetMarker(tvxwidgets.PlotMarkerDot)
dmLineChart.SetDotMarkerRune('\u25c9')
sampleData1 := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sampleData2 := []float64{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
dotModeChartData := [][]float64{sampleData1}
dotModeChartData[0] = append(dotModeChartData[0], sampleData2...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData1[:5]...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData2[5:]...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData1[:7]...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData2[3:]...)
dmLineChart.SetData(dotModeChartData)
scatterPlotData := make([][]float64, 2)
scatterPlotData[0] = []float64{1, 2, 3, 4, 5}
scatterPlotData[1] = sinData[1][4:]
dmScatterPlot := tvxwidgets.NewPlot()
dmScatterPlot.SetBorder(true)
dmScatterPlot.SetTitle("scatter plot (dot mode)")
dmScatterPlot.SetLineColor([]tcell.Color{
tcell.ColorMediumSlateBlue,
tcell.ColorLightSkyBlue,
})
dmScatterPlot.SetPlotType(tvxwidgets.PlotTypeScatter)
dmScatterPlot.SetMarker(tvxwidgets.PlotMarkerDot)
dmScatterPlot.SetData(scatterPlotData)
bmScatterPlot := tvxwidgets.NewPlot()
bmScatterPlot.SetBorder(true)
bmScatterPlot.SetTitle("scatter plot (braille mode)")
bmScatterPlot.SetLineColor([]tcell.Color{
tcell.ColorGold,
tcell.ColorLightSkyBlue,
})
bmScatterPlot.SetPlotType(tvxwidgets.PlotTypeScatter)
bmScatterPlot.SetMarker(tvxwidgets.PlotMarkerBraille)
bmScatterPlot.SetData(scatterPlotData)
firstRow := tview.NewFlex().SetDirection(tview.FlexColumn)
firstRow.AddItem(dmLineChart, 0, 1, false)
firstRow.AddItem(bmLineChart, 0, 1, false)
firstRow.SetRect(0, 0, 100, 15)
secondRow := tview.NewFlex().SetDirection(tview.FlexColumn)
secondRow.AddItem(dmScatterPlot, 0, 1, false)
secondRow.AddItem(bmScatterPlot, 0, 1, false)
secondRow.SetRect(0, 0, 100, 15)
layout := tview.NewFlex().SetDirection(tview.FlexRow)
layout.AddItem(firstRow, 0, 1, false)
layout.AddItem(secondRow, 0, 1, false)
layout.SetRect(0, 0, 100, 30)
if err := app.SetRoot(layout, false).EnableMouse(true).Run(); err != nil {
panic(err)
}
}

BIN
demos/plot/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

368
plot.go Normal file
View File

@ -0,0 +1,368 @@
package tvxwidgets
import (
"fmt"
"image"
"sync"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
// Marker represents plot drawing marker (brialle or dot).
type Marker uint
const (
// plot marker.
PlotMarkerBraille Marker = iota
PlotMarkerDot
)
// PlotType represents plot type (line chart or scatter).
type PlotType uint
const (
PlotTypeLineChart PlotType = iota
PlotTypeScatter
)
const (
plotHorizontalScale = 1
plotXAxisLabelsHeight = 1
plotYAxisLabelsWidth = 4
plotXAxisLabelsGap = 2
plotYAxisLabelsGap = 1
)
type brailleCell struct {
cRune rune
color tcell.Color
}
// Plot represents a plot primitive used for different charts.
type Plot struct {
*tview.Box
data [][]float64
marker Marker
ptype PlotType
dotMarkerRune rune
lineColors []tcell.Color
axesColor tcell.Color
axesLabelColor tcell.Color
drawAxes bool
brailleCellMap map[image.Point]brailleCell
mu sync.Mutex
}
// NewPlot returns a plot widget.
func NewPlot() *Plot {
return &Plot{
Box: tview.NewBox(),
marker: PlotMarkerDot,
ptype: PlotTypeLineChart,
dotMarkerRune: dotRune,
axesColor: tcell.ColorDimGray,
axesLabelColor: tcell.ColorDimGray,
drawAxes: true,
lineColors: []tcell.Color{
tcell.ColorSteelBlue,
},
}
}
// Draw draws this primitive onto the screen.
func (plot *Plot) Draw(screen tcell.Screen) {
plot.Box.DrawForSubclass(screen, plot)
switch plot.marker {
case PlotMarkerDot:
plot.darwDotMarkerToScreen(screen)
case PlotMarkerBraille:
plot.drawBrailleMarkerToScreen(screen)
}
plot.drawAxesToScreen(screen)
}
// SetRect sets rect for this primitive.
func (plot *Plot) SetRect(x, y, width, height int) {
plot.Box.SetRect(x, y, width, height)
}
// SetLineColor sets chart line color.
func (plot *Plot) SetLineColor(color []tcell.Color) {
plot.lineColors = color
}
// SetAxesColor sets axes x and y lines color.
func (plot *Plot) SetAxesColor(color tcell.Color) {
plot.axesColor = color
}
// SetAxesLabelColor sets axes x and y label color.
func (plot *Plot) SetAxesLabelColor(color tcell.Color) {
plot.axesLabelColor = color
}
// SetDrawAxes set true in order to draw axes to screen.
func (plot *Plot) SetDrawAxes(draw bool) {
plot.drawAxes = draw
}
// SetMarker sets marker type braille or dot mode.
func (plot *Plot) SetMarker(marker Marker) {
plot.marker = marker
}
// SetPlotType sets plot type (linechart or scatter).
func (plot *Plot) SetPlotType(ptype PlotType) {
plot.ptype = ptype
}
// SetData sets plot data.
func (plot *Plot) SetData(data [][]float64) {
plot.mu.Lock()
defer plot.mu.Unlock()
plot.brailleCellMap = make(map[image.Point]brailleCell)
plot.data = data
}
// SetDotMarkerRune sets dot marker rune.
func (plot *Plot) SetDotMarkerRune(r rune) {
plot.dotMarkerRune = r
}
func (plot *Plot) getChartAreaRect() (int, int, int, int) {
x, y, width, height := plot.Box.GetInnerRect()
if plot.drawAxes {
x = x + plotYAxisLabelsWidth + 1
width = width - plotYAxisLabelsWidth - 1
height = height - plotXAxisLabelsHeight - 1
} else {
x++
width--
}
return x, y, width, height
}
func (plot *Plot) getData() [][]float64 {
plot.mu.Lock()
data := plot.data
plot.mu.Unlock()
return data
}
func (plot *Plot) drawAxesToScreen(screen tcell.Screen) {
if !plot.drawAxes {
return
}
x, y, width, height := plot.Box.GetInnerRect()
axesStyle := tcell.StyleDefault.Background(plot.GetBackgroundColor()).Foreground(plot.axesColor)
// draw Y axis lin
drawLine(screen,
x+plotYAxisLabelsWidth,
y,
height-plotXAxisLabelsHeight-1,
verticalLine, axesStyle)
// draw X axis line
drawLine(screen,
x+plotYAxisLabelsWidth+1,
y+height-plotXAxisLabelsHeight-1,
width-plotYAxisLabelsWidth-1,
horizontalLine, axesStyle)
tview.PrintJoinedSemigraphics(screen,
x+plotYAxisLabelsWidth,
y+height-plotXAxisLabelsHeight-1,
tview.BoxDrawingsLightUpAndRight, axesStyle)
// draw x axis labels
tview.Print(screen, "0",
x+plotYAxisLabelsWidth,
y+height-plotXAxisLabelsHeight,
1,
tview.AlignLeft, plot.axesLabelColor)
for labelX := x + plotYAxisLabelsWidth +
(plotXAxisLabelsGap)*plotHorizontalScale + 1; labelX < x+width-1; {
label := fmt.Sprintf(
"%d",
(labelX-(x+plotYAxisLabelsWidth)-1)/(plotHorizontalScale)+1,
)
tview.Print(screen, label, labelX, y+height-plotXAxisLabelsHeight, width, tview.AlignLeft, plot.axesLabelColor)
labelX += (len(label) + plotXAxisLabelsGap) * plotHorizontalScale
}
// draw Y axis labels
maxVal := getMaxFloat64From2dSlice(plot.getData())
verticalScale := maxVal / float64(height-plotXAxisLabelsHeight-1)
for i := 0; i*(plotYAxisLabelsGap+1) < height-1; i++ {
label := fmt.Sprintf("%.2f", float64(i)*verticalScale*(plotYAxisLabelsGap+1))
tview.Print(screen,
label,
x,
y+height-(i*(plotYAxisLabelsGap+1))-2, // nolint:gomnd
plotYAxisLabelsWidth,
tview.AlignLeft, plot.axesLabelColor)
}
}
// nolint:gocognit,cyclop
func (plot *Plot) darwDotMarkerToScreen(screen tcell.Screen) {
x, y, width, height := plot.getChartAreaRect()
chartData := plot.getData()
maxVal := getMaxFloat64From2dSlice(chartData)
switch plot.ptype {
case PlotTypeLineChart:
for i, line := range chartData {
style := tcell.StyleDefault.Background(plot.GetBackgroundColor()).Foreground(plot.lineColors[i])
for j := 0; j < len(line) && j*plotHorizontalScale < width; j++ {
val := line[j]
lheight := int((val / maxVal) * float64(height-1))
if (x+(j*plotHorizontalScale) < x+width) && (y+height-1-lheight < y+height) {
tview.PrintJoinedSemigraphics(screen, x+(j*plotHorizontalScale), y+height-1-lheight, plot.dotMarkerRune, style)
}
}
}
case PlotTypeScatter:
for i, line := range chartData {
style := tcell.StyleDefault.Background(plot.GetBackgroundColor()).Foreground(plot.lineColors[i])
for j, val := range line {
lheight := int((val / maxVal) * float64(height-1))
if (x+(j*plotHorizontalScale) < x+width) && (y+height-1-lheight < y+height) {
tview.PrintJoinedSemigraphics(screen, x+(j*plotHorizontalScale), y+height-1-lheight, plot.dotMarkerRune, style)
}
}
}
}
}
func (plot *Plot) drawBrailleMarkerToScreen(screen tcell.Screen) {
var cellMaxY int
x, y, width, height := plot.getChartAreaRect()
plot.calcBrailleLines()
for point := range plot.getBrailleCells() {
if point.Y > cellMaxY {
cellMaxY = point.Y
}
}
diffMAxY := y + height - cellMaxY - 1
// print to screen
for point, cell := range plot.getBrailleCells() {
style := tcell.StyleDefault.Background(plot.GetBackgroundColor()).Foreground(cell.color)
if point.X < x+width && point.Y+diffMAxY < y+height {
tview.PrintJoinedSemigraphics(screen, point.X, point.Y+diffMAxY, cell.cRune, style)
}
}
}
func (plot *Plot) calcBrailleLines() {
x, y, _, height := plot.getChartAreaRect()
chartData := plot.getData()
maxVal := getMaxFloat64From2dSlice(chartData)
for i, line := range chartData {
if len(line) <= 1 {
continue
}
previousHeight := int((line[1] / maxVal) * float64(height-1))
for j, val := range line[1:] {
lheight := int((val / maxVal) * float64(height-1))
plot.setBrailleLine(
image.Pt(
(x+(j*plotHorizontalScale))*2, // nolint:gomnd
(y+height-previousHeight-1)*4, // nolint:gomnd
),
image.Pt(
(x+((j+1)*plotHorizontalScale))*2, // nolint:gomnd
(y+height-lheight-1)*4, // nolint:gomnd
),
plot.lineColors[i],
)
previousHeight = lheight
}
}
}
func (plot *Plot) setBraillePoint(p image.Point, color tcell.Color) {
point := image.Pt(p.X/2, p.Y/4) // nolint:gomnd
plot.brailleCellMap[point] = brailleCell{
plot.brailleCellMap[point].cRune | brailleRune[p.Y%4][p.X%2],
color,
}
}
func (plot *Plot) setBrailleLine(p0, p1 image.Point, color tcell.Color) {
for _, p := range plot.brailleLine(p0, p1) {
plot.setBraillePoint(p, color)
}
}
func (plot *Plot) getBrailleCells() map[image.Point]brailleCell {
cellMap := make(map[image.Point]brailleCell)
for point, cvCell := range plot.brailleCellMap {
cellMap[point] = brailleCell{cvCell.cRune + brailleOffsetRune, cvCell.color}
}
return cellMap
}
func (plot *Plot) brailleLine(p0, p1 image.Point) []image.Point {
points := []image.Point{}
leftPoint, rightPoint := p0, p1
if leftPoint.X > rightPoint.X {
leftPoint, rightPoint = rightPoint, leftPoint
}
xDistance := absInt(leftPoint.X - rightPoint.X)
yDistance := absInt(leftPoint.Y - rightPoint.Y)
slope := float64(yDistance) / float64(xDistance)
slopeSign := 1
if rightPoint.Y < leftPoint.Y {
slopeSign = -1
}
targetYCoordinate := float64(leftPoint.Y)
currentYCoordinate := leftPoint.Y
for i := leftPoint.X; i < rightPoint.X; i++ {
points = append(points, image.Pt(i, currentYCoordinate))
targetYCoordinate += (slope * float64(slopeSign))
for currentYCoordinate != int(targetYCoordinate) {
points = append(points, image.Pt(i, currentYCoordinate))
currentYCoordinate += slopeSign
}
}
return points
}

View File

@ -4,6 +4,14 @@ import (
"strings"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
type drawLineMode int
const (
horizontalLine drawLineMode = iota
verticalLine
)
const (
@ -22,9 +30,18 @@ const (
// dialog padding.
dialogPadding = 2
// empty space parts.
emptySpaceParts = 2
emptySpaceParts = 2
brailleOffsetRune = '\u2800'
dotRune = '\u25CF'
)
var brailleRune = [4][2]rune{ // nolint:gochecknoglobals
{'\u0001', '\u0008'},
{'\u0002', '\u0010'},
{'\u0004', '\u0020'},
{'\u0040', '\u0080'},
}
// getColorName returns convert tcell color to its name.
func getColorName(color tcell.Color) string {
for name, c := range tcell.ColorNames {
@ -47,3 +64,42 @@ func getMessageWidth(message string) int {
return messageWidth
}
// returns max values in 2D float64 slices.
func getMaxFloat64From2dSlice(slices [][]float64) float64 {
if len(slices) == 0 {
return 0
}
var max float64
for _, slice := range slices {
for _, val := range slice {
if val > max {
max = val
}
}
}
return max
}
func absInt(x int) int {
if x >= 0 {
return x
}
return -x
}
func drawLine(screen tcell.Screen, x int, y int, length int, mode drawLineMode, style tcell.Style) {
if mode == horizontalLine {
for i := 0; i < length; i++ {
tview.PrintJoinedSemigraphics(screen, x+i, y, tview.BoxDrawingsLightTripleDashHorizontal, style)
}
} else if mode == verticalLine {
for i := 0; i < length; i++ {
tview.PrintJoinedSemigraphics(screen, x, y+i, tview.BoxDrawingsLightTripleDashVertical, style)
}
}
}