unicode_plot 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,111 +1,7 @@
1
1
  require 'date'
2
2
 
3
3
  module UnicodePlot
4
- class GridCanvas < Plot
5
- MIN_WIDTH = 5
6
- DEFAULT_WIDTH = 40
7
- MIN_HEIGHT = 2
8
- DEFAULT_HEIGHT = 15
9
-
10
- def initialize(x, y, canvas,
11
- width: DEFAULT_WIDTH,
12
- height: DEFAULT_HEIGHT,
13
- xlim: [0, 0],
14
- ylim: [0, 0],
15
- grid: true,
16
- **kw)
17
- unless xlim.length == 2 && ylim.length == 2
18
- raise ArgumentError, "xlim and ylim must be 2-length arrays"
19
- end
20
- width = [width, MIN_WIDTH].max
21
- height = [height, MIN_HEIGHT].max
22
- min_x, max_x = Utils.extend_limits(x, xlim)
23
- min_y, max_y = Utils.extend_limits(y, ylim)
24
- origin_x = min_x
25
- origin_y = min_y
26
- plot_width = max_x - origin_x
27
- plot_height = max_y - origin_y
28
- @canvas = Canvas.create(canvas, width, height,
29
- origin_x: origin_x,
30
- origin_y: origin_y,
31
- plot_width: plot_width,
32
- plot_height: plot_height)
33
- super(**kw)
34
-
35
- min_x_str = (Utils.roundable?(min_x) ? min_x.round : min_x).to_s
36
- max_x_str = (Utils.roundable?(max_x) ? max_x.round : max_x).to_s
37
- min_y_str = (Utils.roundable?(min_y) ? min_y.round : min_y).to_s
38
- max_y_str = (Utils.roundable?(max_y) ? max_y.round : max_y).to_s
39
-
40
- annotate_row!(:l, 0, max_y_str, color: :light_black)
41
- annotate_row!(:l, height-1, min_y_str, color: :light_black)
42
- annotate!(:bl, min_x_str, color: :light_black)
43
- annotate!(:br, max_x_str, color: :light_black)
44
-
45
- if grid
46
- if min_y < 0 && 0 < max_y
47
- step = plot_width.fdiv(width * @canvas.x_pixel_per_char - 1)
48
- min_x.step(max_x, by: step) do |i|
49
- @canvas.point!(i, 0, :normal)
50
- end
51
- end
52
- if min_x < 0 && 0 < max_x
53
- step = plot_height.fdiv(height * @canvas.y_pixel_per_char - 1)
54
- min_y.step(max_y, by: step) do |i|
55
- @canvas.point!(0, i, :normal)
56
- end
57
- end
58
- end
59
- end
60
-
61
- def origin_x
62
- @canvas.origin_x
63
- end
64
-
65
- def origin_y
66
- @canvas.origin_y
67
- end
68
-
69
- def plot_width
70
- @canvas.plot_width
71
- end
72
-
73
- def plot_height
74
- @canvas.plot_height
75
- end
76
-
77
- def n_rows
78
- @canvas.height
79
- end
80
-
81
- def n_columns
82
- @canvas.width
83
- end
84
-
85
- def points!(x, y, color)
86
- @canvas.points!(x, y, color)
87
- end
88
-
89
- def lines!(x, y, color)
90
- @canvas.lines!(x, y, color)
91
- end
92
-
93
- def print_row(out, row_index)
94
- @canvas.print_row(out, row_index)
95
- end
96
- end
97
-
98
- class Lineplot < GridCanvas
99
- def initialize(x, y, canvas,
100
- **kw)
101
- if x.length != y.length
102
- raise ArgumentError, "x and y must be the same length"
103
- end
104
- unless x.length > 0
105
- raise ArgumentError, "x and y must not be empty"
106
- end
107
- super(x, y, canvas, **kw)
108
- end
4
+ class Lineplot < GridPlot
109
5
  end
110
6
 
