Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scatterplot #12

Merged
merged 1 commit into from
Jun 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/unicode_plot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
require 'unicode_plot/braille_canvas'

require 'unicode_plot/plot'
require 'unicode_plot/grid_plot'

require 'unicode_plot/barplot'
require 'unicode_plot/boxplot'
require 'unicode_plot/lineplot'
require 'unicode_plot/histogram'
require 'unicode_plot/scatterplot'
101 changes: 101 additions & 0 deletions lib/unicode_plot/grid_plot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
module UnicodePlot
class GridPlot < Plot
MIN_WIDTH = 5
DEFAULT_WIDTH = 40
MIN_HEIGHT = 2
DEFAULT_HEIGHT = 15

def initialize(x, y, canvas,
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
xlim: [0, 0],
ylim: [0, 0],
grid: true,
**kw)
if x.length != y.length
raise ArgumentError, "x and y must be the same length"
end
unless x.length > 0
raise ArgumentError, "x and y must not be empty"
end
unless xlim.length == 2 && ylim.length == 2
raise ArgumentError, "xlim and ylim must be 2-length arrays"
end
width = [width, MIN_WIDTH].max
height = [height, MIN_HEIGHT].max
min_x, max_x = Utils.extend_limits(x, xlim)
min_y, max_y = Utils.extend_limits(y, ylim)
origin_x = min_x
origin_y = min_y
plot_width = max_x - origin_x
plot_height = max_y - origin_y
@canvas = Canvas.create(canvas, width, height,
origin_x: origin_x,
origin_y: origin_y,
plot_width: plot_width,
plot_height: plot_height)
super(**kw)

min_x_str = (Utils.roundable?(min_x) ? min_x.round : min_x).to_s
max_x_str = (Utils.roundable?(max_x) ? max_x.round : max_x).to_s
min_y_str = (Utils.roundable?(min_y) ? min_y.round : min_y).to_s
max_y_str = (Utils.roundable?(max_y) ? max_y.round : max_y).to_s

annotate_row!(:l, 0, max_y_str, color: :light_black)
annotate_row!(:l, height-1, min_y_str, color: :light_black)
annotate!(:bl, min_x_str, color: :light_black)
annotate!(:br, max_x_str, color: :light_black)

if grid
if min_y < 0 && 0 < max_y
step = plot_width.fdiv(width * @canvas.x_pixel_per_char - 1)
min_x.step(max_x, by: step) do |i|
@canvas.point!(i, 0, :normal)
end
end
if min_x < 0 && 0 < max_x
step = plot_height.fdiv(height * @canvas.y_pixel_per_char - 1)
min_y.step(max_y, by: step) do |i|
@canvas.point!(0, i, :normal)
end
end
end
end

def origin_x
@canvas.origin_x
end

def origin_y
@canvas.origin_y
end

def plot_width
@canvas.plot_width
end

def plot_height
@canvas.plot_height
end

def n_rows
@canvas.height
end

def n_columns
@canvas.width
end

def points!(x, y, color)
@canvas.points!(x, y, color)
end

def lines!(x, y, color)
@canvas.lines!(x, y, color)
end

def print_row(out, row_index)
@canvas.print_row(out, row_index)
end
end
end
106 changes: 1 addition & 105 deletions lib/unicode_plot/lineplot.rb
Original file line number Diff line number Diff line change
@@ -1,111 +1,7 @@
require 'date'

module UnicodePlot
class GridCanvas < Plot
MIN_WIDTH = 5
DEFAULT_WIDTH = 40
MIN_HEIGHT = 2
DEFAULT_HEIGHT = 15

def initialize(x, y, canvas,
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
xlim: [0, 0],
ylim: [0, 0],
grid: true,
**kw)
unless xlim.length == 2 && ylim.length == 2
raise ArgumentError, "xlim and ylim must be 2-length arrays"
end
width = [width, MIN_WIDTH].max
height = [height, MIN_HEIGHT].max
min_x, max_x = Utils.extend_limits(x, xlim)
min_y, max_y = Utils.extend_limits(y, ylim)
origin_x = min_x
origin_y = min_y
plot_width = max_x - origin_x
plot_height = max_y - origin_y
@canvas = Canvas.create(canvas, width, height,
origin_x: origin_x,
origin_y: origin_y,
plot_width: plot_width,
plot_height: plot_height)
super(**kw)

