unicode_plot 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d3752009f07860fb96eba974ed4322ab975bd5547832e7747c95544918ad96c6
4
- data.tar.gz: 1e86d05df7dd39473144ed8b26ec00b32980f6d61a684f978e7a6c914d42b71f
3
+ metadata.gz: 803cdd758712ba3337cf57baeaa0c167485201952cb9becae048375e8a157f58
4
+ data.tar.gz: b001770582a9a9ef06af3e5bb74494b0ec8422d77fd5c6955d7a4420b947f7b0
5
5
  SHA512:
6
- metadata.gz: 68c546819e475ddb1a5e24114b8a8fae8bb0809e723870abfe8cb3583ce838fedb7601b051085f38898ac039e58addee618651f446ed9a22f9d627f64775e2cc
7
- data.tar.gz: 86da79ec98b4d5c461bb0c3c27632fb3fda90e457066dcc5efa987d402f1e93c7a55e1330551eef270c1bb71f68a9ba3c9c24b578c7f6f8ad617ac7167d65433
6
+ metadata.gz: 4e148f810ba8a72332f298a27cf9247d27b65cd5813669d443db00252140426bdd3813d11e07526da2975db4bf6a5627aa5bd7b7a878fdf53cff3286ee662ca5
7
+ data.tar.gz: 3beaef1efb87df4f59318c03970b233a55838b0623e263093e5466e54dd8a83644546e957186b63eb41e08bf55f790744a447c02b3bab61cf0bbef0253c7414d
data/Gemfile CHANGED
@@ -1,3 +1,6 @@
1
1
  source "https://rubygems.org/"
2
2
 
3
3
  gemspec
4
+
5
+ # Temporary use this for module_function decorator support
6
+ gem "yard", github: "mrkn/yard", branch: "module_function_decorator"
data/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  UnicodePlot provides the feature to make charts with Unicode characters.
4
4
 
5
+ ## Documentation
6
+
7
+ https://red-data-tools.github.io/unicode_plot.rb/
8
+
5
9
  ## Install
6
10
 
7
11
  ```console
@@ -18,8 +22,7 @@ y_sin = x.map {|xi| Math.sin(xi) }
18
22
  y_cos = x.map {|xi| Math.cos(xi) }
19
23
  plot = UnicodePlot.lineplot(x, y_sin, name: "sin(x)", width: 40, height: 10)
20
24
  UnicodePlot.lineplot!(plot, x, y_cos, name: "cos(x)")
21
- plot.render($stdout)
22
- puts
25
+ plot.render
23
26
  ```
24
27
 
25
28
  You can get the results below by running the above script:
@@ -31,8 +34,7 @@ You can get the results below by running the above script:
31
34
  ### barplot
32
35
 
33
36
  ```ruby
34
- plot = UnicodePlot.barplot(data: {'foo': 20, 'bar': 50}, title: "Bar")
35
- plot.render($stdout)
37
+ UnicodePlot.barplot(data: {'foo': 20, 'bar': 50}, title: "Bar").render
36
38
  ```
37
39
 
38
40
  <img src="img/barplot.png" width="50%" />
@@ -40,8 +42,7 @@ plot.render($stdout)
40
42
  ### boxplot
41
43
 
42
44
  ```ruby
43
- plot = UnicodePlot.boxplot(data: {foo: [1, 3, 5], bar: [3, 5, 7]}, title: "Box")
44
- plot.render($stdout)
45
+ UnicodePlot.boxplot(data: {foo: [1, 3, 5], bar: [3, 5, 7]}, title: "Box").render
45
46
  ```
46
47
 
47
48
  <img src="img/boxplot.png" width="50%" />
@@ -51,8 +52,7 @@ plot.render($stdout)
51
52
  ```ruby
52
53
  x = Array.new(500) { 20*rand - 10 } + Array.new(500) { 6*rand - 3 }
53
54
  y = Array.new(1000) { 30*rand - 10 }
54
- plot = UnicodePlot.densityplot(x, y, title: "Density")
55
- plot.render($stdout)
55
+ UnicodePlot.densityplot(x, y, title: "Density").render
56
56
  ```
57
57
 
58
58
  <img src="img/densityplot.png" width="50%" />
@@ -61,8 +61,7 @@ plot.render($stdout)
61
61
 
