Skip to content

Commit 56247f7

Browse files
committed
Added support for multiple bar plots in a single Axes.
WIP adding support for multiple bar plots Able to plot multiple bar plots
1 parent 5b76bf5 commit 56247f7

13 files changed

Lines changed: 127 additions & 69 deletions

File tree

lib/rubyplot/artist/axes.rb

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,14 @@ class Axes < Base
2121
# Range of Y axis.
2222
attr_accessor :y_range,
2323
:text_font, :grid,
24-
:bounding_box, :x_axis_padding, :y_axis_padding, :origin,
24+
:bounding_box, :origin,
2525
:title_shift, :title_margin
2626
# Main title for this Axes.
2727
attr_accessor :title
2828
# Rubyplot::Figure object to which this Axes belongs.
2929
attr_reader :figure
3030
# Array of plots contained in this Axes.
3131
attr_reader :plots
32-
# data variables for something
33-
attr_reader :raw_rows
3432

3533
attr_reader :geometry, :font, :marker_font_size, :legend_font_size,
3634
:title_font_size, :scale, :font_color, :marker_color, :axes,
@@ -112,7 +110,9 @@ def legend_box_iy
112110

113111
# Write an image to a file by communicating with the backend.
114112
def draw
113+
assign_plot_defaults
115114
consolidate_plots
115+
gather_plot_data
116116
configure_title
117117
calculate_xy_axes_origin
118118
configure_xy_axes
@@ -186,6 +186,19 @@ def x_ticks= ticks_hash
186186

187187
private
188188

189+
def assign_plot_defaults
190+
assign_label_colors
191+
end
192+
193+
def assign_label_colors
194+
@plots.each_with_index do |p, i|
195+
if p.color == :default
196+
p.color = @figure.theme_options[:label_colors][
197+
i % @figure.theme_options[:label_colors].size]
198+
end
199+
end
200+
end
201+
189202
def add_plot plot_type, *args, &block
190203
plot = with_backend plot_type, *args
191204
yield(plot) if block_given?
@@ -204,11 +217,6 @@ def with_backend plot_type, *args
204217
plot
205218
end
206219

207-
def prepare_legend
208-
@legends = @plots.map(&:create_legend)
209-
@legends.each { |l| l.draw }
210-
end
211-
212220
# Figure out the co-ordinates of the title text w.r.t Axes.
213221
def configure_title
214222
@title = Rubyplot::Artist::Text.new(
@@ -296,8 +304,29 @@ def label_string(value, increment)
296304

297305
def consolidate_plots
298306
bars = @plots.grep(Rubyplot::Artist::Plot::Bar)
299-
@plots.delete_if { |p| p.is_a?(Rubyplot::Artist::Plot::Bar) }
300-
@plots << Rubyplot::Artist::Plot::MultiBars.new(self, bar_plots: bars)
307+
if !bars.empty?
308+
@plots.delete_if { |p| p.is_a?(Rubyplot::Artist::Plot::Bar) }
309+
@plots << Rubyplot::Artist::Plot::MultiBars.new(self, bar_plots: bars)
310+
end
311+
end
312+
313+
def gather_plot_data
314+
set_xrange
315+
set_yrange
316+
end
317+
318+
def set_xrange
319+
if @x_range[0].nil? && @x_range[1].nil?
320+
@x_range[0] = @plots.map { |p| p.x_min }.min
321+
@x_range[1] = @plots.map { |p| p.x_max }.max
322+
end
323+
end
324+
325+
def set_yrange
326+
if @y_range[0].nil? && @y_range[1].nil?
327+
@y_range[0] = @plots.map { |p| p.y_min }.min
328+
@y_range[1] = @plots.map { |p| p.y_max }.max
329+
end
301330
end
302331
end # class Axes
303332
end # moudle Artist

lib/rubyplot/artist/axis/base.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class Base
66
FINISH_ARROW_LENGTH = 10.0
77

88
attr_reader :label, :ticks, :major_ticks_count, :min_val, :max_val, :title
9-
attr_reader :abs_x1, :abs_x2, :abs_y1, :abs_y2, :backend
9+
attr_reader :abs_x1, :abs_x2, :abs_y1, :abs_y2, :backend, :length
1010
attr_reader :stroke_width, :major_ticks
1111

1212
def initialize axes, title, min_val, max_val

lib/rubyplot/artist/axis/x_axis.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def initialize(*)
1111
@abs_y2 = @axes.origin[1]
1212
@major_ticks_count = 5
1313
@major_ticks_distance = (@abs_x2 - @abs_x1) / @major_ticks_count
14+
@length = (@abs_x2 - @abs_x1).abs
1415
configure_axis_line
1516
populate_major_x_ticks
1617
configure_title
@@ -26,12 +27,11 @@ def draw
2627

2728
def configure_axis_line
2829
@line = Rubyplot::Artist::Line2D.new(
29-
self, abs_x1: @abs_x1, abs_y1: @abs_y1, abs_x2: @abs_x2, abs_y2: @abs_y2)
30-
#stroke_width: @stroke_width)
30+
self, abs_x1: @abs_x1, abs_y1: @abs_y1, abs_x2: @abs_x2, abs_y2: @abs_y2,
31+
stroke_width: @stroke_width)
3132
end
3233

