Skip to content

Commit 76848e8

Browse files
committed
finished stacked bar plots
1 parent 71d1f6d commit 76848e8

6 files changed

Lines changed: 97 additions & 13 deletions

File tree

CONTRIBUTING.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ The absolute co-ordinates are calculated during the draw phase. Therefore there
1212
should be no code except in the `draw` methods where actual co-ordinates are calcualted.
1313

1414
Varible naming conventions:
15-
* All values that are absolute values will be prefixed with `abs_`.
15+
* All values that are absolute values will be prefixed with `abs_**.
1616
* Variables relating to positioning of the graph other than the absolute
1717
variables are always ratios.
18+
19+
## Drawing flow
20+
21+
When the `draw` method in `Axes` is called, the call sequence is as follows:
22+
* Determine X and Y ranges.
23+
* Normalize the data within these ranges.
24+
* Assign defaults (if not assigned by user):
25+
- Default label colors.
26+
* Consolidate plots like bar plots into 'Multi-' plots.
27+
* Figure out location of the Axes title.
28+
* Figure out location of the legends.

lib/rubyplot/artist/axes.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,19 @@ def legend_box_iy
112112
end
113113

114114
# Write an image to a file by communicating with the backend.
115+
# FIXME: (refactor) Currently draw first assigns default colors and then
116+
# performs consolidation etc of the data and then assigns the default X ticks.
117+
# The reason for this is that default labels are needed by the consolidated
118+
# plots when they create the required rectangles etc. However assigning
119+
# default ticks should be a part of the 'assign defaults' step and should
120+
# therefore should be clubbed with the assign labels step.
115121
def draw
122+
set_axes_ranges
123+
normalize_plotting_data
116124
assign_default_label_colors
117125
consolidate_plots
118-
gather_plot_data
119126
configure_title
120127
configure_legends
121-
configure_plotting_data
122128
assign_x_ticks
123129
actually_draw
124130
end
@@ -158,7 +164,9 @@ def dot! *args, &block
158164
end
159165

160166
def stacked_bar! *args, &block
161-
appdd_plot "StackedBar", *args, &block
167+
plot = Rubyplot::Artist::Plot::StackedBar.new self
168+
yield(plot) if block_given?
169+
@plots << plot
162170
end
163171

164172
def write file_name
@@ -274,7 +282,7 @@ def configure_legends
274282

275283
# Make adjustments to the data that will be plotted. Maps the data
276284
# contained in the plot to actual pixel values.
277-
def configure_plotting_data
285+
def normalize_plotting_data
278286
@plots.each do |plot|
279287
plot.normalize
280288
end
@@ -341,7 +349,7 @@ def consolidate_plots
341349
end
342350

343351
# FIXME: replace x_range and y_range with XAxis::max/min_value and YAxis::max/min_value.
344-
def gather_plot_data
352+
def set_axes_ranges
345353
set_xrange
346354
set_yrange
347355
end

lib/rubyplot/artist/plot.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
require_relative 'plot/line'
44
require_relative 'plot/bar'
55
require_relative 'plot/multi_bars'
6+
require_relative 'plot/stacked_bar'
7+
require_relative 'plot/multi_stacked_bar'
68
require_relative 'plot/bubble'
79
require_relative 'plot/area'

lib/rubyplot/artist/plot/multi_stacked_bar.rb

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,41 @@ module Rubyplot
22
module Artist
33
module Plot
44
class MultiStackedBar < Artist::Plot::Base
5-
def initialize(*, stacked_bars:)
6-
super
5+
def initialize(*args, stacked_bars:)
6+
super(args[0])
77
@stacked_bars = stacked_bars
88
@x_min = @stacked_bars.map(&:x_min).min
99
@y_min = @stacked_bars.map(&:y_min).min
1010
@x_max = @stacked_bars.map(&:x_max).max
1111
@y_max = @stacked_bars.map(&:y_max).max
12+
reset_axes_ranges
13+
renormalize_data
1214
configure_plot_geometry_data
1315
configure_x_ticks
1416
end
1517

1618
def draw
17-
19+
@stacked_bars.each(&:draw)
1820
end
1921

2022
private
2123

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+
2237
def configure_plot_geometry_data
2338
@num_max_slots = @stacked_bars.map { |bar| bar.num_bars }.max
24-
@max_slot_width = (@axes.x_axis.abs_x2 - @axes.x_axis.abs_x1).abs / @num_max_slots
39+
@max_slot_width = @axes.x_axis.length / @num_max_slots
2540
@spacing_ratio = @stacked_bars[0].spacing_ratio
2641
@padding = @spacing_ratio * @max_slot_width
2742
@max_bars_width = @max_slot_width - @padding
@@ -32,15 +47,31 @@ def configure_plot_geometry_data
3247
end
3348

3449
def configure_x_ticks
35-
50+
@axes.num_x_ticks = @num_max_slots
51+
labels = @axes.x_ticks || Array.new(@num_max_slots) { |i| i.to_s }
52+
if labels.size != @axes.num_x_ticks
53+
labels = labels[0...@axes.num_x_ticks]
54+
end
55+
@axes.x_ticks = labels.map.with_index do |label, i|
56+
Rubyplot::Artist::XTick.new(
57+
@axes,
58+
abs_x: @axes.abs_x + @axes.y_axis_margin + i * @max_slot_width + @max_slot_width / 2,
59+
abs_y: @axes.origin[1],
60+
label: label,
61+
length: 6,
62+
label_distance: 10
63+
)
64+
end
3665
end
3766

3867
def set_bar_dims bar, plot_index
3968
bar.bar_width = @max_bars_width
69+
plots_below = @stacked_bars[0...plot_index]
4070
bar.num_bars.times do |i|
71+
pedestal_height = plots_below.map { |p| p.member_height(i) }.inject(:+) || 0
4172
bar.abs_x_left[i] = @axes.abs_x + @axes.y_axis_margin +
42-
i * @max_bars_width + @padding / 2
43-
bar.abs_y_left[i] =
73+
i * @max_slot_width + @padding / 2
74+
bar.abs_y_left[i] = (@axes.abs_y + @axes.y_axis.length) - pedestal_height
4475
end
4576
end
4677
end # class StackedBar

lib/rubyplot/artist/plot/stacked_bar.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ def initialize(*)
1616
@spacing_ratio = 0.1
1717
@abs_x_left = []
1818
@abs_y_left = []
19+
@rectangles = []
20+
@renormalized_y_values = []
1921
end
2022

2123
def data y_values
@@ -26,6 +28,20 @@ def num_bars
2628
@data[:y_values].size
2729
end
2830

31+
# Normalize this data w.r.t other plots in this graph.
32+
#
33+
# @param max_plots [Integer] Max number of stacked bars in this plot.
34+
def renormalize max_plots
35+
@renormalized_y_values = @normalized_data[:y_values].map do |iy|
36+
iy / max_plots
37+
end
38+
end
39+
40+
# Return the height in pixels of the bar at 'index'.
41+
def member_height index
42+
@renormalized_y_values[index] * @axes.y_axis.length
43+
end
44+
2945
def draw
3046
setup_bar_rectangles
3147
@rectangles.each(&:draw)
@@ -34,6 +50,18 @@ def draw
3450
private
3551

3652
def setup_bar_rectangles
53+
@renormalized_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
3765
end
3866
end # class StackedBar
3967
end # module Plot

lib/rubyplot/artist/rectangle.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ class Rectangle < Base
99
# @param abs_y [Float] Absolute Y co-ordinate of the upper left corner.
1010
# @param width [Float] Width in pixels of the rectangle.
1111
# @param height [Float] Height in pixels of the rectangle.
12+
# @param border_color [Symbol] Symbol from Rubyplot::Color::COLOR_INDEX
13+
# denoting border color.
14+
# @param fill_color [Symbol] nil Symbol from Rubyplot::Color::COLOR_INDEX
15+
# denoting the fill color.
1216
def initialize(owner,abs_x:,abs_y:,width:,height:,border_color:,fill_color: nil)
1317
super(owner.backend, abs_x, abs_y)
1418
@height = height

0 commit comments

Comments
 (0)