unicode_plot 0.0.4 → 0.0.5

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.
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