rubyplot 0.0.1 → 0.1.pre.a1

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.
Files changed (62) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +133 -0
  5. data/.travis.yml +18 -0
  6. data/CHANGELOG.md +9 -0
  7. data/CONTRIBUTING.md +48 -0
  8. data/Gemfile +6 -0
  9. data/README.md +47 -0
  10. data/Rakefile +32 -0
  11. data/ext/grruby/extconf.rb +6 -0
  12. data/ext/grruby/grruby.c +1163 -0
  13. data/ext/grruby/grruby.h +135 -0
  14. data/lib/rubyplot.rb +30 -1
  15. data/lib/rubyplot/artist.rb +13 -0
  16. data/lib/rubyplot/artist/axes.rb +328 -0
  17. data/lib/rubyplot/artist/axis.rb +3 -0
  18. data/lib/rubyplot/artist/axis/base.rb +34 -0
  19. data/lib/rubyplot/artist/axis/x_axis.rb +35 -0
  20. data/lib/rubyplot/artist/axis/y_axis.rb +40 -0
  21. data/lib/rubyplot/artist/base.rb +14 -0
  22. data/lib/rubyplot/artist/circle.rb +28 -0
  23. data/lib/rubyplot/artist/figure.rb +90 -0
  24. data/lib/rubyplot/artist/legend.rb +59 -0
  25. data/lib/rubyplot/artist/legend_box.rb +89 -0
  26. data/lib/rubyplot/artist/line2d.rb +24 -0
  27. data/lib/rubyplot/artist/plot.rb +9 -0
  28. data/lib/rubyplot/artist/plot/area.rb +38 -0
  29. data/lib/rubyplot/artist/plot/bar.rb +69 -0
  30. data/lib/rubyplot/artist/plot/bar_type.rb +31 -0
  31. data/lib/rubyplot/artist/plot/base.rb +67 -0
  32. data/lib/rubyplot/artist/plot/bubble.rb +41 -0
  33. data/lib/rubyplot/artist/plot/line.rb +61 -0
  34. data/lib/rubyplot/artist/plot/multi_bars.rb +75 -0
  35. data/lib/rubyplot/artist/plot/multi_stacked_bar.rb +78 -0
  36. data/lib/rubyplot/artist/plot/scatter.rb +29 -0
  37. data/lib/rubyplot/artist/plot/stacked_bar.rb +69 -0
  38. data/lib/rubyplot/artist/polygon.rb +21 -0
  39. data/lib/rubyplot/artist/rectangle.rb +39 -0
  40. data/lib/rubyplot/artist/text.rb +49 -0
  41. data/lib/rubyplot/artist/tick.rb +3 -0
  42. data/lib/rubyplot/artist/tick/base.rb +35 -0
  43. data/lib/rubyplot/artist/tick/x_tick.rb +25 -0
  44. data/lib/rubyplot/artist/tick/y_tick.rb +24 -0
  45. data/lib/rubyplot/backend.rb +2 -0
  46. data/lib/rubyplot/backend/gr_wrapper.rb +7 -0
  47. data/lib/rubyplot/backend/magick_wrapper.rb +141 -0
  48. data/lib/rubyplot/color.rb +992 -0
  49. data/lib/rubyplot/figure.rb +2 -0
  50. data/lib/rubyplot/spi.rb +8 -0
  51. data/lib/rubyplot/subplot.rb +4 -0
  52. data/lib/rubyplot/themes.rb +47 -0
  53. data/lib/rubyplot/utils.rb +14 -0
  54. data/lib/rubyplot/version.rb +1 -1
  55. data/rubyplot.gemspec +10 -0
  56. data/spec/axes_spec.rb +477 -0
  57. data/spec/figure_spec.rb +12 -0
  58. data/spec/spec_helper.rb +62 -0
  59. data/spec/spi/multi_plot_graph_spec.rb +33 -0
  60. data/spec/spi/single_plot_graph_spec.rb +227 -0
  61. data/spec/spi/subplots_spec.rb +55 -0
  62. metadata +166 -8