62
62
  ```ruby
63
63
  x = Array.new(100) { rand(10) } + Array.new(100) { rand(30) + 10 }
64
- plot = UnicodePlot.histogram(x, title: "Histogram")
65
- plot.render($stdout)
64
+ UnicodePlot.histogram(x, title: "Histogram").render
66
65
  ```
67
66
 
68
67
  <img src="img/histogram.png" width="50%" />
@@ -76,8 +75,7 @@ See [Usage](#usage) section above.
76
75
  ```ruby
77
76
  x = Array.new(50) { rand(20) - 10 }
78
77
  y = x.map {|xx| xx*rand(30) - 10 }
79
- plot = UnicodePlot.scatterplot(x, y, title: "Scatter")
80
- plot.render($stdout)
78
+ UnicodePlot.scatterplot(x, y, title: "Scatter").render
81
79
  ```
82
80
 
83
81
  <img src="img/scatterplot.png" width="50%" />
data/Rakefile CHANGED
@@ -1,9 +1,9 @@
1
1
  require "bundler/gem_helper"
2
+ require "yard"
2
3
 
3
4
  base_dir = File.expand_path("..", __FILE__)
4
5
  helper = Bundler::GemHelper.new(base_dir)
5
6
  helper.install
6
- spec = helper.gemspec
7
7
 
8
8
  desc "Run test"
9
9
  task :test do
@@ -11,3 +11,6 @@ task :test do
11
11
  end
12
12
 
13
13
  task default: :test
14
+
15
+ YARD::Rake::YardocTask.new do |task|
16
+ end
data/lib/unicode_plot.rb CHANGED
@@ -2,17 +2,13 @@ require 'stringio'
2
2
 
3
3
  require 'unicode_plot/version'
4
4
 
5
+ require 'unicode_plot/io_context'
5
6
  require 'unicode_plot/utils'
6
7
  require 'unicode_plot/styled_printer'
7
8
  require 'unicode_plot/value_transformer'
8
9
  require 'unicode_plot/renderer'
9
10
 
10
11
  require 'unicode_plot/canvas'
11
- require 'unicode_plot/braille_canvas'
12
- require 'unicode_plot/density_canvas'
13
- require 'unicode_plot/lookup_canvas'
14
- require 'unicode_plot/ascii_canvas'
15
- require 'unicode_plot/dot_canvas'
16
12
 
17
13
  require 'unicode_plot/plot'
18
14
  require 'unicode_plot/grid_plot'
@@ -23,3 +19,5 @@ require 'unicode_plot/densityplot'
23
19
  require 'unicode_plot/lineplot'
24
20
  require 'unicode_plot/histogram'
25
21
  require 'unicode_plot/scatterplot'
22
+ require 'unicode_plot/stairs'
23
+ require 'unicode_plot/stemplot'
@@ -69,6 +69,57 @@ module UnicodePlot
69
69
  end
70
70
  end
71
71
 
72
+ # @overload barplot(text, heights, xscale: nil, title: nil, xlabel: nil, ylabel: nil, labels: true, border: :barplot, margin: Plot::DEFAULT_MARGIN, padding: Plot::DEFAULT_PADDING, color: Barplot::DEFAULT_COLOR, width: Plot::DEFAULT_WIDTH, symbol: Barplot::DEFAULT_SYMBOL)
73
+ #
74
+ # Draws a horizontal barplot.
75
+ #
76
+ # @param text [Array<String>] The lables / captions of the bars.
77
+ # @param heights [Array<Numeric>] The values / heights of the bars.
78
+ # @param xscale [nil,:log,:ln,:log10,:lg,:log2,:lb,callable]
79
+ # A function name symbol or callable object to transform the bar
80
+ # length before plotting. This effectively scales the x-axis
81
+ # without influencing the captions of the individual bars.
82
+ # e.g. use `xscale: :log10` for logscale.
83
+ # @param title
84
+ # @param xlabel
85
+ # @param ylabel
86
+ # @param labels
87
+ # @param border
88
+ # @param margin
89
+ # @param padding
90
+ # @param color
91
+ # @param width
92
+ # @param symbol [String] Specifies the character that should be used
93
+ # to render the bars.
94
+ #
95
+ # @return [Barplot] A plot object.
96
+ #
97
+ # @example Example usage of barplot on IRB:
98
+ #
99
+ # >> UnicodePlot.barplot(["Paris", "New York", "Moskau", "Madrid"],
100
+ # [2.244, 8.406, 11.92, 3.165],
101
+ # xlabel: "population [in mil]").render
102
+ # ┌ ┐
103
+ # Paris ┤■■■■■■ 2.244
104
+ # New York ┤■■■■■■■■■■■■■■■■■■■■■■■ 8.406
105
+ # Moskau ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 11.92
106
+ # Madrid ┤■■■■■■■■■ 3.165
107
+ # └ ┘
108
+ # population [in mil]
109
+ # => nil
110
+ #
111
+ # @see Plot
112
+ # @see histogram
113
+ # @see Barplot
114
+ #
115
+ # @overload barplot(data, **kwargs)
116
+ #
117
+ # The different variation of barplot described above.
118
+ #
119
+ # @param data [Hash] A hash in which the keys will be used as `text` and
120
+ # the values will be utilized as `heights`.
121
+ # @param kwargs Optional keyword arguments same as ones described above.
122
+ # @return [Barplot] A plot object.
72
123
  module_function def barplot(*args,
73
124
  width: Plot::DEFAULT_WIDTH,
74
125
  color: Barplot::DEFAULT_COLOR,
@@ -108,10 +159,24 @@ module UnicodePlot
108
159
  plot
109
160
  end
110
161
 
162
+ # @overload barplot!(plot, text, heights)
163
+ #
164
+ # Draw additional bars on the given existing plot.
165
+ #
166
+ # @param plot [Barplot] the existing plot.
167
+ # @return [Barplot] A plot object.
168
+ #
169
+ # @see barplot
170
+ #
171
+ # @overload barplot!(plot, data)
172
+ #
173
+ # The different variation of `barplot!` that takes the plotting data in a hash.
174
+ #
175
+ # @param plot [Barplot] the existing plot.
176
+ # @return [Barplot] A plot object.
111
177
  module_function def barplot!(plot,
112
178
  *args,
113
- data: nil,
114
- **kw)
179
+ data: nil)
115
180
  case args.length
116
181
  when 0
117
182
  data = Hash(data)
@@ -2,16 +2,13 @@ module UnicodePlot
2
2
  class Canvas
3
3
  include BorderPrinter
4
4
 
5
+ CANVAS_CLASS_MAP = {}
6
+
5
7
  def self.create(canvas_type, width, height, **kw)
6
- case canvas_type
7
- when :ascii
8
- AsciiCanvas.new(width, height, **kw)
9
- when :braille
10
- BrailleCanvas.new(width, height, **kw)
11
- when :density
12
- DensityCanvas.new(width, height, **kw)
13
- when :dot
14
- DotCanvas.new(width, height, **kw)
8
+ canvas_class = CANVAS_CLASS_MAP[canvas_type]
9
+ case canvas_class
10
+ when Class
11
+ canvas_class.new(width, height, **kw)
15
12
  else
16
13
  raise ArgumentError, "unknown canvas type: #{canvas_type}"
17
14
  end
@@ -166,4 +163,16 @@ module UnicodePlot
166
163
  raise ArgumentError, "#{name} has to be positive"
167
164
  end
168
165
  end
166
+
167
+ def self.canvas_types
168
+ Canvas::CANVAS_CLASS_MAP.keys
169
+ end
169
170
  end
171
+
172
+ require_relative 'canvas/ascii_canvas'
173
+ require_relative 'canvas/block_canvas'
174
+ require_relative 'canvas/braille_canvas'
175
+ require_relative 'canvas/density_canvas'
176
+ require_relative 'canvas/dot_canvas'
177
+
178
+ UnicodePlot::Canvas::CANVAS_CLASS_MAP.freeze
@@ -1,5 +1,9 @@
1
+ require_relative 'lookup_canvas'
2
+
1
3
  module UnicodePlot
2
4
  class AsciiCanvas < LookupCanvas
5
+ Canvas::CANVAS_CLASS_MAP[:ascii] = self
6
+
3
7
  ASCII_SIGNS = [
4
8
  [ 0b100_000_000, 0b000_100_000, 0b000_000_100 ].freeze,
5
9
  [ 0b010_000_000, 0b000_010_000, 0b000_000_010 ].freeze,
@@ -0,0 +1,38 @@
1
+ module UnicodePlot
2
+ # The `BlockCanvas` is also Unicode-based.
3
+ # It has half the resolution of the `BrailleCanvas`.
4
+ # In contrast to BrailleCanvas, the pixels don't
5
+ # have visible spacing between them.
6
+ # This canvas effectively turns every character
7
+ # into 4 pixels that can individually be manipulated
8
+ # using binary operations.
9
+ class BlockCanvas < LookupCanvas
10
+ Canvas::CANVAS_CLASS_MAP[:block] = self
11
+
12
+ X_PIXEL_PER_CHAR = 2
13
+ Y_PIXEL_PER_CHAR = 2
14
+
15
+ def initialize(width, height, fill_char=0, **kw)
16
+ super(width, height,
17
+ X_PIXEL_PER_CHAR,
18
+ Y_PIXEL_PER_CHAR,
19
+ fill_char,
20
+ **kw)
21
+ end
22
+
23
+ BLOCK_SIGNS = [
24
+ [0b1000, 0b0010].freeze,
25
+ [0b0100, 0b0001].freeze
26
+ ].freeze
27
+
28
+ BLOCK_DECODE = [
29
+ -' ', -'▗', -'▖', -'▄',
30
+ -'▝', -'▐', -'▞', -'▟',
31
+ -'▘', -'▚', -'▌', -'▙',
32
+ -'▀', -'▜', -'▛', -'█'
33
+ ].freeze
34
+
35
+ def lookup_encode(x,y) ; BLOCK_SIGNS[x][y] ; end
36
+ def lookup_decode(x) ; BLOCK_DECODE[x] ; end
37
+ end
38
+ end
@@ -1,5 +1,7 @@
1
1
  module UnicodePlot
2
2
  class BrailleCanvas < Canvas
3
+ Canvas::CANVAS_CLASS_MAP[:braille] = self
4
+
3
5
  X_PIXEL_PER_CHAR = 2
4
6
  Y_PIXEL_PER_CHAR = 4
5
7
 
@@ -40,8 +42,8 @@ module UnicodePlot
40
42
  char_x_off = pixel_x % X_PIXEL_PER_CHAR + 1
41
43
  char_x += 1 if char_x < tx.round + 1 && char_x_off == 1
42
44
 
43
- char_y = (pixel_y.fdiv(pixel_height) * height).floor + 1
44
45
  char_y_off = pixel_y % Y_PIXEL_PER_CHAR + 1
46
+ char_y = ((pixel_y - (char_y_off - 1)) / Y_PIXEL_PER_CHAR) + 1
45
47
 
46
48
  index = index_at(char_x - 1, char_y - 1)
47
49
  if index
@@ -1,5 +1,7 @@
1
1
  module UnicodePlot
2
2
  class DensityCanvas < Canvas
3
+ Canvas::CANVAS_CLASS_MAP[:density] = self
4
+
3
5
  DENSITY_SIGNS = [" ", "░", "▒", "▓", "█"].freeze
4
6
 
5
7
  MIN_WIDTH = 5
@@ -1,5 +1,7 @@
1
1
  module UnicodePlot
2
2
  class DotCanvas < LookupCanvas
3
+ Canvas::CANVAS_CLASS_MAP[:dot] = self
4
+
3
5
  DOT_SIGNS = [
4
6
  [
5
7
  0b10,
@@ -23,8 +23,8 @@ module UnicodePlot
23
23
  char_x_off = pixel_x % x_pixel_per_char + 1
24
24
  char_x += 1 if char_x < tx.round + 1 && char_x_off == 1
25
25
 
26
- char_y = (pixel_y.fdiv(pixel_height) * height).floor + 1
27
26
  char_y_off = pixel_y % y_pixel_per_char + 1
27
+ char_y = ((pixel_y - (char_y_off - 1)) / y_pixel_per_char) + 1
28
28
 
29
29
  index = index_at(char_x - 1, char_y - 1)
30
30
  if index
@@ -0,0 +1,32 @@
1
+ require "forwardable"
2
+
3
+ module UnicodePlot
4
+ class IOContext
5
+ extend Forwardable
6
+
7
+ def initialize(io, color: :auto)
8
+ @io = io
9
+ @color = check_color(color)
10
+ end
11
+
12
+ def_delegators :@io, :print, :puts
13
+
14
+ def color?
15
+ case @color
16
+ when :auto
17
+ @io.respond_to?(:tty?) ? @io.tty? : false
18
+ else
19
+ @color
20
+ end
21
+ end
22
+
23
+ private def check_color(color)
24
+ case color
25
+ when true, false, :auto
26
+ color
27
+ else
28
+ raise ArgumentError, "color must be either true, false, :auto"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -4,6 +4,32 @@ module UnicodePlot
4
4
  class Lineplot < GridPlot
5
5
  end
6
6
 
7
+ # @overload lineplot([x], y, name: "", canvas: :braille, title: "", xlabel: "", ylabel: "", labels: true, border: :solid, margin: Plot::DEFAULT_MARGIN, padding: Plot::DEFAULT_PADDING, color: :auto, width: Plot::DEFAULT_WIDTH, height: GridPlot::DEFAULT_HEIGHT, xlim: [0, 0], ylim: [0, 0], canvas: :braille, grid: true)
8
+ #
9
+ # Draws a path through the given points on a new canvas.
10
+ #
11
+ # The first (optional) array `x` should contain the horizontal positions for all the points along the path.
12
+ # The second array `y` should then contain the corresponding vertical positions respectively.
13
+ # This means that the two vectors must be of the same length and ordering.
14
+ #
15
+ # @param x [Array<Numeric>] Optional. The horizontal position for each point. If omitted, the axes of `y` will be used as `x`.
16
+ # @param y [Array<Numeric>] The vertical position for each point.
17
+ # @param name [String] Annotation of the current drawing to be displayed on the right.
18
+ # @param title
19
+ # @param xlabel
20
+ # @param ylabel
21
+ # @param labels
22
+ # @param border
23
+ # @param margin
24
+ # @param padding
25
+ # @param color
26
+ # @param width
27
+ # @param height
28
+ # @param xlim
29
+ # @param ylim
30
+ # @param canvas [Symbol] The type of canvas that should be used for drawing.
31
+ # @param grid [true,false] If `true`, draws grid-lines at the origin.
32
+ # @return [Lineplot] A plot object.
7
33
  module_function def lineplot(*args,
8
34
  canvas: :braille,
9
35
  color: :auto,
@@ -41,6 +67,16 @@ module UnicodePlot
41
67
  end
42
68
  end
43
69
 
70
+ # @overload lineplot!(plot, [x], y, name: "", color: :auto)
71
+ #
72
+ # Draws a path through the given points on the given canvas.
73
+ #
74
+ # @param plot [Lineplot] The plot object.
75
+ # @param x [Array<Numeric>] Optional. The horizontal position for each point. If omitted, the axes of `y` will be used as `x`.
76
+ # @param y [Array<Numeric>] The vertical position for each point.
77
+ # @param name [String] Annotation of the current drawing to be displayed on the right.
78
+ # @param color
79
+ # @return [Lineplot] The plot object same as the `plot` parameter.
44
80
  module_function def lineplot!(plot,
45
81
  *args,
46
82
  color: :auto,
@@ -17,7 +17,7 @@ module UnicodePlot
17
17
  @title = title
18
18
  @xlabel = xlabel
19
19
  @ylabel = ylabel
20
- @border = border
20
+ @border = check_border(border)
21
21
  @margin = check_margin(margin)
22
22
  @padding = padding
23
23
  @labels_left = {}
@@ -103,8 +103,8 @@ module UnicodePlot
103
103
  end
104
104
  end
105
105
 
106
- def render(out=$stdout, newline: true)
107
- Renderer.render(out, self, newline)
106
+ def render(out=$stdout, newline: true, color: :auto)
107
+ Renderer.render(IOContext.new(out, color: color), self, newline)
108
108
  end
109
109
 
110
110
  COLOR_CYCLE = [
@@ -142,5 +142,10 @@ module UnicodePlot
142
142
  raise ArgumentError, "row_index out of bounds"
143
143
  end
144
144
  end
145
+
146
+ private def check_border(border)
147
+ return border if BORDER_MAP.key?(border)
148
+ raise ArgumentError, "unknown border type: #{border}"
149
+ end
145
150
  end
146
151
  end