111
7
  module_function def lineplot(*args,
@@ -0,0 +1,46 @@
1
+ module UnicodePlot
2
+ class LookupCanvas < Canvas
3
+ def initialize(width, height, x_pixel_per_char, y_pixel_per_char, fill_char=0, **kw)
4
+ super(width, height,
5
+ width * x_pixel_per_char,
6
+ height * y_pixel_per_char,
7
+ fill_char,
8
+ x_pixel_per_char: x_pixel_per_char,
9
+ y_pixel_per_char: y_pixel_per_char,
10
+ **kw)
11
+ end
12
+
13
+ def pixel!(pixel_x, pixel_y, color)
14
+ unless 0 <= pixel_x && pixel_x <= pixel_width &&
15
+ 0 <= pixel_y && pixel_y <= pixel_height
16
+ return color
17
+ end
18
+ pixel_x -= 1 unless pixel_x < pixel_width
19
+ pixel_y -= 1 unless pixel_y < pixel_height
20
+
21
+ tx = pixel_x.fdiv(pixel_width) * width
22
+ char_x = tx.floor + 1
23
+ char_x_off = pixel_x % x_pixel_per_char + 1
24
+ char_x += 1 if char_x < tx.round + 1 && char_x_off == 1
25
+
26
+ char_y = (pixel_y.fdiv(pixel_height) * height).floor + 1
27
+ char_y_off = pixel_y % y_pixel_per_char + 1
28
+
29
+ index = index_at(char_x - 1, char_y - 1)
30
+ if index
31
+ @grid[index] |= lookup_encode(char_x_off - 1, char_y_off - 1)
32
+ @colors[index] |= COLOR_ENCODE[color]
33
+ end
34
+ end
35
+
36
+ def print_row(out, row_index)
37
+ unless 0 <= row_index && row_index < height
38
+ raise ArgumentError, "row_index out of bounds"
39
+ end
40
+ y = row_index
41
+ (0 ... width).each do |x|
42
+ print_color(out, color_at(x, y), lookup_decode(char_at(x, y)))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -124,8 +124,8 @@ module UnicodePlot
124
124
  left_col = plot.colors_left.fetch(row, :light_black)
125
125
  right_str = plot.labels_right.fetch(row, "")
126
126
  right_col = plot.colors_right.fetch(row, :light_black)
127
- left_len = left_str.length
128
- right_len = right_str.length
127
+ left_len = nocolor_string(left_str).length
128
+ right_len = nocolor_string(right_str).length
129
129
 
130
130
  unless color?(out)
131
131
  left_str = nocolor_string(left_str)
@@ -201,10 +201,10 @@ module UnicodePlot
201
201
 
202
202
  # get length of largest strings to the left and right
203
203
  @max_len_l = plot.show_labels? && !plot.labels_left.empty? ?
204
- plot.labels_left.each_value.map {|l| l.to_s.length }.max :
204
+ plot.labels_left.each_value.map {|l| nocolor_string(l).length }.max :
205
205
  0
206
206
  @max_len_r = plot.show_labels? && !plot.labels_right.empty? ?
207
- plot.labels_right.each_value.map {|l| l.to_s.length }.max :
207
+ plot.labels_right.each_value.map {|l| nocolor_string(l).length }.max :
208
208
  0
209
209
  if plot.show_labels? && plot.ylabel_given?
210
210
  @max_len_l += plot.ylabel_length + 1
@@ -220,7 +220,7 @@ module UnicodePlot
220
220
  @border_padding = " " * @plot_offset
221
221
 
222
222
  # compute position of ylabel
223
- @y_lab_row = (plot.n_rows / 2.0).round
223
+ @y_lab_row = (plot.n_rows / 2.0).round - 1
224
224
  end
225
225
 
226
226
  def print_title(padding, title, p_width: 0, color: :normal)
@@ -0,0 +1,49 @@
1
+ module UnicodePlot
2
+ class Scatterplot < GridPlot
3
+ end
4
+
5
+ module_function def scatterplot(*args,
6
+ canvas: :braille,
7
+ color: :auto,
8
+ name: "",
9
+ **kw)
10
+ case args.length
11
+ when 1
12
+ # y only
13
+ y = Array(args[0])
14
+ x = Array(1 .. y.length)
15
+ when 2
16
+ # x and y
17
+ x = Array(args[0])
18
+ y = Array(args[1])
19
+ else
20
+ raise ArgumentError, "worng number of arguments"
21
+ end
22
+
23
+ plot = Scatterplot.new(x, y, canvas, **kw)
24
+ scatterplot!(plot, x, y, color: color, name: name)
25
+ end
26
+
27
+ module_function def scatterplot!(plot,
28
+ *args,
29
+ color: :auto,
30
+ name: "")
31
+ case args.length
32
+ when 1
33
+ # y only
34
+ y = Array(args[0])
35
+ x = Array(1 .. y.length)
36
+ when 2
37
+ # x and y
38
+ x = Array(args[0])
39
+ y = Array(args[1])
40
+ else
41
+ raise ArgumentError, "worng number of arguments"
42
+ end
43
+
44
+ color = color == :auto ? plot.next_color : color
45
+ plot.annotate!(:r, name.to_s, color: color) unless name.nil? || name == ""
46
+ plot.points!(x, y, color)
47
+ plot
48
+ end
49
+ end
@@ -26,6 +26,16 @@ module UnicodePlot
26
26
  [xmin.to_f, xmax.to_f]
27
27
  end
28
28
 
29
+ def float_round_log10(x, m)
30
+ if x == 0
31
+ 0.0
32
+ elsif x > 0
33
+ x.round(ceil_neg_log10(m) + 1).to_f
34
+ else
35
+ -(-x).round(ceil_neg_log10(m) + 1).to_f
36
+ end
37
+ end
38
+
29
39
  def round_up_subtick(x, m)
30
40
  if x == 0
31
41
  0.0
@@ -54,8 +64,11 @@ module UnicodePlot
54
64
  end
55
65
  end
56
66
 
67
+ INT64_MIN = -9223372036854775808
68
+ INT64_MAX = 9223372036854775807
69
+
57
70
  def roundable?(x)
58
- x.to_i == x
71
+ x.to_i == x && INT64_MIN <= x && x < INT64_MAX
59
72
  end
60
73
  end
61
74
  end
@@ -1,5 +1,5 @@
1
1
  module UnicodePlot
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
 
4
4
  module Version
5
5
  numbers, TAG = VERSION.split("-", 2)
data/lib/unicode_plot.rb CHANGED
@@ -6,11 +6,18 @@ require 'unicode_plot/value_transformer'
6
6
  require 'unicode_plot/renderer'
7
7
 
8
8
  require 'unicode_plot/canvas'
9
- require 'unicode_plot/ascii_canvas'
10
9
  require 'unicode_plot/braille_canvas'
10
+ require 'unicode_plot/density_canvas'
11
+ require 'unicode_plot/lookup_canvas'
12
+ require 'unicode_plot/ascii_canvas'
13
+ require 'unicode_plot/dot_canvas'
11
14
 
12
15
  require 'unicode_plot/plot'
16
+ require 'unicode_plot/grid_plot'
13
17
 
14
18
  require 'unicode_plot/barplot'
15
19
  require 'unicode_plot/boxplot'
20
+ require 'unicode_plot/densityplot'
16
21
  require 'unicode_plot/lineplot'
22
+ require 'unicode_plot/histogram'
23
+ require 'unicode_plot/scatterplot'
data/test/test-barplot.rb CHANGED
@@ -3,30 +3,46 @@ class BarplotTest < Test::Unit::TestCase
3
3
  include Helper::WithTerm
4
4
 
5
5
  sub_test_case("UnicodePlot.barplot") do
6
- sub_test_case("print to tty") do
7
- test("the output is colored") do
8
- data = { bar: 23, foo: 37 }
9
- plot = UnicodePlot.barplot(data: data)
10
- _, output = with_term do
11
- plot.render($stdout)
12
- end
13
- assert_equal(fixture_path("barplot/default.txt").read,
14
- output)
6
+ test("errors") do
7
+ assert_raise(ArgumentError) do
8
+ UnicodePlot.barplot([:a], [-1, 2])
9
+ end
10
+ assert_raise(ArgumentError) do
11
+ UnicodePlot.barplot([:a, :b], [-1, 2])
15
12
  end
16
13
  end
17
14
 
18
- sub_test_case("print to non-tty IO") do
19
- test("the output is not colored") do
20
- data = { bar: 23, foo: 37 }
21
- plot = UnicodePlot.barplot(data: data)
22
- output = StringIO.open do |sio|
23
- sio.print(plot)
24
- sio.close
25
- sio.string
26
- end
27
- assert_equal(fixture_path("barplot/nocolor.txt").read,
28
- output)
15
+ test("colored") do
16
+ data = { bar: 23, foo: 37 }
17
+ plot = UnicodePlot.barplot(data: data)
18
+ _, output = with_term { plot.render($stdout) }
19
+ assert_equal(fixture_path("barplot/default.txt").read,
20
+ output)
21
+
22
+ plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
23
+ _, output = with_term { plot.render($stdout) }
24
+ assert_equal(fixture_path("barplot/default.txt").read,
25
+ output)
26
+ end
27
+
28
+ test("not colored") do
29
+ data = { bar: 23, foo: 37 }
30
+ plot = UnicodePlot.barplot(data: data)
31
+ output = StringIO.open do |sio|
32
+ sio.print(plot)
33
+ sio.close
34
+ sio.string
29
35
  end
36
+ assert_equal(fixture_path("barplot/nocolor.txt").read,
37
+ output)
38
+ end
39
+
40
+ test("mixed") do
41
+ data = { bar: 23.0, 2.1 => 10, foo: 37.0 }
42
+ plot = UnicodePlot.barplot(data: data)
43
+ _, output = with_term { plot.render($stdout) }
44
+ assert_equal(fixture_path("barplot/default_mixed.txt").read,
45
+ output)
30
46
  end
31
47
 
32
48
  sub_test_case("xscale: :log10") do
@@ -55,5 +71,120 @@ class BarplotTest < Test::Unit::TestCase
55
71
  output)