@@ -0,0 +1,69 @@
1
+ module Rubyplot
2
+ module Artist
3
+ module Plot
4
+ class Bar < Artist::Plot::Base
5
+ # Space between the columns.
6
+ attr_accessor :bar_spacing
7
+ # Width of each bar in pixels.
8
+ attr_accessor :bar_width
9
+ # Number between 0 and 1.0 denoting spacing between the bars.
10
+ # 0.0 means no spacing at all 1.0 means that each bars' width
11
+ # is nearly 0 (so each bar is a simple line with no X dimension).
12
+ # Denotes the total left + right side space.
13
+ attr_reader :spacing_ratio
14
+ # X co-ordinates of the lower left corner of the bar.
15
+ attr_accessor :abs_x_left
16
+ # Y co-ordinates of the lower left corner of the bar.
17
+ attr_accessor :abs_y_left
18
+
19
+ def initialize(*)
20
+ super
21
+ @spacing_ratio = 0.1
22
+ @abs_x_left = []
23
+ @abs_y_left = []
24
+ @rectangles = []
25
+ end
26
+
27
+ # Set the spacing factor for this bar plot.
28
+ def spacing_factor=(s_f)
29
+ raise ValueError, '@spacing_factor must be between 0.00 and 1.00' unless
30
+ (s_f >= 0) && (s_f <= 1)
31
+
32
+ @spacing_factor = s_f
33
+ end
34
+
35
+ # Set Bar plot data.
36
+ def data y_values
37
+ super(Array.new(y_values.size) { |i| i }, y_values)
38
+ end
39
+
40
+ # Number of bars in this Bar plot
41
+ def num_bars
42
+ @data[:y_values].size
43
+ end
44
+
45
+ def draw
46
+ setup_bar_rectangles
47
+ @rectangles.each(&:draw)
48
+ end
49
+
50
+ private
51
+
52
+ def setup_bar_rectangles
53
+ @normalized_data[:y_values].each_with_index do |iy, i|
54
+ height = iy * @axes.y_axis.length
55
+ @rectangles << Rubyplot::Artist::Rectangle.new(
56
+ self,
57
+ abs_x: @abs_x_left[i],
58
+ abs_y: @abs_y_left[i] - height,
59
+ width: @bar_width,
60
+ height: height,
61
+ border_color: @data[:color],
62
+ fill_color: @data[:color]
63
+ )
64
+ end
65
+ end
66
+ end # class Bar
67
+ end # module Plot
68
+ end # module Artist
69
+ end # module Rubyplot
@@ -0,0 +1,31 @@
1
+ module Rubyplot
2
+ module Artist
3
+ module Plot
4
+ class BarType < Artist::Plot::Base
5
+ def initialize(*)
6
+ super
7
+ @spacing_ratio = 0.1
8
+ @abs_x_left = []
9
+ @abs_y_left = []
10
+ @rectangles = []
11
+ end
12
+
13
+ def data y_values
14
+ super(Array(0...(y_values.size)), y_values)
15
+ end
16
+
17
+ def num_bars
18
+ @data[:y_values].size
19
+ end
20
+
21
+ def draw
22
+ setup_bar_rectangles
23
+ @rectangles.each(&:draw)
24
+ end
25
+
26
+ protected
27
+
28
+ end # class BarType
29
+ end # module Plot
30
+ end # module Artist
31
+ end # module Rubyplot
@@ -0,0 +1,67 @@
1
+ module Rubyplot
2
+ module Artist
3
+ module Plot
4
+ class Base < Artist::Base
5
+ attr_reader :axes, :data, :x_max, :x_min, :y_min, :y_max
6
+ attr_writer :stroke_width, :stroke_opacity
7
+
8
+ def initialize axes
9
+ super(axes.abs_x, axes.abs_y)
10
+ @axes = axes
11
+ @data = {
12
+ label: '',
13
+ color: :default
14
+ }
15
+ @normalized_data = {
16
+ y_values: nil,
17
+ x_values: nil
18
+ }
19
+ @stroke_width = 4.0
20
+ @stroke_opacity = 0.0
21
+ end
22
+
23
+ def label
24
+ @data[:label]
25
+ end
26
+
27
+ def color
28
+ @data[:color]
29
+ end
30
+
31
+ def label=(label)
32
+ @data[:label] = label
33
+ end
34
+
35
+ def color= color
36
+ @data[:color] = color
37
+ end
38
+
39
+ def data(x_values, y_values)
40
+ @data[:x_values] = x_values
41
+ @data[:y_values] = y_values
42
+ @y_min = @data[:y_values].min
43
+ @y_max = @data[:y_values].max
44
+ @x_min = @data[:x_values].min
45
+ @x_max = @data[:x_values].max
46
+ end
47
+
48
+ # Normalize original data to values between 0-1. Used for obtaining relative
49
+ # values of the data.
50
+ def normalize
51
+ x_spread = @axes.x_range[1] - @axes.x_range[0]
52
+ y_spread = @axes.y_range[1] - @axes.y_range[0]
53
+ if @data[:x_values]
54
+ @normalized_data[:x_values] = @data[:x_values].map do |x|
55
+ (x.to_f - @axes.x_range[0]) / x_spread
56
+ end
57
+ end
58
+ if @data[:y_values]
59
+ @normalized_data[:y_values] = @data[:y_values].map do |y|
60
+ (y.to_f - @axes.y_range[0]) / y_spread
61
+ end
62
+ end
63
+ end
64
+ end # class Base
65
+ end # module Plot
66
+ end # module Artist
67
+ end # module Rubyplot
@@ -0,0 +1,41 @@
1
+ module Rubyplot
2
+ module Artist
3
+ module Plot
4
+ class Bubble < Artist::Plot::Base
5
+ # Width in pixels of the border of each bubble.
6
+ attr_reader :stroke_width
7
+ attr_reader :z_max, :z_min
8
+ def initialize(*)
9
+ super
10
+ @bubbles = []
11
+ @stroke_width = 1.0
12
+ end
13
+
14
+ def data x_values, y_values, z_values
15
+ super(x_values, y_values)
16
+ @data[:z_values] = z_values
17
+ @z_max = @data[:z_values].max
18
+ @z_min = @data[:z_values].min
19
+ end
20
+
21
+ def draw
22
+ @normalized_data[:y_values].each_with_index do |iy, idx_y|
23
+ ix = @normalized_data[:x_values][idx_y]
24
+ iz = @data[:z_values][idx_y]
25
+ abs_x = ix * @axes.x_axis.length + @axes.abs_x + @axes.y_axis_margin
26
+ abs_y = (@axes.y_axis.length - iy * @axes.y_axis.length) + @axes.abs_y
27
+ @bubbles << Rubyplot::Artist::Circle.new(
28
+ self,
29
+ abs_x: abs_x,
30
+ abs_y: abs_y,
31
+ radius: iz,
32
+ color: @data[:color],
33
+ stroke_width: @stroke_width
34
+ )
35
+ end
36
+ @bubbles.each(&:draw)
37
+ end
38
+ end # class Bubble
39
+ end # module Plot
40
+ end # module Artist
41
+ end # module Rubyplot
@@ -0,0 +1,61 @@
1
+ module Rubyplot
2
+ module Artist
3
+ module Plot
4
+ class Line < Artist::Plot::Base
5
+ # Set true if you want to see only the vertices of the line plot.
6
+ attr_writer :hide_lines
7
+
8
+ def initialize(*)
9
+ super
10
+ @hide_lines = false
11
+ end
12
+
13
+ def data(x_values, y_values=[])
14
+ y_values = Array.new(x_values.size) { |i| i } if y_values.empty?
15
+ super x_values, y_values
16
+ end
17
+
18
+ def draw
19
+ if @normalized_data[:x_values].size == 1
20
+ draw_single_point
21
+ else
22
+ draw_lines
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ # FIXME: make this edge case happen.
29
+ def draw_single_point
30
+ # Rubyplot::Artist::Circle.new()
31
+ end
32
+
33
+ def draw_lines
34
+ prev_x = prev_y = nil
35
+ @normalized_data[:x_values].each_with_index do |ix, idx_ix|
36
+ iy = @normalized_data[:y_values][idx_ix]
37
+ next if ix.nil? || iy.nil?
38
+
39
+ new_x = ix * (@axes.x_axis.abs_x2 - @axes.x_axis.abs_x1).abs + @axes.abs_x +
40
+ @axes.y_axis_margin
41
+ new_y = (@axes.y_axis.length - iy * @axes.y_axis.length) + @axes.abs_y
42
+
43
+ unless prev_x.nil? && prev_y.nil?
44
+ Rubyplot::Artist::Line2D.new(
45
+ self,
46
+ abs_x1: prev_x,
47
+ abs_y1: prev_y,
48
+ abs_x2: new_x,
49
+ abs_y2: new_y,
50
+ stroke_opacity: @stroke_opacity,
51
+ stroke_width: @stroke_width
52
+ ).draw
53
+ end
54
+ prev_x = new_x
55
+ prev_y = new_y
56
+ end
57
+ end
58
+ end # class Line
59
+ end # module Plot
60
+ end # module Artist
61
+ end # module Rubyplot
@@ -0,0 +1,75 @@
1
+ module Rubyplot
2
+ module Artist
3
+ module Plot
4
+ # Class for holding multiple Bar plot objects.
5
+ # Terminoligies used:
6
+ #
7
+ # * A 'bar' is a single bar of a single bar plot.
8
+ # * A 'slot' is a box within which multiple bars can be plotted.
9
+ # * 'padding' is the total whitespace on the left and right of a slot.
10
+ class MultiBars < Artist::Plot::Base
11
+ # The max. width that each bar can occupy.
12
+ attr_reader :max_bar_width
13
+
14
+ def initialize(*args, bar_plots:)
15
+ super(args[0])
16
+ @bar_plots = bar_plots
17
+ @x_min = @bar_plots.map(&:x_min).min
18
+ @y_min = @bar_plots.map(&:y_min).min
19
+ @x_max = @bar_plots.map(&:x_max).max
20
+ @y_max = @bar_plots.map(&:y_max).max
21
+ configure_plot_geometry_data!
22
+ configure_x_ticks!
23
+ end
24
+
25
+ def normalize
26
+ @bar_plots.each(&:normalize)
27
+ end
28
+
29
+ def draw
30
+ @bar_plots.each(&:draw)
31
+ end
32
+
33
+ private
34
+
35
+ def configure_plot_geometry_data!
36
+ @num_max_slots = @bar_plots.map(&:num_bars).max
37
+ @max_slot_width = (@axes.x_axis.abs_x2 - @axes.x_axis.abs_x1).abs / @num_max_slots
38
+ # FIXME: figure out a way to specify inter-box space somehow.
39
+ @spacing_ratio = @bar_plots[0].spacing_ratio
40
+ @padding = @spacing_ratio * @max_slot_width
41
+ @max_bars_width = @max_slot_width - @padding
42
+ @bars_per_slot = @bar_plots.size
43
+ @bar_plots.each_with_index do |bar, index|
44
+ set_bar_dims bar, index
45
+ end
46
+ end
47
+
48
+ def configure_x_ticks!
49
+ @axes.num_x_ticks = @num_max_slots
50
+ labels = @axes.x_ticks || Array.new(@num_max_slots, &:to_s)
51
+ labels = labels[0...@axes.num_x_ticks] if labels.size != @axes.num_x_ticks
52
+ @axes.x_ticks = labels.map.with_index do |label, i|
53
+ Rubyplot::Artist::XTick.new(
54
+ @axes,
55
+ abs_x: @axes.abs_x + @axes.y_axis_margin + i * @max_slot_width + @max_slot_width / 2,
56
+ abs_y: @axes.origin[1],
57
+ label: label,
58
+ length: 6,
59
+ label_distance: 10
60
+ )
61
+ end
62
+ end
63
+
64
+ def set_bar_dims bar_plot, index
65
+ bar_plot.bar_width = @max_bars_width / @bars_per_slot
66
+ @num_max_slots.times do |i|
67
+ bar_plot.abs_x_left[i] = @axes.abs_x + @axes.y_axis_margin +
68
+ i * @max_slot_width + @padding / 2 + index * bar_plot.bar_width
69
+ bar_plot.abs_y_left[i] = @axes.origin[1] - @axes.x_axis.stroke_width
70
+ end
71
+ end
72
+ end # class MultiBars
73
+ end # module Plot
74
+ end # module Artist
75
+ end # module Rubyplot
@@ -0,0 +1,78 @@
1
+ module Rubyplot
2
+ module Artist
3
+ module Plot
4
+ class MultiStackedBar < Artist::Plot::Base
5
+ def initialize(*args, stacked_bars:)
6
+ super(args[0])
7
+ @stacked_bars = stacked_bars
8
+ @x_min = @stacked_bars.map(&:x_min).min
9
+ @y_min = @stacked_bars.map(&:y_min).min
10
+ @x_max = @stacked_bars.map(&:x_max).max
11
+ @y_max = @stacked_bars.map(&:y_max).max
12
+ reset_axes_ranges
13
+ renormalize_data
14
+ configure_plot_geometry_data
15
+ configure_x_ticks
16
+ end
17
+
18
+ def draw
19
+ @stacked_bars.each(&:draw)
20
+ end
21
+
22
+ private
23
+
24
+ def reset_axes_ranges
25
+ @axes.y_range[0] = 0
26
+ @axes.x_range[0] = 0
27
+ end
28
+
29
+ # Normalize data in the stacked bar plot w.r.t each other and new X & Y ranges.
30
+ def renormalize_data
31
+ @stacked_bars.each do |p|
32
+ p.normalize
33
+ p.renormalize @stacked_bars.size
34
+ end
35
+ end
36
+
37
+ def configure_plot_geometry_data
38
+ @num_max_slots = @stacked_bars.map(&:num_bars).max
39
+ @max_slot_width = @axes.x_axis.length / @num_max_slots
40
+ @spacing_ratio = @stacked_bars[0].spacing_ratio
41
+ @padding = @spacing_ratio * @max_slot_width
42
+ @max_bars_width = @max_slot_width - @padding
43
+ @num_max_stacks = @stacked_bars.size
44
+ @stacked_bars.each_with_index do |bar, index|
45
+ set_bar_dims bar, index
46
+ end
47
+ end
48
+
49
+ def configure_x_ticks
50
+ @axes.num_x_ticks = @num_max_slots
51
+ labels = @axes.x_ticks || Array.new(@num_max_slots, &:to_s)
52
+ labels = labels[0...@axes.num_x_ticks] if labels.size != @axes.num_x_ticks
53
+ @axes.x_ticks = labels.map.with_index do |label, i|
54
+ Rubyplot::Artist::XTick.new(
55
+ @axes,
56
+ abs_x: @axes.abs_x + @axes.y_axis_margin + i * @max_slot_width + @max_slot_width / 2,
57
+ abs_y: @axes.origin[1],
58
+ label: label,
59
+ length: 6,
60
+ label_distance: 10
61
+ )
62
+ end
63
+ end
64
+
65
+ def set_bar_dims bar, plot_index
66
+ bar.bar_width = @max_bars_width
67
+ plots_below = @stacked_bars[0...plot_index]
68
+ bar.num_bars.times do |i|
69
+ pedestal_height = plots_below.map { |p| p.member_height(i) }.inject(:+) || 0
70
+ bar.abs_x_left[i] = @axes.abs_x + @axes.y_axis_margin +
71
+ i * @max_slot_width + @padding / 2
72
+ bar.abs_y_left[i] = (@axes.abs_y + @axes.y_axis.length) - pedestal_height
73
+ end
74
+ end
75
+ end # class StackedBar
76
+ end # module Plot
77
+ end # module Artist
78
+ end # module Rubyplot
@@ -0,0 +1,29 @@
1
+ module Rubyplot
2
+ module Artist
3
+ module Plot
4
+ class Scatter < Artist::Plot::Base
5
+ attr_writer :circle_radius
6
+
7
+ def initialize(*)
8
+ super
9
+ @circle_radius = 4.0
10
+ end
11
+
12
+ def draw
13
+ @normalized_data[:y_values].each_with_index do |iy, idx_y|
14
+ ix = @normalized_data[:x_values][idx_y]
15
+ next if iy.nil? || ix.nil?
16
+
17
+ abs_x = ix * @axes.x_axis.length + @axes.abs_x + @axes.y_axis_margin
18
+ abs_y = (@axes.y_axis.length - iy * @axes.y_axis.length) + @axes.abs_y
19
+ Rubyplot::Artist::Circle.new(
20
+ self, abs_x: abs_x, abs_y: abs_y, radius: @circle_radius,
21
+ stroke_opacity: @stroke_opacity,
22
+ stroke_width: @stroke_width
23
+ ).draw
24
+ end
25
+ end
26
+ end # class Scatter
27
+ end # module Plot
28
+ end # module Artist
29
+ end # module Rubyplot