min_x_str = (Utils.roundable?(min_x) ? min_x.round : min_x).to_s
max_x_str = (Utils.roundable?(max_x) ? max_x.round : max_x).to_s
min_y_str = (Utils.roundable?(min_y) ? min_y.round : min_y).to_s
max_y_str = (Utils.roundable?(max_y) ? max_y.round : max_y).to_s

annotate_row!(:l, 0, max_y_str, color: :light_black)
annotate_row!(:l, height-1, min_y_str, color: :light_black)
annotate!(:bl, min_x_str, color: :light_black)
annotate!(:br, max_x_str, color: :light_black)

if grid
if min_y < 0 && 0 < max_y
step = plot_width.fdiv(width * @canvas.x_pixel_per_char - 1)
min_x.step(max_x, by: step) do |i|
@canvas.point!(i, 0, :normal)
end
end
if min_x < 0 && 0 < max_x
step = plot_height.fdiv(height * @canvas.y_pixel_per_char - 1)
min_y.step(max_y, by: step) do |i|
@canvas.point!(0, i, :normal)
end
end
end
end

def origin_x
@canvas.origin_x
end

def origin_y
@canvas.origin_y
end

def plot_width
@canvas.plot_width
end

def plot_height
@canvas.plot_height
end

def n_rows
@canvas.height
end

def n_columns
@canvas.width
end

def points!(x, y, color)
@canvas.points!(x, y, color)
end

def lines!(x, y, color)
@canvas.lines!(x, y, color)
end

def print_row(out, row_index)
@canvas.print_row(out, row_index)
end
end

class Lineplot < GridCanvas
def initialize(x, y, canvas,
**kw)
if x.length != y.length
raise ArgumentError, "x and y must be the same length"
end
unless x.length > 0
raise ArgumentError, "x and y must not be empty"
end
super(x, y, canvas, **kw)
end
class Lineplot < GridPlot
end

module_function def lineplot(*args,
Expand Down
2 changes: 1 addition & 1 deletion lib/unicode_plot/renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def init_render
@border_padding = " " * @plot_offset

# compute position of ylabel
@y_lab_row = (plot.n_rows / 2.0).round
@y_lab_row = (plot.n_rows / 2.0).round - 1
end

def print_title(padding, title, p_width: 0, color: :normal)
Expand Down
49 changes: 49 additions & 0 deletions lib/unicode_plot/scatterplot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
module UnicodePlot
class Scatterplot < GridPlot
end

module_function def scatterplot(*args,
canvas: :braille,
color: :auto,
name: "",
**kw)
case args.length
when 1
# y only
y = Array(args[0])
x = Array(1 .. y.length)
when 2
# x and y
x = Array(args[0])
y = Array(args[1])
else
raise ArgumentError, "worng number of arguments"
end

plot = Scatterplot.new(x, y, canvas, **kw)
scatterplot!(plot, x, y, color: color, name: name)
end

module_function def scatterplot!(plot,
*args,
color: :auto,
name: "")
case args.length
when 1
# y only
y = Array(args[0])
x = Array(1 .. y.length)
when 2
# x and y
x = Array(args[0])
y = Array(args[1])
else
raise ArgumentError, "worng number of arguments"
end

color = color == :auto ? plot.next_color : color
plot.annotate!(:r, name.to_s, color: color) unless name.nil? || name == ""
plot.points!(x, y, color)
plot
end
end
5 changes: 4 additions & 1 deletion lib/unicode_plot/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ def ceil_neg_log10(x)
end
end

INT64_MIN = -9223372036854775808
INT64_MAX = 9223372036854775807

def roundable?(x)
x.to_i == x
x.to_i == x && INT64_MIN <= x && x < INT64_MAX
end
end
end
18 changes: 18 additions & 0 deletions test/fixtures/scatterplot/blue.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
 ┌────────────────────────────────────────┐
2 │⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈│ points1
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
-5 │⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀│
 └────────────────────────────────────────┘
 -1  3
9 changes: 9 additions & 0 deletions test/fixtures/scatterplot/canvassize.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
 Scatter
 ┌──────────┐
2 │' :      '│
 │'':'''''''│
 │  :       │
 │  :       │
-5 │. :      .│
 └──────────┘
 -1  3
18 changes: 18 additions & 0 deletions test/fixtures/scatterplot/default.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
 ┌────────────────────────────────────────┐
2 │⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
-5 │⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀│
 └────────────────────────────────────────┘
 -1  3
Loading