3334
def populate_major_x_ticks
34-
puts "max_val #{@max_val} #{@major_ticks_count}"
3535
value_distance = (@max_val) / @major_ticks_count.to_f
3636
@major_ticks_count.times do |count|
3737
count += 1

lib/rubyplot/artist/axis/y_axis.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def initialize(*)
88
@abs_x2 = @axes.origin[0]
99
@abs_y2 = @axes.origin[1] - (@axes.height - @axes.x_axis_margin)
1010
@y_ticks = []
11+
@length = (@abs_y1 - @abs_y2).abs
1112
configure_axis_line
1213
configure_title
1314
populate_major_y_ticks

lib/rubyplot/artist/figure.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,17 @@ class Figure < Artist::Base
2828
attr_reader :theme_options
2929
attr_reader :marker_color
3030
attr_reader :font_color
31-
def initialize
31+
32+
# Initialize a Rubyplot::Artist::Figure object.
33+
# @param height [Integer] nil Height in pixels of the complete Figure.
34+
# @param width [Integer] nil Width in pixels of the complete Figure.
35+
def initialize(height: nil, width: nil)
3236
@title = ''
3337
@nrows = 1
3438
@ncols = 1
3539
@backend = Rubyplot::Backend::MagickWrapper.new
36-
@width = DEFAULT_TARGET_WIDTH
37-
@height = @width * 0.75
40+
@width = width || DEFAULT_TARGET_WIDTH
41+
@height = height || @width * 0.75
3842
@abs_x = 0
3943
@abs_y = 0
4044
@top_spacing = 0.05
@@ -58,6 +62,8 @@ def write file_name
5862
@backend.write(file_name)
5963
end
6064

