From c76ad10b1dc63beeee3cf4fa8f4f4f4b02222289 Mon Sep 17 00:00:00 2001 From: Sergey Grebenshchikov Date: Wed, 28 Mar 2018 17:27:59 +0200 Subject: Add braille & quarter-block canvas. Add scatter plot type. Re-write linechart + barchart. --- Makefile | 2 +- README.md | 271 +- cmd/jp/bar.go | 10 +- cmd/jp/canvas_other.go | 10 + cmd/jp/canvas_windows.go | 10 + cmd/jp/flag.go | 6 +- cmd/jp/line.go | 12 +- cmd/jp/main.go | 71 +- cmd/jp/scatter.go | 56 + docs/bar_chart.png | Bin 46350 -> 42164 bytes docs/line_chart.png | Bin 41045 -> 47349 bytes docs/scatter_plot.png | Bin 0 -> 86690 bytes examples/mvrnorm.json | 12002 ++++++++++++++++++++++++++++++++++ pkg/draw/box.go | 6 + pkg/draw/braille.go | 42 + pkg/draw/buffer.go | 108 + pkg/draw/canvas.go | 23 + pkg/draw/full.go | 11 + pkg/draw/line.go | 67 + pkg/draw/quarter.go | 37 + pkg/draw/runes.go | 12 + pkg/draw/runes_windows.go | 11 + pkg/jp/barchart.go | 124 - pkg/jp/datatable.go | 14 - pkg/jp/linechart.go | 205 - pkg/jp/primitives/box.go | 6 - pkg/jp/primitives/buffer.go | 9 - pkg/jp/primitives/format.go | 8 - pkg/jp/primitives/line.go | 68 - pkg/jp/primitives/runes.go | 18 - pkg/plot/barchart.go | 87 + pkg/plot/datatable.go | 14 + pkg/plot/format.go | 15 + pkg/plot/linechart.go | 107 + pkg/plot/scatterchart.go | 63 + pkg/terminal/terminal_nosysioctl.go | 2 +- 36 files changed, 12923 insertions(+), 584 deletions(-) create mode 100644 cmd/jp/canvas_other.go create mode 100644 cmd/jp/canvas_windows.go create mode 100644 cmd/jp/scatter.go create mode 100644 docs/scatter_plot.png create mode 100644 examples/mvrnorm.json create mode 100644 pkg/draw/box.go create mode 100644 pkg/draw/braille.go create mode 100644 pkg/draw/buffer.go create mode 100644 pkg/draw/canvas.go create mode 100644 pkg/draw/full.go create mode 100644 pkg/draw/line.go create mode 100644 pkg/draw/quarter.go create mode 100644 pkg/draw/runes.go create mode 100644 pkg/draw/runes_windows.go delete mode 100644 pkg/jp/barchart.go delete mode 100644 pkg/jp/datatable.go delete mode 100644 pkg/jp/linechart.go delete mode 100644 pkg/jp/primitives/box.go delete mode 100644 pkg/jp/primitives/buffer.go delete mode 100644 pkg/jp/primitives/format.go delete mode 100644 pkg/jp/primitives/line.go delete mode 100644 pkg/jp/primitives/runes.go create mode 100644 pkg/plot/barchart.go create mode 100644 pkg/plot/datatable.go create mode 100644 pkg/plot/format.go create mode 100644 pkg/plot/linechart.go create mode 100644 pkg/plot/scatterchart.go diff --git a/Makefile b/Makefile index a12c493..34e18e2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION = 1.0.1 +VERSION = 1.1.0 APP := jp PACKAGES := $(shell go list -f {{.Dir}} ./...) diff --git a/README.md b/README.md index 3e701ed..6e28155 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,10 @@ Dead simple terminal plots from JSON data. Bar charts and line charts are suppor - [Y values only (X=index)](#y-values-only-xindex) - [Array data](#array-data) - [Line chart](#line-chart) - - [Y values only (X=index)](#y-values-only-xindex-1) - [Array data, separate X and Y values](#array-data-separate-x-and-y-values) - [Array data, XY pairs](#array-data-xy-pairs) + - [Y values only (X=index)](#y-values-only-xindex-1) + - [Scatter plot](#scatter-plot) @@ -30,16 +31,16 @@ Or [download the binary](https://github.com/sgreben/jp/releases/latest) from the ```bash # Linux -curl -LO https://github.com/sgreben/jp/releases/download/1.0.1/jp_1.0.1_linux_x86_64.zip -unzip jp_1.0.1_linux_x86_64.zip +curl -LO https://github.com/sgreben/jp/releases/download/1.1.0/jp_1.1.0_linux_x86_64.zip +unzip jp_1.1.0_linux_x86_64.zip # OS X -curl -LO https://github.com/sgreben/jp/releases/download/1.0.1/jp_1.0.1_osx_x86_64.zip -unzip jp_1.0.1_osx_x86_64.zip +curl -LO https://github.com/sgreben/jp/releases/download/1.1.0/jp_1.1.0_osx_x86_64.zip +unzip jp_1.1.0_osx_x86_64.zip # Windows -curl -LO https://github.com/sgreben/jp/releases/download/1.0.1/jp_1.0.1_windows_x86_64.zip -unzip jp_1.0.1_windows_x86_64.zip +curl -LO https://github.com/sgreben/jp/releases/download/1.1.0/jp_1.1.0_windows_x86_64.zip +unzip jp_1.1.0_windows_x86_64.zip ``` ## Use it @@ -49,7 +50,7 @@ unzip jp_1.0.1_windows_x86_64.zip ```text Usage of jp: -type value - Plot type. One of [line bar] (default line) + Plot type. One of [line bar scatter] (default line) -x string x values (JSONPath expression) -y string @@ -60,6 +61,8 @@ Usage of jp: Plot height (default 0 (auto)) -width int Plot width (default 0 (auto)) + -canvas value + Canvas type. One of [full quarter braille auto] (default auto) ``` ## Examples @@ -68,6 +71,7 @@ Usage of jp: In case you're on mobile, here's some PNGs of what `jp` output looks like: +- ![Scatter plot](docs/scatter_plot.png) - ![Bar chart](docs/bar_chart.png) - ![Line chart](docs/line_chart.png) @@ -77,16 +81,18 @@ In case you're on mobile, here's some PNGs of what `jp` output looks like: ```bash $ cat examples/tcp-time.json | jp -x ..Label -y ..Count -type bar - 69.0 - █████████████ - █████████████ - █████████████ - █████████████ - █████████████ 21.0 - █████████████ █████████████ 7.0 2.0 1.0 - █████████████ █████████████ ▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁ - - 46.85267ms 48.38578ms 49.91889ms 51.452ms 52.98511ms + + 69 + █████████████ + █████████████ + █████████████ + █████████████ + █████████████ + █████████████ 21 + █████████████ █████████████ 7 2 1 + █████████████ █████████████ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁ + + 46.85267ms 48.38578ms 49.91889ms 51.452ms 52.98511ms ``` @@ -94,16 +100,18 @@ $ cat examples/tcp-time.json | jp -x ..Label -y ..Count -type bar ```bash $ cat examples/tcp-time.json | jp -xy "..[Label,Count]" -type bar - 69.0 - █████████████ - █████████████ - █████████████ - █████████████ - █████████████ 21.0 - █████████████ █████████████ 7.0 2.0 1.0 - █████████████ █████████████ ▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁ - - 46.85267ms 48.38578ms 49.91889ms 51.452ms 52.98511ms + + 69 + █████████████ + █████████████ + █████████████ + █████████████ + █████████████ + █████████████ 21 + █████████████ █████████████ 7 2 1 + █████████████ █████████████ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁ + + 46.85267ms 48.38578ms 49.91889ms 51.452ms 52.98511ms ``` #### Y values only (X=index) @@ -111,16 +119,17 @@ $ cat examples/tcp-time.json | jp -xy "..[Label,Count]" -type bar ```bash $ cat examples/tcp-time.json | jp -y ..Count -type bar - 69.0 - █████████████ - █████████████ - █████████████ - █████████████ - █████████████ 21.0 - █████████████ █████████████ 7.0 2.0 1.0 - █████████████ █████████████ ▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁ - - 0 1 2 3 4 + 69 + █████████████ + █████████████ + █████████████ + █████████████ + █████████████ + █████████████ 21 + █████████████ █████████████ 7 2 1 + █████████████ █████████████ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁ + + 0 1 2 3 4 ``` #### Array data @@ -128,83 +137,145 @@ $ cat examples/tcp-time.json | jp -y ..Count -type bar ```bash $ echo '[[-3, 5], [-2, 0], [-1, 0.1], [0, 1], [1, 2], [2, 3]]' | jp -xy '[*][0, 1]' -type bar - 5.0 - ███████████ - ███████████ - ███████████ 3.0 - ███████████ ███████████ - ███████████ 2.0 ███████████ - ███████████ 0.0 0.1 1.0 ███████████ ███████████ - ███████████ ▁▁▁▁▁▁▁▁▁▁▁ ███████████ ███████████ ███████████ - - -3 -2 -1 0 1 2 + 5 + ███████████ + ███████████ + ███████████ 3 + ███████████ ▄▄▄▄▄▄▄▄▄▄▄ + ███████████ 2 ███████████ + ███████████ 1 ███████████ ███████████ + ███████████ 0.1 ▄▄▄▄▄▄▄▄▄▄▄ ███████████ ███████████ + ███████████ 0 ▁▁▁▁▁▁▁▁▁▁▁ ███████████ ███████████ ███████████ + + -3 -2 -1 0 1 2 ``` ### Line chart -#### Y values only (X=index) +#### Array data, separate X and Y values ```bash -$ cat examples/tcp-time.json | jp -y ..Duration - -59970040.9│ - │ - │ - │ █ - │██ - │██ █ - │█ █ █ █ - y█ █ ██ ██ █ █ ███ ███ █ █ █ - │ ██ ██ ███ ████ ███ ██ █ ██ █ ███ █ ██ █ █ ██ ██ ██ ██ █ ██ █ █ █ █ - │ █ █ ██ █ ██ ████ ██ █████ ██████ █ ██ ████████ █ ██ █ - │ - │ - │ -42167403.0└───────────────────────────────────────x───────────────────────────────────────────────── - 0.0 99.0 +$ jq -n '[range(200)/20 | [., sin]]' | jp -x '[*][0]' -y '[*][1]' + 1.059955│ ▄▄▄▄▖ ▄▄▄▄▖ + │ ▗▀ ▝▚▖ ▗▛▘ ▝▚▖ + │ ▟▘ ▀▖ ▄▘ ▝▖ + │ ▟ ▝▖ ▞▘ ▝▖ + │ ▟ ▝▖ ▞▘ ▝▖ + │ ▐ ▐ ▗▘ ▐▖ + │ ▐▘ ▚ ▗▘ ▐ + │▗▘ ▚ ▞ ▚ + │▞ ▝▖ ▞ ▌ + │ ▝▖ ▐ ▝▖ + │ ▐ ▗▘ ▀▖ + │ ▚ ▗▘ ▚ + │ ▚ ▗▞ ▙ + │ ▝▚ ▞ ▘ + │ ▝▄ ▗▞ + │ ▝▙ ▞ + │ ▚▖ ▄▛ + │ ▝▀▀▀▀▘ + -1.059955└───────────────────────────────────────────────────────────────────── + 0 10.547 ``` -#### Array data, separate X and Y values +#### Array data, XY pairs ```bash -$ jq -n '[range(200)/20 | [., sin]]' | jp -x '[*][0]' -y '[*][1]' +$ jq -n '[range(200)/20 | [., sin]]' | jp -xy '[*][0, 1]' -1.1 │ ███████ ██████ - │ ███ ███ ███ ███ - │ ███ ██ ██ ███ - │ ██ ███ ██ ██ - │ █ ██ ██ ███ - │██ ██ ██ ██ - │█ █ ██ ██ - y █ ██ █ - │ ██ ██ ██ - │ ██ ██ ██ - │ ██ ██ - │ ██ ████ - │ ██████ --1.1└───────────────────────────────────x─────────────────────────────────────── - 0.0 9.9 + 1.059955│ ▄▄▄▄▖ ▄▄▄▄▖ + │ ▗▀ ▝▚▖ ▗▛▘ ▝▚▖ + │ ▟▘ ▀▖ ▄▘ ▝▖ + │ ▟ ▝▖ ▞▘ ▝▖ + │ ▟ ▝▖ ▞▘ ▝▖ + │ ▐ ▐ ▗▘ ▐▖ + │ ▐▘ ▚ ▗▘ ▐ + │▗▘ ▚ ▞ ▚ + │▞ ▝▖ ▞ ▌ + │ ▝▖ ▐ ▝▖ + │ ▐ ▗▘ ▀▖ + │ ▚ ▗▘ ▚ + │ ▚ ▗▞ ▙ + │ ▝▚ ▞ ▘ + │ ▝▄ ▗▞ + │ ▝▙ ▞ + │ ▚▖ ▄▛ + │ ▝▀▀▀▀▘ + -1.059955└───────────────────────────────────────────────────────────────────── + 0 10.547 ``` -#### Array data, XY pairs +#### Y values only (X=index) ```bash -$ jq -n '[range(200)/20 | [., sin]]' | jp -xy '[*][0, 1]' +$ cat examples/tcp-time.json | jp -y ..Duration -1.1 │ ███████ ██████ - │ ███ ███ ███ ███ - │ ███ ██ ██ ███ - │ ██ ███ ██ ██ - │ █ ██ ██ ███ - │██ ██ ██ ██ - │█ █ ██ ██ - y █ ██ █ - │ ██ ██ ██ - │ ██ ██ ██ - │ ██ ██ - │ ██ ████ - │ ██████ --1.1└───────────────────────────────────x─────────────────────────────────────── - 0.0 9.9 + 5.342888e+07│▐▌ + │▐▌ + │▐▐ + │▐▐ ▐ + │▐▐ ▐ + │▐▐ ▐ + │▐▐ ▐ + │▞▐ ▐▖ + │▌▐ ▐▌ + │▌▐ ▐▌ + │▌▐ ▞▌ ▌ + │▌▐ ▗ ▄ ▌▌ ▌ + │▘▐ ▗ ▐ ▞▐ ▌▌ ▌ + │ ▐ ▗ ▟ █ ▌▐ ▌▌ ▗▌ + │ ▐ ▙ ▐▌ █ █ ▌▐ ▟ ▌▌ ▐▌ + │ ▌ ▛▖ ▐▐ ▗▜ █ ▌ ▌▗ █ ▌▌ ▐▌ + │ ▌ ▟ ▚ ▌▐▗ ▐▝▟▐ ▌ ▌▟▐ ▌ ▌▚ ▐▌ ▖ + │ ▌ █ ▐ ▌▐▐▌▐ █ ▌▗ ▐ ▌▛▟ ▌▗ ▌▐ ▐▐ ▌ + │ ▌▗▀ ▝▄▘ ▛▐▌ ▛ ▌▟ ▐ ▀ ▌ ▌▐ ▌▐ ▐▐ ▗▚ + │ ▌▐ ▘ ▌ ▘ ▌ ▌█ ▐ ▘ ▐▛▖▌▐ ▐▐ ▗▌ ▖ ▖ ▐▐ + │ ▙▐ ▘ ▘ █▐ ▐ ▝▌▌▌▐▄ ▞▐ ▌▌▗▜ ▐▌ ▐▐ ▗▖ + │ ▜▐ ▌ ▌▟ █ ▌▌▐ ▄▘▚▐▐ ▗▄ ▌▐▖ ▗ ▞▐▐ ▞▚ ▗ ▖▗▌▐▄ + │ ▝▞ ▙▘ ▜ ▐▌▐▐▜ ▐▞ ▌▐▝▟ ▝▄▀▄▘▝▞▀▘▝▖ ▞▄▌▌▌▌ + │ ▌ ▛ ▐ ▌▐▌▝ ▝▌ ▚▌ ▌ ▚▞▘▜▐ ▐▘ + │ ▘ ▘ ▝ ▝▌ ▘ ▝ ▝ + │ + │ + │ + 4.591664e+07└────────────────────────────────────────────────────────────────── + 0 97.02 ``` + +### Scatter plot + +```bash +$ cat examples/mvrnorm.json | jp -xy '..[x,y]' -type scatter + + 3.535344│ ⠄ ⠄ + │ ⠈⠂ ⠂ ⡀ ⠂ + │ ⠐⡀⡀⡂ ⠁ ⢄ ⠁ ⠠ + │ ⡀ ⠆ ⠈ ⠄⡀ ⠂ + │ ⡀ ⠠ ⡀ ⡀ ⠄ ⡀⠐⠄⠁⠐ ⠠⢆⠠⠂⠂⠄⣀⢈ ⡀⠈ ⡀ + │ ⡀⠂⠂⠄ ⡀⠂⢔⠠ ⢤⢀⠌⣡⠁⠦⠄⠐⡐⠂⣀⠅⠁⠈ ⠂ ⠈⠁⠁ ⡀ ⠄ + │ ⡀ ⢀ ⠄ ⠈⠠ ⠡⠑⠈⠈⢢⡁⡄⢈⠂⢡⠈⡄⡀⠈⠰⢉⡠⠘⢄⢃⠉⢀⣄⠢⠠⠄ ⠠ ⡀⠁ ⡀ ⠂ + │ ⠈ ⡂⠈⡁⠈⠄⢂⡹⡐⡡⡆⡥⣙⡶⡼⠱⣅⣅⣼⢗⡱⢐⣈⠑⢁⠂ ⢐⢁⠭⠘⡀ ⠈ + │ ⠁ ⢀⠄⢈⠈⡰⢀⡥⠋⣧⣓⣚⡛⢲⣽⣝⣭⢙⣟⢲⡽⣋⡠⣿⣜⣵⠙⡦⠗ ⣡⠁⠁⠁⠄⠠ ⠄⡂ + │ ⠄⠌⠌⠡⠉⡐⢯⣵⡏⢵⡞⠂⢰⣽⣷⢛⣯⡣⣷⢭⣞⣏⠤⣾⢡⡻⠢⢊⢠⡠⠸⢄⣃⡀⢁⠐ ⠐⡀ ⠂ ⠄ + │ ⠨ ⡈⠂ ⢀⢑⠄⣜⡾⣴⢨⠶⣪⣧⢿⣷⣷⡱⣿⣞⣲⣮⣮⣯⢾⡷⡬⡷⣺⠤⢏⡼⣨⢌⡬⠠⢂⢠⠒⠱⠆⡈ + │ ⠈ ⠃ ⠄⡐⠂⠐⢀⢈⣂⡈⣳⣷⣜⢺⣿⣹⣷⣼⣯⡿⣃⣽⣿⢾⣟⣾⢵⣻⠯⡼⡃⣼⣗⢲⠪⠇⣉⠺ ⢱⠠⠙⡀⢐⠌ + │ ⠄ ⠈⠊⠐⠑⠨⠚⢁⡊⢾⡶⢩⢿⣏⣽⢞⣼⣇⣵⣿⣿⣽⣿⢽⣭⠺⣿⣽⣳⢚⣾⣻⣾⣜⠩⡒⣃⠈⢢⠕⢂⢰⡀ ⡔⢀⢀ ⡀ + │ ⢀⠂⠁⠂⠇ ⠂⠊⢀⠐⡘⡍⡇⣚⢸⢟⣯⢿⣳⡪⣫⣵⣿⣯⣿⢿⣷⣻⣖⣗⣻⣚⢥⡷⣕⣏⠶⠊⠄⣠⠰⠂⡄⠂ ⠄⠁ + │ ⠠⢂ ⠘⠐⣀ ⣀⢡⢐⠔⢫⠯⢕⠫⠿⣹⢶⣾⡻⣭⣽⢗⠿⣹⣛⣺⣿⠯⢲⡼⣵⢉⣭⢐⣟⡍⠄⠈⠥⠄ ⠁ ⠄ + │ ⠈⠂ ⠊⡀⡈⠢⡌⡠⠖⢤⠥⡑⣯⣾⣴⣯⡿⣯⣝⣯⣿⠧⣽⣒⢾⣼⣻⣛⣗⡹⡽⢪⠯⠒⡨⠈ ⠈⡐⢄ ⠂⠘⠠ ⠂ + │ ⠰ ⡀⠃⠁⠠ ⠉⡈⡨⡱⢍⠌⠷⣯⠫⠬⡙⣴⣯⡣⡟⡮⠩⣫⠿⢞⢵⡰⠞⡂⠴ ⠕⢀⡂⠁ ⢀ ⠤ ⠈ + │ ⢩⠂ ⠁⡄ ⢀⠲⢂⠑⢁⡘⠄⠵⣣⢑⢻⠨⡩⣌⠕⢮⣮⣋⢹⡁⣊⡃⠈⡕⡘⡠⠨⠄⡘⠨ ⠊⠁ ⠂ + │ ⠐ ⢀ ⠈⠐⠔⠈ ⠁⢀⣀⡃⣊⢁⡘⠁⠛⠨ ⠒⡑⡀⠵⢙⠄⠡⠢⠃⠄⠋⠅ ⠥⠁⠠⢀ ⠄ + │ ⢀⢁ ⠆ ⠉⠁⠐ ⠄⠁⢑⡀⢀⠠⠑⢡⢊⠂⠑⠌⡅⠊⠄⠉⢈⡐ ⡀ ⠠ ⠂ + │ ⢀ ⠑ ⠂ ⡁ ⠌⢠⠈⠂⠄⠉⡃⠈⠄ ⠂⠠⠁ ⠄ ⢀ ⠠ + │ ⠐ ⠐ ⢁⠂⠂⠢⠠⠄⠔⠐ ⠁ + │ ⠢ ⠁ ⠂⠐ ⠐ ⠐ ⠈ + │ ⢀ ⠄⠈ ⠈ + │ ⠐ + │ + │ + │ ⠈ +-4.271874└───────────────────────────────────────────────────────────────────── + -4.08815 3.79083 +``` \ No newline at end of file diff --git a/cmd/jp/bar.go b/cmd/jp/bar.go index e1a622b..15dd196 100644 --- a/cmd/jp/bar.go +++ b/cmd/jp/bar.go @@ -4,8 +4,8 @@ import ( "fmt" "reflect" - "github.com/sgreben/jp/pkg/jp" - "github.com/sgreben/jp/pkg/jp/primitives" + "github.com/sgreben/jp/pkg/draw" + "github.com/sgreben/jp/pkg/plot" ) func barPlotData(xvv, yvv [][]reflect.Value) (x []string, y []float64) { @@ -29,10 +29,10 @@ func barPlotData(xvv, yvv [][]reflect.Value) (x []string, y []float64) { return } -func barPlot(xvv, yvv [][]reflect.Value, box primitives.Box) string { +func barPlot(xvv, yvv [][]reflect.Value, c draw.Canvas) string { groups, y := barPlotData(xvv, yvv) - chart := jp.NewBarChart(box.Width, box.Height) - data := new(jp.DataTable) + chart := plot.NewBarChart(c) + data := new(plot.DataTable) if len(groups) != len(y) { for i := range y { data.AddColumn(fmt.Sprint(i)) diff --git a/cmd/jp/canvas_other.go b/cmd/jp/canvas_other.go new file mode 100644 index 0000000..053cef7 --- /dev/null +++ b/cmd/jp/canvas_other.go @@ -0,0 +1,10 @@ +// +build !windows + +package main + +var autoCanvas = map[string]string{ + plotTypeBar: canvasTypeQuarter, + plotTypeLine: canvasTypeQuarter, + plotTypeScatter: canvasTypeBraille, + plotTypeHist: canvasTypeQuarter, +} diff --git a/cmd/jp/canvas_windows.go b/cmd/jp/canvas_windows.go new file mode 100644 index 0000000..de22e5b --- /dev/null +++ b/cmd/jp/canvas_windows.go @@ -0,0 +1,10 @@ +// +build windows + +package main + +var autoCanvas = map[string]string{ + plotTypeBar: canvasTypeFull, + plotTypeLine: canvasTypeFull, + plotTypeScatter: canvasTypeFull, + plotTypeHist: canvasTypeFull, +} diff --git a/cmd/jp/flag.go b/cmd/jp/flag.go index 533b35d..eeec98c 100644 --- a/cmd/jp/flag.go +++ b/cmd/jp/flag.go @@ -6,8 +6,8 @@ import ( ) type enumVar struct { - Choices []string // The acceptable choices the user may pass to the flag - Value string // the current value of the flag + Choices []string + Value string } // Set implements the flag.Value interface. @@ -18,7 +18,7 @@ func (so *enumVar) Set(v string) error { return nil } } - return fmt.Errorf("invalid choice; must be one of %s", strings.Join(so.Choices, ",")) + return fmt.Errorf("must be one of [%s]", strings.Join(so.Choices, " ")) } func (so *enumVar) String() string { diff --git a/cmd/jp/line.go b/cmd/jp/line.go index b47b38c..caca49f 100644 --- a/cmd/jp/line.go +++ b/cmd/jp/line.go @@ -3,8 +3,9 @@ package main import ( "reflect" - "github.com/sgreben/jp/pkg/jp" - "github.com/sgreben/jp/pkg/jp/primitives" + "github.com/sgreben/jp/pkg/draw" + + "github.com/sgreben/jp/pkg/plot" ) func linePlotData(xvv, yvv [][]reflect.Value) (x, y []float64) { @@ -31,10 +32,10 @@ func linePlotData(xvv, yvv [][]reflect.Value) (x, y []float64) { return } -func linePlot(xvv, yvv [][]reflect.Value, box primitives.Box) string { +func linePlot(xvv, yvv [][]reflect.Value, c draw.Canvas) string { x, y := linePlotData(xvv, yvv) - chart := jp.NewLineChart(box.Width, box.Height) - data := new(jp.DataTable) + chart := plot.NewLineChart(c) + data := new(plot.DataTable) data.AddColumn("x") data.AddColumn("y") n := len(x) @@ -51,6 +52,5 @@ func linePlot(xvv, yvv [][]reflect.Value, box primitives.Box) string { for i := 0; i < n; i++ { data.AddRow(x[i%len(x)], y[i%len(y)]) } - chart.Symbol = "█" return chart.Draw(data) } diff --git a/cmd/jp/main.go b/cmd/jp/main.go index c603f68..07de252 100644 --- a/cmd/jp/main.go +++ b/cmd/jp/main.go @@ -8,24 +8,34 @@ import ( "os" "reflect" - "github.com/sgreben/jp/pkg/jp/primitives" + "github.com/sgreben/jp/pkg/draw" "github.com/sgreben/jp/pkg/terminal" "github.com/sgreben/jp/pkg/jsonpath" ) type configuration struct { - Box primitives.Box - X string - Y string - XY string - PlotType enumVar + Box draw.Box + X string + Y string + XY string + PlotType enumVar + CanvasType enumVar } -const plotTypeLine = "line" -const plotTypeBar = "bar" -const plotTypeScatter = "scatter" -const plotTypeHist = "hist" +const ( + plotTypeLine = "line" + plotTypeBar = "bar" + plotTypeScatter = "scatter" + plotTypeHist = "hist" +) + +const ( + canvasTypeFull = "full" + canvasTypeQuarter = "quarter" + canvasTypeBraille = "braille" + canvasTypeAuto = "auto" +) var config = configuration{ PlotType: enumVar{ @@ -33,16 +43,29 @@ var config = configuration{ Choices: []string{ plotTypeLine, plotTypeBar, + plotTypeScatter, + }, + }, + CanvasType: enumVar{ + Value: canvasTypeAuto, + Choices: []string{ + canvasTypeFull, + canvasTypeQuarter, + canvasTypeBraille, + canvasTypeAuto, }, }, } -var xPattern *jsonpath.JSONPath -var yPattern *jsonpath.JSONPath -var xyPattern *jsonpath.JSONPath +var ( + xPattern *jsonpath.JSONPath + yPattern *jsonpath.JSONPath + xyPattern *jsonpath.JSONPath +) func init() { flag.Var(&config.PlotType, "type", fmt.Sprintf("Plot type. One of %v", config.PlotType.Choices)) + flag.Var(&config.CanvasType, "canvas", fmt.Sprintf("Canvas type. One of %v", config.CanvasType.Choices)) flag.StringVar(&config.X, "x", "", "x values (JSONPath expression)") flag.StringVar(&config.Y, "y", "", "y values (JSONPath expression)") flag.StringVar(&config.XY, "xy", "", "x,y value pairs (JSONPath expression). Overrides -x and -y if given.") @@ -75,6 +98,9 @@ func init() { if config.Box.Height == 0 { config.Box.Height = terminal.Height() - 1 } + if config.CanvasType.Value == canvasTypeAuto { + config.CanvasType.Value = autoCanvas[config.PlotType.Value] + } } func match(in interface{}, p *jsonpath.JSONPath) [][]reflect.Value { @@ -100,11 +126,24 @@ func main() { x = match(in, xPattern) y = match(in, yPattern) } + buffer := draw.NewBuffer(config.Box) + var p draw.Pixels + switch config.CanvasType.Value { + case canvasTypeBraille: + p = &draw.Braille{Buffer: buffer} + case canvasTypeQuarter: + p = &draw.Quarter{Buffer: buffer} + case canvasTypeFull: + p = &draw.Full{Buffer: buffer} + } + p.Clear() + c := draw.Canvas{Pixels: p} switch config.PlotType.Value { case plotTypeLine: - fmt.Print(linePlot(x, y, config.Box)) + fmt.Println(linePlot(x, y, c)) + case plotTypeScatter: + fmt.Println(scatterPlot(x, y, c)) case plotTypeBar: - fmt.Print(barPlot(x, y, config.Box)) + fmt.Println(barPlot(x, y, c)) } - } diff --git a/cmd/jp/scatter.go b/cmd/jp/scatter.go new file mode 100644 index 0000000..9df8b4a --- /dev/null +++ b/cmd/jp/scatter.go @@ -0,0 +1,56 @@ +package main + +import ( + "reflect" + + "github.com/sgreben/jp/pkg/draw" + + "github.com/sgreben/jp/pkg/plot" +) + +func scatterPlotData(xvv, yvv [][]reflect.Value) (x, y []float64) { + for _, xv := range xvv { + for i := range xv { + if xv[i].IsValid() && xv[i].CanInterface() { + xvi, ok := xv[i].Interface().(float64) + if ok { + x = append(x, xvi) + } + } + } + } + for _, yv := range yvv { + for i := range yv { + if yv[i].IsValid() && yv[i].CanInterface() { + yvi, ok := yv[i].Interface().(float64) + if ok { + y = append(y, yvi) + } + } + } + } + return +} + +func scatterPlot(xvv, yvv [][]reflect.Value, c draw.Canvas) string { + x, y := scatterPlotData(xvv, yvv) + chart := plot.NewScatterChart(c) + data := new(plot.DataTable) + data.AddColumn("x") + data.AddColumn("y") + n := len(x) + if len(y) > n { + n = len(y) + } + // If no valid xs are given, use the indices as x values. + if len(x) == 0 { + x = make([]float64, len(y)) + for i := 0; i < len(y); i++ { + x[i] = float64(i) + } + } + for i := 0; i < n; i++ { + data.AddRow(x[i%len(x)], y[i%len(y)]) + } + return chart.Draw(data) +} diff --git a/docs/bar_chart.png b/docs/bar_chart.png index 7d47f2a..47a89e1 100644 Binary files a/docs/bar_chart.png and b/docs/bar_chart.png differ diff --git a/docs/line_chart.png b/docs/line_chart.png index 6c168a3..80357cb 100644 Binary files a/docs/line_chart.png and b/docs/line_chart.png differ diff --git a/docs/scatter_plot.png b/docs/scatter_plot.png new file mode 100644 index 0000000..bdd5f0e Binary files /dev/null and b/docs/scatter_plot.png differ diff --git a/examples/mvrnorm.json b/examples/mvrnorm.json new file mode 100644 index 0000000..f53b75b --- /dev/null +++ b/examples/mvrnorm.json @@ -0,0 +1,12002 @@ +[ + { + "x": -0.4338, + "y": -0.0224 + }, + { + "x": -1.2232, + "y": -1.7639 + }, + { + "x": -0.092, + "y": 0.4678 + }, + { + "x": -0.55, + "y": -0.1672 + }, + { + "x": 0.8358, + "y": -0.7396 + }, + { + "x": -0.8512, + "y": -2.1754 + }, + { + "x": 0.7703, + "y": -1.684 + }, + { + "x": 0.9778, + "y": 0.2346 + }, + { + "x": -0.8232, + "y": 0.6021 + }, + { + "x": -1.09, + "y": -0.9606 + }, + { + "x": 0.4675, + "y": -1.2182 + }, + { + "x": 1.2294, + "y": -0.612 + }, + { + "x": 0.2564, + "y": -1.8518 + }, + { + "x": 1.2165, + "y": 1.5875 + }, + { + "x": -1.1669, + "y": 0.4164 + }, + { + "x": 1.1598, + "y": 0.3446 + }, + { + "x": 0.3701, + "y": -1.8253 + }, + { + "x": 0.6374, + "y": -1.7835 + }, + { + "x": 0.2492, + "y": -0.3061 + }, + { + "x": 0.1369, + "y": -1.8699 + }, + { + "x": -0.1534, + "y": -0.1076 + }, + { + "x": 2.5378, + "y": -0.1148 + }, + { + "x": 1.1414, + "y": -0.4227 + }, + { + "x": -0.7786, + "y": -0.3173 + }, + { + "x": -0.3563, + "y": 1.1658 + }, + { + "x": 2.1436, + "y": 0.4586 + }, + { + "x": -0.4243, + "y": -0.8846 + }, + { + "x": 1.099, + "y": -0.9139 + }, + { + "x": 0.7341, + "y": -1.3188 + }, + { + "x": 0.3771, + "y": -0.6355 + }, + { + "x": -2.3198, + "y": -1.2687 + }, + { + "x": 0.9466, + "y": 0.274 + }, + { + "x": -2.5867, + "y": 0.3888 + }, + { + "x": -0.3587, + "y": 0.9501 + }, + { + "x": -0.1967, + "y": -0.9302 + }, + { + "x": 0.4722, + "y": -0.2876 + }, + { + "x": 0.6935, + "y": 1.4842 + }, + { + "x": 0.4163, + "y": -0.7427 + }, + { + "x": 0.0296, + "y": 0.9153 + }, + { + "x": 0.2581, + "y": -0.5955 + }, + { + "x": -0.6246, + "y": -0.0194 + }, + { + "x": 0.8978, + "y": -0.435 + }, + { + "x": -0.5765, + "y": -1.5643 + }, + { + "x": -1.3451, + "y": 1.821 + }, + { + "x": -0.0097, + "y": 0.0981 + }, + { + "x": 0.6144, + "y": -1.0336 + }, + { + "x": -1.033, + "y": 0.1675 + }, + { + "x": 1.1086, + "y": 1.0135 + }, + { + "x": 0.24, + "y": -0.1399 + }, + { + "x": -0.5265, + "y": 0.1699 + }, + { + "x": -0.4864, + "y": 1.4061 + }, + { + "x": -1.0437, + "y": -0.2007 + }, + { + "x": -0.9415, + "y": -0.5451 + }, + { + "x": -0.0623, + "y": -0.9202 + }, + { + "x": 0.4866, + "y": 1.1309 + }, + { + "x": -1.6893, + "y": -1.8922 + }, + { + "x": 1.0813, + "y": -0.8466 + }, + { + "x": -0.822, + "y": 0.189 + }, + { + "x": -0.8949, + "y": 1.4773 + }, + { + "x": -0.131, + "y": 0.403 + }, + { + "x": 0.7297, + "y": 1.3343 + }, + { + "x": -0.173, + "y": -0.5286 + }, + { + "x": -0.6834, + "y": 0.5082 + }, + { + "x": 1.5151, + "y": 0.4908 + }, + { + "x": 1.2301, + "y": 0.8553 + }, + { + "x": -0.1307, + "y": -0.6051 + }, + { + "x": 1.9388, + "y": 0.2044 + }, + { + "x": 1.4048, + "y": -1.094 + }, + { + "x": -1.8354, + "y": -0.1311 + }, + { + "x": -0.9335, + "y": 0.1399 + }, + { + "x": 1.0078, + "y": -0.7602 + }, + { + "x": 0.8201, + "y": 0.319 + }, + { + "x": 1.1169, + "y": 0.2431 + }, + { + "x": 0.6262, + "y": -2.0147 + }, + { + "x": 1.2169, + "y": 0.0008 + }, + { + "x": -0.5127, + "y": -1.1056 + }, + { + "x": -1.4487, + "y": 0.6535 + }, + { + "x": -0.1074, + "y": 0.0536 + }, + { + "x": 1.2785, + "y": -0.5805 + }, + { + "x": -0.7918, + "y": 2.0026 + }, + { + "x": -0.5918, + "y": 1.0478 + }, + { + "x": -0.0679, + "y": -1.4035 + }, + { + "x": -1.0281, + "y": -1.2594 + }, + { + "x": -0.4734, + "y": 0.9634 + }, + { + "x": 1.0925, + "y": 0.4544 + }, + { + "x": 0.1584, + "y": -1.1382 + }, + { + "x": -0.4662, + "y": -0.9309 + }, + { + "x": -1.6006, + "y": -0.6181 + }, + { + "x": 0.1662, + "y": 0.9687 + }, + { + "x": 0.4664, + "y": -1.336 + }, + { + "x": -2.3806, + "y": -1.0268 + }, + { + "x": 0.3816, + "y": 1.5156 + }, + { + "x": -0.4057, + "y": 0.8135 + }, + { + "x": -1.6688, + "y": 0.991 + }, + { + "x": -1.0106, + "y": 0.3264 + }, + { + "x": -1.3403, + "y": 0.8187 + }, + { + "x": -0.2728, + "y": 2.9131 + }, + { + "x": 1.4519, + "y": -0.4581 + }, + { + "x": -0.3322, + "y": -0.0608 + }, + { + "x": -0.6436, + "y": -0.3212 + }, + { + "x": -1.2526, + "y": 0.7761 + }, + { + "x": -1.9087, + "y": 0.05 + }, + { + "x": -1.2135, + "y": 1.2349 + }, + { + "x": 1.4034, + "y": 1.5624 + }, + { + "x": -2.3177, + "y": -1.4137 + }, + { + "x": -0.713, + "y": 0.2339 + }, + { + "x": 0.3951, + "y": 0.8168 + }, + { + "x": -2.4405, + "y": -0.4962 + }, + { + "x": 0.4724, + "y": 0.0078 + }, + { + "x": 0.6332, + "y": 2.1505 + }, + { + "x": 1.1937, + "y": -0.3866 + }, + { + "x": -0.8169, + "y": 0.2658 + }, + { + "x": 0.6039, + "y": 0.4119 + }, + { + "x": -2.0716, + "y": -0.6879 + }, + { + "x": -0.2379, + "y": -0.2192 + }, + { + "x": -0.0128, + "y": -1.8972 + }, + { + "x": -0.4122, + "y": -0.1406 + }, + { + "x": -0.8209, + "y": 1.7228 + }, + { + "x": 0.973, + "y": -0.9183 + }, + { + "x": -3.8421, + "y": 1.652 + }, + { + "x": 0.0403, + "y": -0.0136 + }, + { + "x": -0.4901, + "y": 0.2448 + }, + { + "x": -0.1592, + "y": -1.168 + }, + { + "x": -2.3838, + "y": 0.5446 + }, + { + "x": 1.0713, + "y": -0.265 + }, + { + "x": 1.6445, + "y": 0.7718 + }, + { + "x": -0.4299, + "y": 0.4563 + }, + { + "x": 0.0103, + "y": 0.109 + }, + { + "x": -0.1246, + "y": 0.6258 + }, + { + "x": -0.6811, + "y": -0.4092 + }, + { + "x": 0.2963, + "y": 0.3953 + }, + { + "x": -0.4716, + "y": -1.4683 + }, + { + "x": 0.3918, + "y": 0.7538 + }, + { + "x": -0.3215, + "y": 0.3926 + }, + { + "x": 1.4489, + "y": 1.2646 + }, + { + "x": 0.7927, + "y": -0.4824 + }, + { + "x": -0.775, + "y": -1.0249 + }, + { + "x": -0.1907, + "y": -0.9543 + }, + { + "x": -0.1641, + "y": -0.5665 + }, + { + "x": -0.5846, + "y": -0.5591 + }, + { + "x": -0.8796, + "y": 0.58 + }, + { + "x": -0.3873, + "y": 0.2345 + }, + { + "x": 1.2628, + "y": 0.6322 + }, + { + "x": 0.001, + "y": -2.096 + }, + { + "x": 1.9357, + "y": -1.2595 + }, + { + "x": 1.1051, + "y": 0.337 + }, + { + "x": -1.8311, + "y": 1.5493 + }, + { + "x": -0.6962, + "y": 0.3099 + }, + { + "x": 0.0252, + "y": 0.2962 + }, + { + "x": -0.7347, + "y": -1.1244 + }, + { + "x": -0.2697, + "y": 0.0501 + }, + { + "x": 0.7826, + "y": -0.779 + }, + { + "x": 1.0164, + "y": 0.4256 + }, + { + "x": -1.8954, + "y": -0.1808 + }, + { + "x": -0.524, + "y": 0.1836 + }, + { + "x": 1.3725, + "y": 0.86 + }, + { + "x": -0.9681, + "y": -0.2255 + }, + { + "x": -1.1374, + "y": 0.4136 + }, + { + "x": -1.2133, + "y": -1.1653 + }, + { + "x": 0.4294, + "y": -1.4477 + }, + { + "x": -0.4508, + "y": 3.2062 + }, + { + "x": 0.1712, + "y": -1.7463 + }, + { + "x": -1.6654, + "y": 0.0947 + }, + { + "x": -0.5734, + "y": -0.5775 + }, + { + "x": -0.9614, + "y": 0.2411 + }, + { + "x": 0.7098, + "y": -0.9794 + }, + { + "x": -0.8236, + "y": -1.7343 + }, + { + "x": 0.4603, + "y": 0.7881 + }, + { + "x": 1.3545, + "y": 0.7441 + }, + { + "x": 1.0416, + "y": 0.8407 + }, + { + "x": 1.0751, + "y": -0.1758 + }, + { + "x": -0.5117, + "y": -0.9134 + }, + { + "x": -0.2396, + "y": 0.6114 + }, + { + "x": 1.1269, + "y": -2.672 + }, + { + "x": -0.0539, + "y": -0.3627 + }, + { + "x": -0.6459, + "y": -0.1901 + }, + { + "x": 1.0721, + "y": 0.2373 + }, + { + "x": 1.4808, + "y": -0.4373 + }, + { + "x": -0.6734, + "y": 0.2898 + }, + { + "x": -0.4681, + "y": -1.8651 + }, + { + "x": 0.3359, + "y": 0.2408 + }, + { + "x": -0.4835, + "y": -0.6538 + }, + { + "x": 1.6546, + "y": -0.5515 + }, + { + "x": -0.0969, + "y": 1.927 + }, + { + "x": -0.8635, + "y": 0.1017 + }, + { + "x": -0.3091, + "y": 1.3844 + }, + { + "x": -0.6815, + "y": -0.9736 + }, + { + "x": -0.3805, + "y": -0.0075 + }, + { + "x": -1.0575, + "y": -0.3525 + }, + { + "x": 0.7107, + "y": -0.1807 + }, + { + "x": -0.2649, + "y": 1.961 + }, + { + "x": -0.8438, + "y": 1.9458 + }, + { + "x": -0.1915, + "y": -1.3461 + }, + { + "x": 0.4889, + "y": -1.0242 + }, + { + "x": -0.4812, + "y": 0.5272 + }, + { + "x": -2.0634, + "y": 0.0892 + }, + { + "x": -0.4213, + "y": -1.1561 + }, + { + "x": -0.2786, + "y": -0.6126 + }, + { + "x": 0.3546, + "y": 0.5053 + }, + { + "x": -0.6365, + "y": 1.7186 + }, + { + "x": -0.0173, + "y": 0.2086 + }, + { + "x": -1.3917, + "y": 0.9066 + }, + { + "x": 1.2196, + "y": -0.286 + }, + { + "x": -0.4778, + "y": -1.2451 + }, + { + "x": 0.8676, + "y": 2.4244 + }, + { + "x": 1.0796, + "y": -0.3292 + }, + { + "x": -0.1017, + "y": -2.2415 + }, + { + "x": -1.7143, + "y": -0.0211 + }, + { + "x": 0.6444, + "y": -1.4522 + }, + { + "x": -1.3234, + "y": -0.5039 + }, + { + "x": 0.2641, + "y": 0.2144 + }, + { + "x": 0.8471, + "y": 1.837 + }, + { + "x": -1.0411, + "y": -0.3424 + }, + { + "x": 1.1937, + "y": -0.5203 + }, + { + "x": 0.5813, + "y": 0.2407 + }, + { + "x": 0.998, + "y": -0.4947 + }, + { + "x": 0.9615, + "y": -1.8193 + }, + { + "x": -0.4859, + "y": -0.7827 + }, + { + "x": -0.2771, + "y": -0.854 + }, + { + "x": -1.4095, + "y": 0.4608 + }, + { + "x": 1.0381, + "y": 0.386 + }, + { + "x": 0.8027, + "y": -1.1741 + }, + { + "x": 0.4187, + "y": -0.8029 + }, + { + "x": -1.3287, + "y": -1.1387 + }, + { + "x": 0.1189, + "y": 0.8642 + }, + { + "x": -1.0484, + "y": 0.6049 + }, + { + "x": -1.2799, + "y": 1.1014 + }, + { + "x": -0.5275, + "y": -0.3531 + }, + { + "x": -0.3679, + "y": 0.8468 + }, + { + "x": -0.7347, + "y": -1.5154 + }, + { + "x": 1.7441, + "y": 1.351 + }, + { + "x": -1.46, + "y": -0.8075 + }, + { + "x": -1.0516, + "y": -1.7307 + }, + { + "x": -0.98, + "y": 2.0333 + }, + { + "x": 0.7891, + "y": 1.5778 + }, + { + "x": -0.1811, + "y": -1.0899 + }, + { + "x": -2.1769, + "y": -0.3864 + }, + { + "x": 0.7367, + "y": -1.0877 + }, + { + "x": 0.2939, + "y": 1.1957 + }, + { + "x": -0.4877, + "y": -0.6021 + }, + { + "x": -0.0677, + "y": -0.3735 + }, + { + "x": -1.11, + "y": -0.8569 + }, + { + "x": -0.4173, + "y": 1.3321 + }, + { + "x": 0.8592, + "y": 0.0694 + }, + { + "x": 0.0178, + "y": 0.6226 + }, + { + "x": -1.7347, + "y": -0.3647 + }, + { + "x": 0.6876, + "y": -0.8497 + }, + { + "x": -1.2345, + "y": -1.3151 + }, + { + "x": -0.9929, + "y": -0.6564 + }, + { + "x": 0.2569, + "y": -0.8897 + }, + { + "x": 0.7645, + "y": 0.6344 + }, + { + "x": 1.107, + "y": -1.1432 + }, + { + "x": 1.2143, + "y": 0.0349 + }, + { + "x": -1.7338, + "y": 1.2575 + }, + { + "x": 0.4892, + "y": 0.4607 + }, + { + "x": 0.296, + "y": -0.6267 + }, + { + "x": 0.145, + "y": 0.2382 + }, + { + "x": 0.512, + "y": -0.1607 + }, + { + "x": 0.1938, + "y": -0.1644 + }, + { + "x": -1.0389, + "y": -0.0403 + }, + { + "x": -0.5656, + "y": -0.1699 + }, + { + "x": -0.8709, + "y": 2.7212 + }, + { + "x": -1.5239, + "y": -1.3136 + }, + { + "x": 2.4426, + "y": 0.3175 + }, + { + "x": 0.553, + "y": -1.795 + }, + { + "x": 0.558, + "y": 0.3247 + }, + { + "x": -0.0384, + "y": 1.5966 + }, + { + "x": -0.2614, + "y": 1.0446 + }, + { + "x": -0.7362, + "y": 0.6224 + }, + { + "x": -0.1411, + "y": -1.1214 + }, + { + "x": 1.0066, + "y": -0.5415 + }, + { + "x": -0.3286, + "y": -0.3482 + }, + { + "x": -0.4582, + "y": -0.2944 + }, + { + "x": -0.116, + "y": -0.5766 + }, + { + "x": -0.5038, + "y": 0.8207 + }, + { + "x": -1.2088, + "y": 1.0035 + }, + { + "x": -0.701, + "y": -0.0458 + }, + { + "x": -0.4658, + "y": 0.1927 + }, + { + "x": -0.8321, + "y": -0.9992 + }, + { + "x": -0.3521, + "y": 0.8594 + }, + { + "x": -0.2733, + "y": 0.0052 + }, + { + "x": -2.8274, + "y": 2.1508 + }, + { + "x": 1.14, + "y": 0.72 + }, + { + "x": 1.2622, + "y": -0.6901 + }, + { + "x": -1.0407, + "y": -0.0537 + }, + { + "x": 0.5202, + "y": 0.8263 + }, + { + "x": -1.2631, + "y": 1.0016 + }, + { + "x": -2.412, + "y": -0.5986 + }, + { + "x": -0.362, + "y": 0.332 + }, + { + "x": 0.3489, + "y": -0.2657 + }, + { + "x": -1.345, + "y": -0.8233 + }, + { + "x": 0.8049, + "y": 1.1215 + }, + { + "x": 1.9326, + "y": 1.1325 + }, + { + "x": -0.6195, + "y": 0.2185 + }, + { + "x": -1.3197, + "y": 1.1455 + }, + { + "x": -0.7654, + "y": 0.6761 + }, + { + "x": 0.6258, + "y": 0.3275 + }, + { + "x": -0.0978, + "y": 1.0096 + }, + { + "x": -0.2254, + "y": 0.9802 + }, + { + "x": -0.638, + "y": -1.0637 + }, + { + "x": -1.943, + "y": -0.0147 + }, + { + "x": 0.2192, + "y": 0.8676 + }, + { + "x": 0.0159, + "y": -1.3238 + }, + { + "x": 0.3411, + "y": 1.3935 + }, + { + "x": 1.0837, + "y": -1.5106 + }, + { + "x": -2.3004, + "y": -1.4794 + }, + { + "x": -0.9747, + "y": -1.1324 + }, + { + "x": 0.1058, + "y": 0.0335 + }, + { + "x": -0.9425, + "y": -1.8496 + }, + { + "x": 0.1345, + "y": -1.4574 + }, + { + "x": -1.2195, + "y": 0.8447 + }, + { + "x": -0.2004, + "y": 2.0896 + }, + { + "x": -1.4654, + "y": 0.0891 + }, + { + "x": -0.3691, + "y": 0.3107 + }, + { + "x": 0.1312, + "y": 2.1846 + }, + { + "x": -0.0579, + "y": -0.9063 + }, + { + "x": -0.8397, + "y": -1.1195 + }, + { + "x": 0.2521, + "y": -0.389 + }, + { + "x": -1.4417, + "y": -0.3045 + }, + { + "x": 0.3798, + "y": -2.2505 + }, + { + "x": -0.3684, + "y": -0.1207 + }, + { + "x": 1.6397, + "y": 0.9203 + }, + { + "x": 1.0158, + "y": -0.7456 + }, + { + "x": 0.0372, + "y": 1.3276 + }, + { + "x": -1.2478, + "y": 0.8666 + }, + { + "x": 2.1335, + "y": -0.8427 + }, + { + "x": 0.5279, + "y": 0.6502 + }, + { + "x": 2.4859, + "y": -0.672 + }, + { + "x": -0.8759, + "y": -0.1695 + }, + { + "x": 0.5724, + "y": -2.0717 + }, + { + "x": 0.4823, + "y": 1.276 + }, + { + "x": 0.8642, + "y": 0.2067 + }, + { + "x": -1.2794, + "y": 0.2607 + }, + { + "x": 0.305, + "y": 0.6703 + }, + { + "x": -1.0565, + "y": 0.6535 + }, + { + "x": 0.5885, + "y": 0.149 + }, + { + "x": 0.683, + "y": 0.4467 + }, + { + "x": 0.4782, + "y": 2.6644 + }, + { + "x": -0.3715, + "y": -0.0351 + }, + { + "x": -0.4945, + "y": -2.2727 + }, + { + "x": 1.0551, + "y": -0.6658 + }, + { + "x": 1.235, + "y": 0.8678 + }, + { + "x": -0.7448, + "y": -0.938 + }, + { + "x": -0.9409, + "y": -0.3805 + }, + { + "x": 0.1466, + "y": -0.1297 + }, + { + "x": 2.7361, + "y": -0.0505 + }, + { + "x": 0.5566, + "y": 0.1446 + }, + { + "x": 0.4086, + "y": -0.1945 + }, + { + "x": -2.0046, + "y": -0.9676 + }, + { + "x": 0.2538, + "y": -0.2154 + }, + { + "x": 0.0054, + "y": -0.2842 + }, + { + "x": -0.5136, + "y": 0.9829 + }, + { + "x": 1.3386, + "y": -0.8463 + }, + { + "x": -1.1417, + "y": 0.215 + }, + { + "x": -0.0078, + "y": -0.6682 + }, + { + "x": -0.1273, + "y": 1.5731 + }, + { + "x": 1.1288, + "y": 0.0861 + }, + { + "x": 0.0624, + "y": -1.7653 + }, + { + "x": -0.3284, + "y": -0.0027 + }, + { + "x": -0.41, + "y": -0.7394 + }, + { + "x": -0.7994, + "y": -0.4863 + }, + { + "x": -0.9547, + "y": -0.1781 + }, + { + "x": 2.5729, + "y": 1.7364 + }, + { + "x": -0.0179, + "y": 0.2872 + }, + { + "x": 0.2968, + "y": -0.2172 + }, + { + "x": -1.1003, + "y": -1.7157 + }, + { + "x": -0.5636, + "y": 0.3528 + }, + { + "x": -0.4474, + "y": 1.0051 + }, + { + "x": 0.5282, + "y": 0.2225 + }, + { + "x": 0.8666, + "y": -0.0694 + }, + { + "x": -0.5149, + "y": -0.5803 + }, + { + "x": 1.1451, + "y": -0.4073 + }, + { + "x": -1.2763, + "y": 0.1776 + }, + { + "x": 2.2017, + "y": 1.2227 + }, + { + "x": -0.8639, + "y": 1.073 + }, + { + "x": 0.0115, + "y": -0.3013 + }, + { + "x": -0.9275, + "y": 1.835 + }, + { + "x": 0.4637, + "y": 0.5651 + }, + { + "x": 0.6614, + "y": 1.0628 + }, + { + "x": 1.4044, + "y": -1.3571 + }, + { + "x": -0.8067, + "y": -0.466 + }, + { + "x": -1.588, + "y": 0.8845 + }, + { + "x": -0.1962, + "y": -0.1872 + }, + { + "x": 0.9075, + "y": 0.5765 + }, + { + "x": -0.5605, + "y": 0.4784 + }, + { + "x": 1.1226, + "y": -1.3136 + }, + { + "x": 1.0756, + "y": 0.271 + }, + { + "x": 0.0737, + "y": -0.7489 + }, + { + "x": 0.6134, + "y": -1.9595 + }, + { + "x": -1.0405, + "y": 1.3195 + }, + { + "x": 0.2552, + "y": -0.308 + }, + { + "x": -0.9629, + "y": 0.288 + }, + { + "x": -0.2594, + "y": 0.5944 + }, + { + "x": -1.0799, + "y": -1.0897 + }, + { + "x": 1.229, + "y": -1.1074 + }, + { + "x": 0.8977, + "y": 0.8876 + }, + { + "x": -1.133, + "y": -1.014 + }, + { + "x": -0.1846, + "y": -0.2084 + }, + { + "x": -0.4722, + "y": 1.1966 + }, + { + "x": 0.2679, + "y": 2.3328 + }, + { + "x": -0.6337, + "y": -0.0686 + }, + { + "x": 1.0585, + "y": 0.7224 + }, + { + "x": -1.0192, + "y": 2.8521 + }, + { + "x": 0.0525, + "y": -0.3956 + }, + { + "x": -0.2404, + "y": -1.5144 + }, + { + "x": 0.2117, + "y": -0.5348 + }, + { + "x": 1.0119, + "y": -0.3385 + }, + { + "x": 0.4615, + "y": 0.7295 + }, + { + "x": 0.2589, + "y": 0.6979 + }, + { + "x": 1.0181, + "y": 0.2422 + }, + { + "x": -1.4625, + "y": -0.984 + }, + { + "x": 1.6018, + "y": 0.697 + }, + { + "x": 0.1116, + "y": 0.1376 + }, + { + "x": -0.1534, + "y": -0.6846 + }, + { + "x": 0.5489, + "y": -0.717 + }, + { + "x": 0.0506, + "y": -0.167 + }, + { + "x": 0.8419, + "y": -0.7471 + }, + { + "x": -0.2577, + "y": -2.0483 + }, + { + "x": -0.7759, + "y": -0.8005 + }, + { + "x": 0.7148, + "y": -1.5963 + }, + { + "x": -0.5763, + "y": -0.6001 + }, + { + "x": -0.2409, + "y": -0.5132 + }, + { + "x": 1.5157, + "y": 1.3964 + }, + { + "x": -0.2535, + "y": -0.214 + }, + { + "x": -0.9208, + "y": 0.0558 + }, + { + "x": 1.2014, + "y": -1.135 + }, + { + "x": 0.2584, + "y": -0.6387 + }, + { + "x": -1.1933, + "y": -0.3035 + }, + { + "x": 1.5779, + "y": -1.2398 + }, + { + "x": -1.0728, + "y": 0.6405 + }, + { + "x": -0.3502, + "y": -1.3961 + }, + { + "x": 0.6576, + "y": 1.9291 + }, + { + "x": -1.2188, + "y": 0.0089 + }, + { + "x": 0.3681, + "y": 0.9108 + }, + { + "x": -0.8839, + "y": 0.2328 + }, + { + "x": -1.4239, + "y": -0.363 + }, + { + "x": -0.9281, + "y": 0.1307 + }, + { + "x": 0.2467, + "y": -2.473 +