cli graph graphs
Some checks failed
cicd / ci (push) Failing after 51s

This commit is contained in:
Bel LaPointe
2025-05-07 15:34:31 -06:00
parent 2f8dba4e23
commit 58462fb5a4
403 changed files with 226724 additions and 4 deletions

18
vendor/github.com/guptarohit/asciigraph/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,18 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# ide
.idea
# build dirs
*dist*

View File

@@ -0,0 +1,74 @@
# Build customization
builds:
- env:
- CGO_ENABLED=0
main: ./cmd/asciigraph/main.go
ldflags: '-s -w'
# GOOS list to build in.
# For more info refer to https://golang.org/doc/install/source#environment
goos:
- linux
- darwin
- windows
# GOARCH to build in.
# For more info refer to https://golang.org/doc/install/source#environment
goarch:
- 386
- amd64
- arm
- arm64
ignore:
- goos: darwin
goarch: 386
checksum:
name_template: '{{ .ProjectName }}_{{ .Version }}_sha512-checksums.txt'
algorithm: sha512
# Archive customization
archives:
- id: tar
format: tar.gz
files:
- LICENSE
- README.md
name_template: >-
{{- .ProjectName }}_
{{- .Version }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end -}}
format_overrides:
- goos: windows
format: zip
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
release:
github:
owner: guptarohit
name: asciigraph
# If set to true, will not auto-publish the release.
# Default is false.
draft: true
dockers:
- image_templates:
- 'ghcr.io/guptarohit/asciigraph:{{ .Version }}'
- 'ghcr.io/guptarohit/asciigraph:{{ .Tag }}'
- 'ghcr.io/guptarohit/asciigraph:v{{ .Major }}'
- 'ghcr.io/guptarohit/asciigraph:v{{ .Major }}.{{ .Minor }}'
- 'ghcr.io/guptarohit/asciigraph:latest'
dockerfile: goreleaser.dockerfile
build_flag_templates:
- '--label=org.opencontainers.image.title={{ .ProjectName }}'
- '--label=org.opencontainers.image.name={{ .ProjectName }}'
- '--label=org.opencontainers.image.description=Go package to make lightweight line graphs ╭┈╯ in CLI'
- '--label=org.opencontainers.image.url=https://github.com/guptarohit/asciigraph'
- '--label=org.opencontainers.image.source=https://github.com/guptarohit/asciigraph'
- '--label=org.opencontainers.image.version={{ .Version }}'
- '--label=org.opencontainers.image.created={{ .Date }}'
- '--label=org.opencontainers.image.revision={{ .FullCommit }}'
- '--label=org.opencontainers.image.licenses=BSD-3-Clause'

