Skip to content

Commit

Permalink
Enable to draw plot chart
Browse files Browse the repository at this point in the history
  • Loading branch information
nakabonne committed Sep 12, 2020
1 parent 237d364 commit fab357e
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -11,5 +11,8 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# IDE
.idea

# Dependency directories (remove the comment below to include it)
# vendor/
8 changes: 8 additions & 0 deletions go.mod
@@ -0,0 +1,8 @@
module github.com/nakabonne/ali

go 1.15

require (
github.com/mum4k/termdash v0.12.2
github.com/spf13/pflag v1.0.5
)
17 changes: 17 additions & 0 deletions go.sum
@@ -0,0 +1,17 @@
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mum4k/termdash v0.12.2 h1:S2frz71OrXUKIVVZ3snYBEzyYlUNRTu0ElV6d5Pf6gI=
github.com/mum4k/termdash v0.12.2/go.mod h1:haerPCSO0U8pehROAecmuOHDF+2UXw2KaCTxdWooDFE=
github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be h1:yzmWtPyxEUIKdZg4RcPq64MfS8NA6A5fNOJgYhpR9EQ=
github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
69 changes: 69 additions & 0 deletions gui/gui.go
@@ -0,0 +1,69 @@
package gui

import (
"context"
"fmt"

"github.com/mum4k/termdash"
"github.com/mum4k/termdash/container"
"github.com/mum4k/termdash/container/grid"
"github.com/mum4k/termdash/keyboard"
"github.com/mum4k/termdash/linestyle"
"github.com/mum4k/termdash/terminal/termbox"
"github.com/mum4k/termdash/terminal/terminalapi"
)

const rootID = "root"

func Run() error {
ctx, cancel := context.WithCancel(context.Background())

t, err := termbox.New(termbox.ColorMode(terminalapi.ColorMode256))
if err != nil {
return fmt.Errorf("failed to generate terminal interface: %w", err)
}
defer t.Close()

c, err := container.New(t, container.ID(rootID))
if err != nil {
return fmt.Errorf("failed to generate container: %w", err)
}

w, err := newWidgets(ctx, c)
if err != nil {
return fmt.Errorf("failed to generate widgets: %w", err)
}
gridOpts, err := gridLayout(w)
if err != nil {
return fmt.Errorf("failed to build grid layout: %w", err)
}
if err := c.Update(rootID, gridOpts...); err != nil {
return fmt.Errorf("failed to update container: %w", err)
}

quitter := func(k *terminalapi.Keyboard) {
if k.Key == keyboard.KeyEsc || k.Key == keyboard.KeyCtrlC {
cancel()
}
}
return termdash.Run(ctx, t, c, termdash.KeyboardSubscriber(quitter), termdash.RedrawInterval(redrawInterval))
}

func gridLayout(w *widgets) ([]container.Option, error) {
rows := []grid.Element{
grid.RowHeightPerc(99,
grid.Widget(w.plotChart,
container.Border(linestyle.Light),
container.BorderTitle("Plot"),
),
),
}
col := grid.ColWidthPerc(99, rows...)

builder := grid.New()
builder.Add(
col,
)

return builder.Build()
}
92 changes: 92 additions & 0 deletions gui/widgets.go
@@ -0,0 +1,92 @@
package gui

import (
"context"
"math"
"time"

"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/container"
"github.com/mum4k/termdash/widgets/button"
"github.com/mum4k/termdash/widgets/linechart"
"github.com/mum4k/termdash/widgets/textinput"
)

// redrawInterval is how often termdash redraws the screen.
const (
redrawInterval = 250 * time.Millisecond
)

type widgets struct {
URLInput *textinput.TextInput
attackButton *button.Button
plotChart *linechart.LineChart
}

func newWidgets(ctx context.Context, c *container.Container) (*widgets, error) {
l, err := newLineChart(ctx)
if err != nil {
return nil, err
}

return &widgets{
URLInput: nil,
attackButton: nil,
plotChart: l,
}, nil
}

// newLineChart returns a line plotChart that displays a heartbeat-like progression.
func newLineChart(ctx context.Context) (*linechart.LineChart, error) {
var inputs []float64
for i := 0; i < 100; i++ {
v := math.Pow(math.Sin(float64(i)), 63) * math.Sin(float64(i)+1.5) * 8
inputs = append(inputs, v)
}

lc, err := linechart.New(
linechart.AxesCellOpts(cell.FgColor(cell.ColorRed)),
linechart.YLabelCellOpts(cell.FgColor(cell.ColorGreen)),
linechart.XLabelCellOpts(cell.FgColor(cell.ColorGreen)),
)
if err != nil {
return nil, err
}
step := 0
go periodic(ctx, redrawInterval/3, func() error {
step = (step + 1) % len(inputs)
return lc.Series("heartbeat", rotateFloats(inputs, step),
linechart.SeriesCellOpts(cell.FgColor(cell.ColorNumber(87))),
linechart.SeriesXLabels(map[int]string{
0: "zero",
}),
)
})
return lc, nil
}

// periodic executes the provided closure periodically every interval.
// Exits when the context expires.
func periodic(ctx context.Context, interval time.Duration, fn func() error) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := fn(); err != nil {
panic(err)
}
case <-ctx.Done():
return
}
}
}

// rotateFloats returns a new slice with inputs rotated by step.
// I.e. for a step of one:
// inputs[0] -> inputs[len(inputs)-1]
// inputs[1] -> inputs[0]
// And so on.
func rotateFloats(inputs []float64, step int) []float64 {
return append(inputs[step:], inputs[:step]...)
}
64 changes: 64 additions & 0 deletions main.go
@@ -0,0 +1,64 @@
package main

import (
"errors"
"fmt"
"io"
"os"
"runtime"

flag "github.com/spf13/pflag"

"github.com/nakabonne/ali/gui"
)

var (
flagSet = flag.NewFlagSet("ali", flag.ContinueOnError)

usage = func() {
fmt.Fprintln(os.Stderr, "usage: ali [<flag> ...]")
flagSet.PrintDefaults()
}
// Automatically populated by goreleaser during build
version = "unversioned"
commit = "?"
date = "?"
)

type cli struct {
debug bool
version bool
stdout io.Writer
stderr io.Writer
}

func main() {
c := &cli{
stdout: os.Stdout,
stderr: os.Stderr,
}
flagSet.BoolVarP(&c.version, "version", "v", false, "print the current version")
flagSet.BoolVar(&c.debug, "debug", false, "run in debug mode")
flagSet.Usage = usage
if err := flagSet.Parse(os.Args[1:]); err != nil {
if !errors.Is(err, flag.ErrHelp) {
fmt.Fprintln(c.stderr, err)
}
return
}

os.Exit(c.run())
}
func (c *cli) run() int {
if c.version {
fmt.Fprintf(c.stderr, "version=%s, commit=%s, buildDate=%s, os=%s, arch=%s\n", version, commit, date, runtime.GOOS, runtime.GOARCH)
return 0
}

if err := gui.Run(); err != nil {
fmt.Fprintf(c.stderr, "failed to start application: %s", err.Error())
return 1
}

return 0
}

0 comments on commit fab357e

Please sign in to comment.