56
72
  end
57
73
  end
74
+
75
+ sub_test_case("with parameters") do
76
+ test("parameters1") do
77
+ plot = UnicodePlot.barplot(
78
+ ["Paris", "New York", "Moskau", "Madrid"],
79
+ [2.244, 8.406, 11.92, 3.165],
80
+ title: "Relative sizes of cities",
81
+ xlabel: "population [in mil]",
82
+ color: :blue,
83
+ margin: 7,
84
+ padding: 3
85
+ )
86
+ _, output = with_term { plot.render($stdout) }
87
+ assert_equal(fixture_path("barplot/parameters1.txt").read,
88
+ output)
89
+ end
90
+
91
+ test("parameters1_nolabels") do
92
+ plot = UnicodePlot.barplot(
93
+ ["Paris", "New York", "Moskau", "Madrid"],
94
+ [2.244, 8.406, 11.92, 3.165],
95
+ title: "Relative sizes of cities",
96
+ xlabel: "population [in mil]",
97
+ color: :blue,
98
+ margin: 7,
99
+ padding: 3,
100
+ labels: false
101
+ )
102
+ _, output = with_term { plot.render($stdout) }
103
+ assert_equal(fixture_path("barplot/parameters1_nolabels.txt").read,
104
+ output)
105
+ end
106
+
107
+ test("parameters2") do
108
+ plot = UnicodePlot.barplot(
109
+ ["Paris", "New York", "Moskau", "Madrid"],
110
+ [2.244, 8.406, 11.92, 3.165],
111
+ title: "Relative sizes of cities",
112
+ xlabel: "population [in mil]",
113
+ color: :yellow,
114
+ border: :solid,
115
+ symbol: "=",
116
+ width: 60
117
+ )
118
+ _, output = with_term { plot.render($stdout) }
119
+ assert_equal(fixture_path("barplot/parameters2.txt").read,
120
+ output)
121
+ end
122
+ end
123
+
124
+ test("ranges") do
125
+ plot = UnicodePlot.barplot(2..6, 11..15)
126
+ _, output = with_term { plot.render($stdout) }
127
+ assert_equal(fixture_path("barplot/ranges.txt").read,
128
+ output)
129
+ end
130
+
131
+ test("all zeros") do
132
+ plot = UnicodePlot.barplot([5, 4, 3, 2, 1], [0, 0, 0, 0, 0])
133
+ _, output = with_term { plot.render($stdout) }
134
+ assert_equal(fixture_path("barplot/edgecase_zeros.txt").read,
135
+ output)
136
+ end
137
+
138
+ test("one large") do
139
+ plot = UnicodePlot.barplot([:a, :b, :c, :d], [1, 1, 1, 1000000])
140
+ _, output = with_term { plot.render($stdout) }
141
+ assert_equal(fixture_path("barplot/edgecase_onelarge.txt").read,
142
+ output)
143
+ end
144
+ end
145
+
146
+ sub_test_case("UnicodePlot.barplot!") do
147
+ test("errors") do
148
+ plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
149
+ assert_raise(ArgumentError) do
150
+ UnicodePlot.barplot!(plot, ["zoom"], [90, 80])
151
+ end
152
+ assert_raise(ArgumentError) do
153
+ UnicodePlot.barplot!(plot, ["zoom", "boom"], [90])
154
+ end
155
+ UnicodePlot.barplot!(plot, "zoom", 90.1)
156
+ end
157
+
158
+ test("return value") do
159
+ plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
160
+ assert_same(plot,
161
+ UnicodePlot.barplot!(plot, ["zoom"], [90]))
162
+ _, output = with_term { plot.render($stdout) }
163
+ assert_equal(fixture_path("barplot/default2.txt").read,
164
+ output)
165
+
166
+ plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
167
+ assert_same(plot,
168
+ UnicodePlot.barplot!(plot, "zoom", 90))
169
+ _, output = with_term { plot.render($stdout) }
170
+ assert_equal(fixture_path("barplot/default2.txt").read,
171
+ output)
172
+
173
+ plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
174
+ assert_same(plot,
175
+ UnicodePlot.barplot!(plot, data: { zoom: 90 }))
176
+ _, output = with_term { plot.render($stdout) }
177
+ assert_equal(fixture_path("barplot/default2.txt").read,
178
+ output)
179
+ end
180
+
181
+ test("ranges") do
182
+ plot = UnicodePlot.barplot(2..6, 11..15)
183
+ assert_same(plot,
184
+ UnicodePlot.barplot!(plot, 9..10, 20..21))
185
+ _, output = with_term { plot.render($stdout) }
186
+ assert_equal(fixture_path("barplot/ranges2.txt").read,
187
+ output)
188
+ end
58
189
  end
59
190
  end