rubyplot 0.0.1 → 0.1.pre.a1

Sign up to get free protection for your applications and to get access to all the features.
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