113
vendor/github.com/guptarohit/asciigraph/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,113 @@
# Changelog
All notable changes to this project will be documented in this file.
## [0.7.3] - 2024-10-26
### Fixed
- Incorrect plot height calculation for small value ranges (#59)
## [0.7.2] - 2024-08-12
### Fixed
- Unintended modification of input data (#55)
## [0.7.1] - 2024-03-30
### Added
- CLI: Option to specify legends for series (`sl`)
## [0.7.0] - 2024-03-30
### Added
- CLI: Options to specify delimiter (`d`) and number of series (`sn`)
### Changed
- CLI: Option (`sc`) to specify series colors
## [0.6.0] - 2024-03-25
### Added
- Option to add legends for colored graphs
## [0.5.6] - 2023-06-24
### Added
- Options to set upper & lower bound of graph
## [0.5.5] - 2022-05-03
### Added
- Ansi colors support for graphs
## [0.5.4] - 2022-05-03
### Added
- Option to plot multiple series together (#34)
- Dockerfile file support (#33)
## [0.5.3] - 2022-02-20
### Fixed
- Handled NaN first value (#32)
- Fixed incorrect y-axis start value tick (#31)
## [0.5.2] - 2021-03-28
### Added
- added support to set custom precision of data point labels along the y-axis
- added go module support
### Changed
- updated README to markdown format
## [0.5.1] - 2020-09-14
### Added
- added support for NaN values in series
- added option to control fps of plot rendering via cli for real-time data
### Changed
- removed use of append() method
- make caption centered
- removed trailing spaces from plot
## [0.5.0] - 2020-06-28
### Added
- added support for the realtime plot of data points (from stdin) for CLI.
## [0.4.2] - 2020-06-07
### Fixed
- Prevent panics when data is flat. (#8)
- Prevent BADPREC issue when maximum and minimum values in a series are 0. (#10)
[0.7.2]: https://github.com/guptarohit/asciigraph/releases/tag/v0.7.2
[0.7.1]: https://github.com/guptarohit/asciigraph/releases/tag/v0.7.1
[0.7.0]: https://github.com/guptarohit/asciigraph/releases/tag/v0.7.0
[0.6.0]: https://github.com/guptarohit/asciigraph/releases/tag/v0.6.0
[0.5.6]: https://github.com/guptarohit/asciigraph/releases/tag/v0.5.6
[0.5.5]: https://github.com/guptarohit/asciigraph/releases/tag/v0.5.5
[0.5.4]: https://github.com/guptarohit/asciigraph/releases/tag/v0.5.4
[0.5.3]: https://github.com/guptarohit/asciigraph/releases/tag/v0.5.3
[0.5.2]: https://github.com/guptarohit/asciigraph/releases/tag/v0.5.2
[0.5.1]: https://github.com/guptarohit/asciigraph/releases/tag/v0.5.1
[0.5.0]: https://github.com/guptarohit/asciigraph/releases/tag/v0.5.0
[0.4.2]: https://github.com/guptarohit/asciigraph/releases/tag/v0.4.2

View File

@@ -0,0 +1,133 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
https://github.com/guptarohit.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

10
vendor/github.com/guptarohit/asciigraph/Dockerfile generated vendored Normal file
View File

@@ -0,0 +1,10 @@
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY cmd ./cmd
COPY go.mod ./
COPY *.go ./
RUN GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /app/asciigraph ./cmd/asciigraph/main.go
FROM scratch
COPY --from=builder /app/asciigraph /asciigraph
ENTRYPOINT ["/asciigraph"]

29
vendor/github.com/guptarohit/asciigraph/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2018, Rohit Gupta
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

292
vendor/github.com/guptarohit/asciigraph/README.md generated vendored Normal file
View File

@@ -0,0 +1,292 @@
# asciigraph
[![Build status][]][1] [![Go Report Card][]][2] [![Coverage Status][]][3] [![GoDoc][]][4] [![License][]][5] [![Mentioned in Awesome Go][]][6]
Go package to make lightweight ASCII line graphs ╭┈╯.
![image][]
## Installation
```bash
go get -u github.com/guptarohit/asciigraph@latest
```
## Usage
### Basic graph
```go
package main
import (
"fmt"
"github.com/guptarohit/asciigraph"
)
func main() {
data := []float64{3, 4, 9, 6, 2, 4, 5, 8, 5, 10, 2, 7, 2, 5, 6}
graph := asciigraph.Plot(data)
fmt.Println(graph)
}
```
Running this example would render the following graph:
```bash
10.00 ┤ ╭╮
9.00 ┤ ╭╮ ││
8.00 ┤ ││ ╭╮││
7.00 ┤ ││ ││││╭╮
6.00 ┤ │╰╮ ││││││ ╭
5.00 ┤ │ │ ╭╯╰╯│││╭╯
4.00 ┤╭╯ │╭╯ ││││
3.00 ┼╯ ││ ││││
2.00 ┤ ╰╯ ╰╯╰╯
```
### Multiple Series
```go
package main
import (
"fmt"
"github.com/guptarohit/asciigraph"
)
func main() {
data := [][]float64{{0, 1, 2, 3, 3, 3, 2, 0}, {5, 4, 2, 1, 4, 6, 6}}
graph := asciigraph.PlotMany(data)
fmt.Println(graph)
}
```
Running this example would render the following graph:
```bash
6.00 ┤ ╭─
5.00 ┼╮ │
4.00 ┤╰╮ ╭╯
3.00 ┤ │╭│─╮
2.00 ┤ ╰╮│ ╰╮
1.00 ┤╭╯╰╯ │
0.00 ┼╯ ╰
```
### Colored graphs
```go
package main
import (
"fmt"
"github.com/guptarohit/asciigraph"
)
func main() {
data := make([][]float64, 4)
for i := 0; i < 4; i++ {
for x := -20; x <= 20; x++ {
v := math.NaN()
if r := 20 - i; x >= -r && x <= r {
v = math.Sqrt(math.Pow(float64(r), 2)-math.Pow(float64(x), 2)) / 2
}
data[i] = append(data[i], v)
}
}
graph := asciigraph.PlotMany(data, asciigraph.Precision(0), asciigraph.SeriesColors(
asciigraph.Red,
asciigraph.Yellow,
asciigraph.Green,
asciigraph.Blue,
))
fmt.Println(graph)
}
```
Running this example would render the following graph:
![colored_graph_image][]
### Legends for colored graphs
The graph can include legends for each series, making it easier to interpret.
```go
package main
import (
"fmt"
"github.com/guptarohit/asciigraph"
"math"
)
func main() {
data := make([][]float64, 3)
for i := 0; i < 3; i++ {
for x := -12; x <= 12; x++ {
v := math.NaN()
if r := 12 - i; x >= -r && x <= r {
v = math.Sqrt(math.Pow(float64(r), 2)-math.Pow(float64(x), 2)) / 2
}
data[i] = append(data[i], v)
}
}
graph := asciigraph.PlotMany(data,
asciigraph.Precision(0),
asciigraph.SeriesColors(asciigraph.Red, asciigraph.Green, asciigraph.Blue),
asciigraph.SeriesLegends("Red", "Green", "Blue"),
asciigraph.Caption("Series with legends"))
fmt.Println(graph)
}
```
Running this example would render the following graph:
![graph_with_legends_image][]
## CLI Installation
This package also brings a small utility for command line usage.
Assuming `$GOPATH/bin` is in your `$PATH`, install CLI with following command:
```bash
go install github.com/guptarohit/asciigraph/cmd/asciigraph@latest
```
or pull Docker image:
```bash
docker pull ghcr.io/guptarohit/asciigraph:latest
```
or download binaries from the [releases][] page.
## CLI Usage
```bash ✘ 0|125  16:19:23
> asciigraph --help
Usage of asciigraph:
asciigraph [options]
Options:
-ac axis color
y-axis color of the plot
-b buffer
data points buffer when realtime graph enabled, default equal to `width`
-c caption
caption for the graph
-cc caption color
caption color of the plot
-d delimiter
data delimiter for splitting data points in the input stream (default ",")
-f fps
set fps to control how frequently graph to be rendered when realtime graph enabled (default 24)
-h height
height in text rows, 0 for auto-scaling
-lb lower bound
lower bound set the minimum value for the vertical axis (ignored if series contains lower values) (default +Inf)
-lc label color
y-axis label color of the plot
-o offset
offset in columns, for the label (default 3)
-p precision
precision of data point labels along the y-axis (default 2)
-r realtime
enables realtime graph for data stream
-sc series colors
comma-separated series colors corresponding to each series
-sl series legends
comma-separated series legends corresponding to each series
-sn number of series
number of series (columns) in the input data (default 1)
-ub upper bound
upper bound set the maximum value for the vertical axis (ignored if series contains larger values) (default -Inf)
-w width
width in columns, 0 for auto-scaling
asciigraph expects data points from stdin. Invalid values are logged to stderr.
```
Feed it data points via stdin:
```bash
seq 1 72 | asciigraph -h 10 -c "plot data from stdin"
```
or use Docker image:
```bash
seq 1 72 | docker run -i --rm ghcr.io/guptarohit/asciigraph -h 10 -c "plot data from stdin"
```
Output:
```bash
72.00 ┤ ╭────
64.90 ┤ ╭──────╯
57.80 ┤ ╭──────╯
50.70 ┤ ╭──────╯
43.60 ┤ ╭──────╯
36.50 ┤ ╭───────╯
29.40 ┤ ╭──────╯
22.30 ┤ ╭──────╯
15.20 ┤ ╭──────╯
8.10 ┤ ╭──────╯
1.00 ┼──╯
plot data from stdin
```
Example of **real-time graph** for data points stream via stdin:
<a href="https://asciinema.org/a/382383" target="_blank"><img width="500" alt="Realtime graph for data points via stdin (google ping) using asciigraph" src="https://asciinema.org/a/382383.svg" /></a>
<details>
<summary>command for above graph</summary>
```sh
ping -i.2 google.com | grep -oP '(?<=time=).*(?=ms)' --line-buffered | asciigraph -r -h 10 -w 40 -c "realtime plot data (google ping in ms) from stdin"
```
</details>
Example of **multi-series real-time graph** for data points stream via stdin:
<a href="https://asciinema.org/a/649906" target="_blank"><img width="500" alt="Ping latency comparison: Google (Blue) vs. DuckDuckGo (Red) with asciigraph" src="https://asciinema.org/a/649906.svg" /></a>
<details>
<summary>command for above graph</summary>
```sh
{unbuffer paste -d, <(ping -i 0.4 google.com | sed -u -n -E 's/.*time=(.*)ms.*/\1/p') <(ping -i 0.4 duckduckgo.com | sed -u -n -E 's/.*time=(.*)ms.*/\1/p') } | asciigraph -r -h 15 -w 60 -sn 2 -sc "blue,red" -c "Ping Latency Comparison" -sl "Google, DuckDuckGo"
```
</details>
## Acknowledgement
This package started as golang port of [asciichart][].
## Contributing
Feel free to make a pull request! :octocat:
[Build status]: https://github.com/guptarohit/asciigraph/actions/workflows/test.yml/badge.svg
[1]: https://github.com/guptarohit/asciigraph/actions/workflows/test.yml
[Go Report Card]: https://goreportcard.com/badge/github.com/guptarohit/asciigraph
[2]: https://goreportcard.com/report/github.com/guptarohit/asciigraph
[Coverage Status]: https://coveralls.io/repos/github/guptarohit/asciigraph/badge.svg?branch=master
[3]: https://coveralls.io/github/guptarohit/asciigraph?branch=master
[GoDoc]: https://godoc.org/github.com/guptarohit/asciigraph?status.svg
[4]: https://godoc.org/github.com/guptarohit/asciigraph
[License]: https://img.shields.io/badge/licence-BSD-blue.svg
[5]: https://github.com/guptarohit/asciigraph/blob/master/LICENSE
[Mentioned in Awesome Go]: https://awesome.re/mentioned-badge-flat.svg
[6]: https://github.com/avelino/awesome-go#advanced-console-uis
[image]: https://user-images.githubusercontent.com/7895001/41509956-b1b2b3d0-7279-11e8-9d19-d7dea17d5e44.png
[colored_graph_image]: https://user-images.githubusercontent.com/7895001/166443444-40ad8113-2c0f-46d7-9c75-1cf08435ce15.png
[releases]: https://github.com/guptarohit/asciigraph/releases
[asciichart]: https://github.com/kroitor/asciichart
[graph_with_legends_image]: https://github.com/guptarohit/asciigraph/assets/7895001/4066ee95-55ca-42a4-8a03-e73ce20df5d3

265
vendor/github.com/guptarohit/asciigraph/asciigraph.go generated vendored Normal file
View File

@@ -0,0 +1,265 @@
package asciigraph
import (
"bytes"
"fmt"
"math"
"strings"
)
// Plot returns ascii graph for a series.
func Plot(series []float64, options ...Option) string {
return PlotMany([][]float64{series}, options...)
}
// PlotMany returns ascii graph for multiple series.
func PlotMany(data [][]float64, options ...Option) string {
var logMaximum float64
config := configure(config{
Offset: 3,
Precision: 2,
}, options)
// Create a deep copy of the input data
dataCopy := make([][]float64, len(data))
for i, series := range data {
dataCopy[i] = make([]float64, len(series))
copy(dataCopy[i], series)
}
data = dataCopy
lenMax := 0
for i := range data {
if l := len(data[i]); l > lenMax {
lenMax = l
}
}
if config.Width > 0 {
for i := range data {
for j := len(data[i]); j < lenMax; j++ {
data[i] = append(data[i], math.NaN())
}
data[i] = interpolateArray(data[i], config.Width)
}
lenMax = config.Width
}
minimum, maximum := math.Inf(1), math.Inf(-1)
for i := range data {
minVal, maxVal := minMaxFloat64Slice(data[i])
if minVal < minimum {
minimum = minVal
}
if maxVal > maximum {
maximum = maxVal
}
}
if config.LowerBound != nil && *config.LowerBound < minimum {
minimum = *config.LowerBound
}
if config.UpperBound != nil && *config.UpperBound > maximum {
maximum = *config.UpperBound
}
interval := math.Abs(maximum - minimum)
if config.Height <= 0 {
config.Height = calculateHeight(interval)
}
if config.Offset <= 0 {
config.Offset = 3
}
var ratio float64
if interval != 0 {
ratio = float64(config.Height) / interval
} else {
ratio = 1
}
min2 := round(minimum * ratio)
max2 := round(maximum * ratio)
intmin2 := int(min2)
intmax2 := int(max2)
rows := int(math.Abs(float64(intmax2 - intmin2)))
width := lenMax + config.Offset
type cell struct {
Text string
Color AnsiColor
}
plot := make([][]cell, rows+1)
// initialise empty 2D grid
for i := 0; i < rows+1; i++ {
line := make([]cell, width)
for j := 0; j < width; j++ {
line[j].Text = " "
line[j].Color = Default
}
plot[i] = line
}
precision := config.Precision
logMaximum = math.Log10(math.Max(math.Abs(maximum), math.Abs(minimum))) //to find number of zeros after decimal
if minimum == float64(0) && maximum == float64(0) {
logMaximum = float64(-1)
}
if logMaximum < 0 {
// negative log
if math.Mod(logMaximum, 1) != 0 {
// non-zero digits after decimal
precision += uint(math.Abs(logMaximum))
} else {
precision += uint(math.Abs(logMaximum) - 1.0)
}
} else if logMaximum > 2 {
precision = 0
}
maxNumLength := len(fmt.Sprintf("%0.*f", precision, maximum))
minNumLength := len(fmt.Sprintf("%0.*f", precision, minimum))
maxWidth := int(math.Max(float64(maxNumLength), float64(minNumLength)))
// axis and labels
for y := intmin2; y < intmax2+1; y++ {
var magnitude float64
if rows > 0 {
magnitude = maximum - (float64(y-intmin2) * interval / float64(rows))
} else {
magnitude = float64(y)
}
label := fmt.Sprintf("%*.*f", maxWidth+1, precision, magnitude)
w := y - intmin2
h := int(math.Max(float64(config.Offset)-float64(len(label)), 0))
plot[w][h].Text = label
plot[w][h].Color = config.LabelColor
plot[w][config.Offset-1].Text = "┤"
plot[w][config.Offset-1].Color = config.AxisColor
}
for i := range data {
series := data[i]
color := Default
if i < len(config.SeriesColors) {
color = config.SeriesColors[i]
}
var y0, y1 int
if !math.IsNaN(series[0]) {
y0 = int(round(series[0]*ratio) - min2)
plot[rows-y0][config.Offset-1].Text = "┼" // first value
plot[rows-y0][config.Offset-1].Color = config.AxisColor
}
for x := 0; x < len(series)-1; x++ { // plot the line
d0 := series[x]
d1 := series[x+1]
if math.IsNaN(d0) && math.IsNaN(d1) {
continue
}
if math.IsNaN(d1) && !math.IsNaN(d0) {
y0 = int(round(d0*ratio) - float64(intmin2))
plot[rows-y0][x+config.Offset].Text = "╴"
plot[rows-y0][x+config.Offset].Color = color
continue
}
if math.IsNaN(d0) && !math.IsNaN(d1) {
y1 = int(round(d1*ratio) - float64(intmin2))
plot[rows-y1][x+config.Offset].Text = "╶"
plot[rows-y1][x+config.Offset].Color = color
continue
}
y0 = int(round(d0*ratio) - float64(intmin2))
y1 = int(round(d1*ratio) - float64(intmin2))
if y0 == y1 {
plot[rows-y0][x+config.Offset].Text = "─"
} else {
if y0 > y1 {
plot[rows-y1][x+config.Offset].Text = "╰"
plot[rows-y0][x+config.Offset].Text = "╮"
} else {
plot[rows-y1][x+config.Offset].Text = "╭"
plot[rows-y0][x+config.Offset].Text = "╯"
}
start := int(math.Min(float64(y0), float64(y1))) + 1
end := int(math.Max(float64(y0), float64(y1)))
for y := start; y < end; y++ {
plot[rows-y][x+config.Offset].Text = "│"
}
}
start := int(math.Min(float64(y0), float64(y1)))
end := int(math.Max(float64(y0), float64(y1)))
for y := start; y <= end; y++ {
plot[rows-y][x+config.Offset].Color = color
}
}
}
// join columns
var lines bytes.Buffer
for h, horizontal := range plot {
if h != 0 {
lines.WriteRune('\n')
}
// remove trailing spaces
lastCharIndex := 0
for i := width - 1; i >= 0; i-- {
if horizontal[i].Text != " " {
lastCharIndex = i
break
}
}
c := Default
for _, v := range horizontal[:lastCharIndex+1] {
if v.Color != c {
c = v.Color
lines.WriteString(c.String())
}
lines.WriteString(v.Text)
}
if c != Default {
lines.WriteString(Default.String())
}
}
// add caption if not empty
if config.Caption != "" {
lines.WriteRune('\n')
lines.WriteString(strings.Repeat(" ", config.Offset+maxWidth))
if len(config.Caption) < lenMax {
lines.WriteString(strings.Repeat(" ", (lenMax-len(config.Caption))/2))
}
if config.CaptionColor != Default {
lines.WriteString(config.CaptionColor.String())
}
lines.WriteString(config.Caption)
if config.CaptionColor != Default {
lines.WriteString(Default.String())
}
}
if len(config.SeriesLegends) > 0 {
addLegends(&lines, config, lenMax, config.Offset+maxWidth)
}
return lines.String()
}

312
vendor/github.com/guptarohit/asciigraph/color.go generated vendored Normal file
View File

@@ -0,0 +1,312 @@
package asciigraph
import "fmt"
type AnsiColor byte
var (
Default AnsiColor = 0
AliceBlue AnsiColor = 255
AntiqueWhite AnsiColor = 255
Aqua AnsiColor = 14
Aquamarine AnsiColor = 122
Azure AnsiColor = 15
Beige AnsiColor = 230
Bisque AnsiColor = 224
Black AnsiColor = 188 // dummy value
BlanchedAlmond AnsiColor = 230
Blue AnsiColor = 12
BlueViolet AnsiColor = 92
Brown AnsiColor = 88
BurlyWood AnsiColor = 180
CadetBlue AnsiColor = 73
Chartreuse AnsiColor = 118
Chocolate AnsiColor = 166
Coral AnsiColor = 209
CornflowerBlue AnsiColor = 68
Cornsilk AnsiColor = 230
Crimson AnsiColor = 161
Cyan AnsiColor = 14
DarkBlue AnsiColor = 18
DarkCyan AnsiColor = 30
DarkGoldenrod AnsiColor = 136
DarkGray AnsiColor = 248
DarkGreen AnsiColor = 22
DarkKhaki AnsiColor = 143
DarkMagenta AnsiColor = 90
DarkOliveGreen AnsiColor = 59
DarkOrange AnsiColor = 208
DarkOrchid AnsiColor = 134
DarkRed AnsiColor = 88
DarkSalmon AnsiColor = 173
DarkSeaGreen AnsiColor = 108
DarkSlateBlue AnsiColor = 60
DarkSlateGray AnsiColor = 238
DarkTurquoise AnsiColor = 44
DarkViolet AnsiColor = 92
DeepPink AnsiColor = 198
DeepSkyBlue AnsiColor = 39
DimGray AnsiColor = 242
DodgerBlue AnsiColor = 33
Firebrick AnsiColor = 124
FloralWhite AnsiColor = 15
ForestGreen AnsiColor = 28
Fuchsia AnsiColor = 13
Gainsboro AnsiColor = 253
GhostWhite AnsiColor = 15
Gold AnsiColor = 220
Goldenrod AnsiColor = 178
Gray AnsiColor = 8
Green AnsiColor = 2
GreenYellow AnsiColor = 155
Honeydew AnsiColor = 15
HotPink AnsiColor = 205
IndianRed AnsiColor = 167
Indigo AnsiColor = 54
Ivory AnsiColor = 15
Khaki AnsiColor = 222
Lavender AnsiColor = 254
LavenderBlush AnsiColor = 255
LawnGreen AnsiColor = 118
LemonChiffon AnsiColor = 230
LightBlue AnsiColor = 152
LightCoral AnsiColor = 210
LightCyan AnsiColor = 195
LightGoldenrodYellow AnsiColor = 230
LightGray AnsiColor = 252
LightGreen AnsiColor = 120
LightPink AnsiColor = 217
LightSalmon AnsiColor = 216
LightSeaGreen AnsiColor = 37
LightSkyBlue AnsiColor = 117
LightSlateGray AnsiColor = 103
LightSteelBlue AnsiColor = 152
LightYellow AnsiColor = 230
Lime AnsiColor = 10
LimeGreen AnsiColor = 77
Linen AnsiColor = 255
Magenta AnsiColor = 13
Maroon AnsiColor = 1
MediumAquamarine AnsiColor = 79
MediumBlue AnsiColor = 20
MediumOrchid AnsiColor = 134
MediumPurple AnsiColor = 98
MediumSeaGreen AnsiColor = 72
MediumSlateBlue AnsiColor = 99
MediumSpringGreen AnsiColor = 48
MediumTurquoise AnsiColor = 80
MediumVioletRed AnsiColor = 162
MidnightBlue AnsiColor = 17
MintCream AnsiColor = 15
MistyRose AnsiColor = 224
Moccasin AnsiColor = 223
NavajoWhite AnsiColor = 223
Navy AnsiColor = 4
OldLace AnsiColor = 230
Olive AnsiColor = 3
OliveDrab AnsiColor = 64
Orange AnsiColor = 214
OrangeRed AnsiColor = 202
Orchid AnsiColor = 170
PaleGoldenrod AnsiColor = 223
PaleGreen AnsiColor = 120
PaleTurquoise AnsiColor = 159
PaleVioletRed AnsiColor = 168
PapayaWhip AnsiColor = 230
PeachPuff AnsiColor = 223
Peru AnsiColor = 173
Pink AnsiColor = 218
Plum AnsiColor = 182
PowderBlue AnsiColor = 152
Purple AnsiColor = 5
Red AnsiColor = 9
RosyBrown AnsiColor = 138
RoyalBlue AnsiColor = 63
SaddleBrown AnsiColor = 94
Salmon AnsiColor = 210
SandyBrown AnsiColor = 215
SeaGreen AnsiColor = 29
SeaShell AnsiColor = 15
Sienna AnsiColor = 131
Silver AnsiColor = 7
SkyBlue AnsiColor = 117
SlateBlue AnsiColor = 62
SlateGray AnsiColor = 66
Snow AnsiColor = 15
SpringGreen AnsiColor = 48
SteelBlue AnsiColor = 67
Tan AnsiColor = 180
Teal AnsiColor = 6
Thistle AnsiColor = 182
Tomato AnsiColor = 203
Turquoise AnsiColor = 80
Violet AnsiColor = 213
Wheat AnsiColor = 223
White AnsiColor = 15
WhiteSmoke AnsiColor = 255
Yellow AnsiColor = 11
YellowGreen AnsiColor = 149
)
var ColorNames = map[string]AnsiColor{
"default": Default,
"aliceblue": AliceBlue,
"antiquewhite": AntiqueWhite,
"aqua": Aqua,
"aquamarine": Aquamarine,
"azure": Azure,
"beige": Beige,
"bisque": Bisque,
"black": Black,
"blanchedalmond": BlanchedAlmond,
"blue": Blue,
"blueviolet": BlueViolet,
"brown": Brown,
"burlywood": BurlyWood,
"cadetblue": CadetBlue,
"chartreuse": Chartreuse,
"chocolate": Chocolate,
"coral": Coral,
"cornflowerblue": CornflowerBlue,
"cornsilk": Cornsilk,
"crimson": Crimson,
"cyan": Cyan,
"darkblue": DarkBlue,
"darkcyan": DarkCyan,
"darkgoldenrod": DarkGoldenrod,
"darkgray": DarkGray,
"darkgreen": DarkGreen,
"darkkhaki": DarkKhaki,
"darkmagenta": DarkMagenta,
"darkolivegreen": DarkOliveGreen,
"darkorange": DarkOrange,
"darkorchid": DarkOrchid,
"darkred": DarkRed,
"darksalmon": DarkSalmon,
"darkseagreen": DarkSeaGreen,
"darkslateblue": DarkSlateBlue,
"darkslategray": DarkSlateGray,
"darkturquoise": DarkTurquoise,
"darkviolet": DarkViolet,
"deeppink": DeepPink,
"deepskyblue": DeepSkyBlue,
"dimgray": DimGray,
"dodgerblue": DodgerBlue,
"firebrick": Firebrick,
"floralwhite": FloralWhite,
"forestgreen": ForestGreen,
"fuchsia": Fuchsia,
"gainsboro": Gainsboro,
"ghostwhite": GhostWhite,
"gold": Gold,
"goldenrod": Goldenrod,
"gray": Gray,
"green": Green,
"greenyellow": GreenYellow,
"honeydew": Honeydew,
"hotpink": HotPink,
"indianred": IndianRed,
"indigo": Indigo,
"ivory": Ivory,
"khaki": Khaki,
"lavender": Lavender,
"lavenderblush": LavenderBlush,
"lawngreen": LawnGreen,
"lemonchiffon": LemonChiffon,
"lightblue": LightBlue,
"lightcoral": LightCoral,
"lightcyan": LightCyan,
"lightgoldenrodyellow": LightGoldenrodYellow,
"lightgray": LightGray,
"lightgreen": LightGreen,
"lightpink": LightPink,
"lightsalmon": LightSalmon,
"lightseagreen": LightSeaGreen,
"lightskyblue": LightSkyBlue,
"lightslategray": LightSlateGray,
"lightsteelblue": LightSteelBlue,
"lightyellow": LightYellow,
"lime": Lime,
"limegreen": LimeGreen,
"linen": Linen,
"magenta": Magenta,
"maroon": Maroon,
"mediumaquamarine": MediumAquamarine,
"mediumblue": MediumBlue,
"mediumorchid": MediumOrchid,
"mediumpurple": MediumPurple,
"mediumseagreen": MediumSeaGreen,
"mediumslateblue": MediumSlateBlue,
"mediumspringgreen": MediumSpringGreen,
"mediumturquoise": MediumTurquoise,
"mediumvioletred": MediumVioletRed,
"midnightblue": MidnightBlue,
"mintcream": MintCream,
"mistyrose": MistyRose,
"moccasin": Moccasin,
"navajowhite": NavajoWhite,
"navy": Navy,
"oldlace": OldLace,
"olive": Olive,
"olivedrab": OliveDrab,
"orange": Orange,
"orangered": OrangeRed,
"orchid": Orchid,
"palegoldenrod": PaleGoldenrod,
"palegreen": PaleGreen,
"paleturquoise": PaleTurquoise,
"palevioletred": PaleVioletRed,
"papayawhip": PapayaWhip,
"peachpuff": PeachPuff,
"peru": Peru,
"pink": Pink,
"plum": Plum,
"powderblue": PowderBlue,
"purple": Purple,
"red": Red,
"rosybrown": RosyBrown,
"royalblue": RoyalBlue,
"saddlebrown": SaddleBrown,
"salmon": Salmon,
"sandybrown": SandyBrown,
"seagreen": SeaGreen,
"seashell": SeaShell,
"sienna": Sienna,
"silver": Silver,
"skyblue": SkyBlue,
"slateblue": SlateBlue,
"slategray": SlateGray,
"snow": Snow,
"springgreen": SpringGreen,
"steelblue": SteelBlue,
"tan": Tan,
"teal": Teal,
"thistle": Thistle,
"tomato": Tomato,
"turquoise": Turquoise,
"violet": Violet,
"wheat": Wheat,
"white": White,
"whitesmoke": WhiteSmoke,
"yellow": Yellow,
"yellowgreen": YellowGreen,
}
func (c AnsiColor) String() string {
if c == Default {
return "\x1b[0m"
}
if c == Black {
c = 0
}
if c <= Silver {
// 3-bit color
return fmt.Sprintf("\x1b[%dm", 30+byte(c))
}
if c <= White {
// 4-bit color
return fmt.Sprintf("\x1b[%dm", 82+byte(c))
}
// 8-bit color
return fmt.Sprintf("\x1b[38;5;%dm", byte(c))
}

View File

@@ -0,0 +1,3 @@
FROM scratch
COPY asciigraph /asciigraph
ENTRYPOINT ["/asciigraph"]

45
vendor/github.com/guptarohit/asciigraph/legend.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package asciigraph
import (
"bytes"
"fmt"
"strings"
"unicode/utf8"
)
// Create legend item as a colored box and text
func createLegendItem(text string, color AnsiColor) (string, int) {
return fmt.Sprintf(
"%s■%s %s",
color.String(),
Default.String(),
text,
),
// Can't use len() because of AnsiColor, add 2 for box and space
utf8.RuneCountInString(text) + 2
}
// Add legend for each series added to the graph
func addLegends(lines *bytes.Buffer, config *config, lenMax int, leftPad int) {
lines.WriteString("\n\n")
lines.WriteString(strings.Repeat(" ", leftPad))
var legendsText string
var legendsTextLen int
rightPad := 3
for i, text := range config.SeriesLegends {
item, itemLen := createLegendItem(text, config.SeriesColors[i])
legendsText += item
legendsTextLen += itemLen
if i < len(config.SeriesLegends)-1 {
legendsText += strings.Repeat(" ", rightPad)
legendsTextLen += rightPad
}
}
if legendsTextLen < lenMax {
lines.WriteString(strings.Repeat(" ", (lenMax-legendsTextLen)/2))
}
lines.WriteString(legendsText)
}

126
vendor/github.com/guptarohit/asciigraph/options.go generated vendored Normal file
View File

@@ -0,0 +1,126 @@
package asciigraph
import (
"strings"
)
// Option represents a configuration setting.
type Option interface {
apply(c *config)
}
// config holds various graph options
type config struct {
Width, Height int
LowerBound, UpperBound *float64
Offset int
Caption string
Precision uint
CaptionColor AnsiColor
AxisColor AnsiColor
LabelColor AnsiColor
SeriesColors []AnsiColor
SeriesLegends []string
}
// An optionFunc applies an option.
type optionFunc func(*config)
// apply implements the Option interface.
func (of optionFunc) apply(c *config) { of(c) }
func configure(defaults config, options []Option) *config {
for _, o := range options {
o.apply(&defaults)
}
return &defaults
}
// Width sets the graphs width. By default, the width of the graph is
// determined by the number of data points. If the value given is a
// positive number, the data points are interpolated on the x axis.
// Values <= 0 reset the width to the default value.
func Width(w int) Option {
return optionFunc(func(c *config) {
if w > 0 {
c.Width = w
} else {
c.Width = 0
}
})
}
// Height sets the graphs height.
func Height(h int) Option {
return optionFunc(func(c *config) {
if h > 0 {
c.Height = h
} else {
c.Height = 0
}
})
}
// LowerBound sets the graph's minimum value for the vertical axis. It will be ignored
// if the series contains a lower value.
func LowerBound(min float64) Option {
return optionFunc(func(c *config) { c.LowerBound = &min })
}
// UpperBound sets the graph's maximum value for the vertical axis. It will be ignored
// if the series contains a bigger value.
func UpperBound(max float64) Option {
return optionFunc(func(c *config) { c.UpperBound = &max })
}
// Offset sets the graphs offset.
func Offset(o int) Option {
return optionFunc(func(c *config) { c.Offset = o })
}
// Precision sets the graphs precision.
func Precision(p uint) Option {
return optionFunc(func(c *config) { c.Precision = p })
}
// Caption sets the graphs caption.
func Caption(caption string) Option {
return optionFunc(func(c *config) {
c.Caption = strings.TrimSpace(caption)
})
}
// CaptionColor sets the caption color.
func CaptionColor(ac AnsiColor) Option {
return optionFunc(func(c *config) {
c.CaptionColor = ac
})
}
// AxisColor sets the axis color.
func AxisColor(ac AnsiColor) Option {
return optionFunc(func(c *config) {
c.AxisColor = ac
})
}
// LabelColor sets the axis label color.
func LabelColor(ac AnsiColor) Option {
return optionFunc(func(c *config) {
c.LabelColor = ac
})
}
// SeriesColors sets the series colors.
func SeriesColors(ac ...AnsiColor) Option {
return optionFunc(func(c *config) {
c.SeriesColors = ac
})
}
// SeriesLegends sets the legend text for the corresponding series.
func SeriesLegends(text ...string) Option {
return optionFunc(func(c *config) {
c.SeriesLegends = text
})
}

105
vendor/github.com/guptarohit/asciigraph/utils.go generated vendored Normal file
View File

@@ -0,0 +1,105 @@
package asciigraph
import (
"fmt"
"log"
"math"
"os"
"os/exec"
"runtime"
)
func minMaxFloat64Slice(v []float64) (min, max float64) {
min = math.Inf(1)
max = math.Inf(-1)
if len(v) == 0 {
panic("Empty slice")
}
for _, e := range v {
if e < min {
min = e
}
if e > max {
max = e
}
}
return
}
func round(input float64) float64 {
if math.IsNaN(input) {
return math.NaN()
}
sign := 1.0
if input < 0 {
sign = -1
input *= -1
}
_, decimal := math.Modf(input)
var rounded float64
if decimal >= 0.5 {
rounded = math.Ceil(input)
} else {
rounded = math.Floor(input)
}
return rounded * sign
}
func linearInterpolate(before, after, atPoint float64) float64 {
return before + (after-before)*atPoint
}
func interpolateArray(data []float64, fitCount int) []float64 {
var interpolatedData []float64
springFactor := float64(len(data)-1) / float64(fitCount-1)
interpolatedData = append(interpolatedData, data[0])
for i := 1; i < fitCount-1; i++ {
spring := float64(i) * springFactor
before := math.Floor(spring)
after := math.Ceil(spring)
atPoint := spring - before
interpolatedData = append(interpolatedData, linearInterpolate(data[int(before)], data[int(after)], atPoint))
}
interpolatedData = append(interpolatedData, data[len(data)-1])
return interpolatedData
}
// clear terminal screen
var Clear func()
func init() {
platform := runtime.GOOS
if platform == "windows" {
Clear = func() {
cmd := exec.Command("cmd", "/c", "cls")
cmd.Stdout = os.Stdout
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
}
} else {
Clear = func() {
fmt.Print("\033[2J\033[H")
}
}
}
func calculateHeight(interval float64) int {
if interval >= 1 {
return int(interval)
}
scaleFactor := math.Pow(10, math.Floor(math.Log10(interval)))
scaledDelta := interval / scaleFactor
if scaledDelta < 2 {
return int(math.Ceil(scaledDelta))
}
return int(math.Floor(scaledDelta))
}