65+
private
66+
6167
def setup_default_theme
6268
defaults = {
6369
marker_color: 'white',

lib/rubyplot/artist/plot/bar.rb

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,22 @@ class Bar < Artist::Plot::Base
66
attr_accessor :bar_spacing
77
# Width of each bar in pixels.
88
attr_accessor :bar_width
9-
# Height of each bar in pixels.
10-
attr_accessor :bar_height
119
# Number between 0 and 1.0 denoting spacing between the bars.
1210
# 0.0 means no spacing at all 1.0 means that each bars' width
1311
# is nearly 0 (so each bar is a simple line with no X dimension).
1412
# Denotes the total left + right side space.
1513
attr_reader :spacing_ratio
16-
# X co-ordinate of the lower left corner of the bar.
14+
# X co-ordinates of the lower left corner of the bar.
1715
attr_accessor :abs_x_left
18-
# Y co-ordinate of the lower left corner of the bar.
16+
# Y co-ordinates of the lower left corner of the bar.
1917
attr_accessor :abs_y_left
18+
2019
def initialize(*)
2120
super
2221
@spacing_ratio = 0.1
22+
@abs_x_left = []
23+
@abs_y_left = []
24+
@rectangles = []
2325
end
2426

2527
# Set the spacing factor for this bar plot.
@@ -41,13 +43,25 @@ def num_bars
4143

4244
def draw
4345
return unless @axes.geometry.has_data
44-
setup_bar_heights
46+
setup_bar_rectangles
47+
@rectangles.each(&:draw)
4548
end
4649

4750
private
4851

49-
def setup_bar_heights
50-
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
5165
end
5266
end # class Bar
5367
end # module Plot

lib/rubyplot/artist/plot/base.rb

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ module Rubyplot
22
module Artist
33
module Plot
44
class Base < Artist::Base
5-
attr_reader :axes, :data
5+
attr_reader :axes, :data, :x_max, :x_min, :y_min,
6+
:y_max
67
attr_writer :stroke_width, :stroke_opacity
78

89
def initialize axes
10+
super(axes.backend, axes.abs_x, axes.abs_y)
911
@axes = axes
1012
@backend = @axes.backend
1113
@data = {
@@ -41,41 +43,27 @@ def data x_values, y_values
4143
@data[:y_values] = y_values
4244
# Set column count if this is larger than previous column counts
4345
@axes.geometry.column_count = y_values.length > @axes.geometry.column_count ?
44-
y_values.length : @axes.geometry.column_count
45-
set_yrange
46-
set_xrange
46+
y_values.length : @axes.geometry.column_count
47+
@y_min = @data[:y_values].min
48+
@y_max = @data[:y_values].max
49+
@x_min = @data[:x_values].min
50+
@x_max = @data[:x_values].max
51+
4752
@axes.geometry.has_data = true
4853
end
4954

50-
# Normalize original data to values between 0-100.
55+
# Normalize original data to values between 0-1. Used for obtaining relative
56+
# values of the data.
5157
def normalize
52-
x_min = @axes.x_range[0] < 0 ? @axes.x_range[0] : 0
53-
y_min = @axes.y_range[0] < 0 ? @axes.y_range[0] : 0
54-
x_spread = @axes.x_range[1] - x_min
55-
y_spread = @axes.y_range[1] - y_min
58+
x_spread = @axes.x_range[1] - @axes.x_range[0]
59+
y_spread = @axes.y_range[1] - @axes.y_range[0]
5660
@normalized_data[:x_values] = @data[:x_values].map do |x|
57-
(x.to_f - x_min) / x_spread
61+
(x.to_f - @axes.x_range[0]) / x_spread
5862
end if @data[:x_values]
5963
@normalized_data[:y_values] = @data[:y_values].map do |y|
60-
(y.to_f - y_min) / y_spread
64+
(y.to_f - @axes.y_range[0]) / y_spread
6165
end if @data[:y_values]
6266
end
63-
64-
protected
65-
66-
def set_xrange
67-
if @axes.x_range[1].nil? && @axes.x_range[0].nil?
68-
@axes.x_range[0] = @data[:x_values].min
69-
@axes.x_range[1] = @data[:x_values].max
70-
end
71-
end
72-
73-
def set_yrange
74-
if @axes.y_range[0].nil? && @axes.y_range[1].nil?
75-
@axes.y_range[0] = @data[:y_values].min
76-
@axes.y_range[1] = @data[:y_values].max
77-
end
78-
end
7967
end # class Base
8068
end # module Plot
8169
end # module Artist

lib/rubyplot/artist/plot/multi_bars.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ class MultiBars < Artist::Plot::Base
1414
def initialize(*args, bar_plots:)
1515
super(args[0])
1616
@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+
end
22+
23+
def normalize
24+
@bar_plots.each(&:normalize)
1725
end
1826

1927
def draw
@@ -47,8 +55,11 @@ def configure_x_ticks
4755

4856
def set_bar_dims bar_plot, index
4957
bar_plot.bar_width = @max_bars_width / @bars_per_slot
50-
bar_plot.abs_x_left = @padding / 2 + index * bar_plot.bar_width
51-
bar_plot.abs_y_left = @axes.x_axis.abs_y1
58+
@num_max_slots.times do |i|
59+
bar_plot.abs_x_left[i] = @axes.abs_x + @axes.y_axis_margin +
60+
i * @max_slot_width + @padding / 2 + index * bar_plot.bar_width
61+
bar_plot.abs_y_left[i] = @axes.x_axis.abs_y1 - @axes.x_axis.stroke_width
62+
end
5263
end
5364
end # class MultiBars
5465
end # module Plot

lib/rubyplot/artist/plot/scatter.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ def initialize(*)
1010
end
1111

1212
def draw
13-
y_axis_length = (@axes.y_axis.abs_y2 - @axes.y_axis.abs_y1).abs
1413
@normalized_data[:y_values].each_with_index do |iy, idx_y|
1514
ix = @normalized_data[:x_values][idx_y]
1615
next if iy.nil? || ix.nil?
17-
abs_x = ix * (@axes.x_axis.abs_x2 - @axes.x_axis.abs_x1).abs + @axes.abs_x +
18-
@axes.y_axis_margin
19-
abs_y = (y_axis_length - iy * y_axis_length) + @axes.abs_y
16+
abs_x = ix * @axes.x_axis.length + @axes.abs_x + @axes.y_axis_margin
17+
abs_y = (@axes.y_axis.length - iy * @axes.y_axis.length) + @axes.abs_y
2018
Rubyplot::Artist::Circle.new(
2119
self,
2220
abs_x: abs_x,

lib/rubyplot/artist/rectangle.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ module Artist
33
class Rectangle < Base
44
attr_reader :width, :height, :border_color, :fill_color
55

6+
# Create a Rectangle for drawing on the canvas.
7+
#
8+
# @param abs_x [Float] Absolute X co-ordinate of the upper left corner.
9+
# @param abs_y [Float] Absolute Y co-ordinate of the upper left corner.
10+
# @param width [Float] Width in pixels of the rectangle.
11+
# @param height [Float] Height in pixels of the rectangle.
612
def initialize(owner,abs_x:,abs_y:,width:,height:,border_color:,fill_color: nil)
713
super(owner.backend, abs_x, abs_y)
814
@height = height

0 commit comments

Comments
 (0)