Skip to content

Commit 1791f3d

Browse files
committed
revamped graph calculation architecture
1 parent 0d8982b commit 1791f3d

13 files changed

Lines changed: 186 additions & 140 deletions

File tree

CONTRIBUTING.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
Each Artist contains a `(abs_x, abs_y)` pair that denotes the absolute position of the
55
Artist on the canvas. For `Figure` and `Axes` this pair denotes the top left corner.
66

7-
During the data collection phase (anything before calling `draw`), all the co-ordinates
8-
exist in the form of ratios. The absolute co-ordinates are calculated during the draw
9-
phase. Therefore there should be no code except in the `draw` methods where actual
10-
co-ordinates are calcualted.
7+
The absolute co-ordinates are calculated during the draw phase. Therefore there
8+
should be no code except in the `draw` methods where actual co-ordinates are calcualted.
119

1210
Varible naming conventions:
1311
* All values that are absolute values will be prefixed with `abs_`.

lib/rubyplot/artist/axes.rb

Lines changed: 64 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,28 @@ class Axes < Base
4949
attr_reader :label_stagger_height
5050
# FIXME: possibly disposable attrs
5151
attr_reader :title_caps_height
52-
attr_reader :top_spacing
52+
# Set true if title is to be hidden.
53+
attr_accessor :hide_title
54+
# Margin between the X axis and the bottom of the Axes artist.
55+
attr_accessor :x_axis_margin
56+
# Margin between the Y axis and the left of the Axes artist.
57+
attr_accessor :y_axis_margin
5358

5459
# @param figure [Rubyplot::Figure] Figure object to which this Axes belongs.
5560
def initialize figure
5661
@figure = figure
5762

5863
@x_title = ''
5964
@y_title = ''
65+
@x_axis_margin = 40.0
66+
@y_axis_margin = 40.0
6067
@x_range = [nil, nil]
6168
@y_range = [nil, nil]
6269
@x_tick_count = :default
6370
@y_tick_count = :default
6471

65-
@origin = %i[default default]
66-
@title = nil
72+
@origin = [nil, nil]
73+
@title = ""
6774
@title_shift = 0
6875
@title_margin = TITLE_MARGIN
6976
@text_font = :default
@@ -74,7 +81,7 @@ def initialize figure
7481
@x_ticks = {}
7582
@plots = []
7683

77-
@raw_rows = @width * (@height/@width)
84+
@raw_rows = width * (height/width)
7885

7986
@theme = Rubyplot::Themes::CLASSIC_WHITE
8087
@geometry = Rubyplot::MagickWrapper::Plot::Scatter::Geometry.new
@@ -84,10 +91,8 @@ def initialize figure
8491
@legend_font_size = 20.0
8592
@legend_margin = LEGEND_MARGIN
8693
@title_font_size = 36.0
87-
@scale = @width / @geometry.raw_columns
8894
@backend = @figure.backend
89-
@backend.scale(@scale)
90-
setup_default_theme
95+
#@backend.scale(@scale)
9196
@plot_colors = []
9297
@legends = []
9398
@lines = []
@@ -98,16 +103,13 @@ def initialize figure
98103

99104
# Write an image to a file by communicating with the backend.
100105
def draw
101-
setup_drawing
102-
prepare_legend
103-
prepare_xy_axes
104-
prepare_title
105-
@plots.each(&:draw)
106-
end
107-
108-
# Set the dimensions in pixels of the graph. Format: "widthxheight". so "800x600".
109-
def dim= dim_string
110-
@width, @height = dim_string.split('x').map(&:to_f)
106+
configure_title
107+
calculate_xy_axes_origin
108+
configure_xy_axes
109+
# configure_legends
110+
# configure_plotting_data
111+
# actually_draw
112+
# @plots.each(&:draw)
111113
end
112114

113115
def scatter! *args, &block
@@ -150,21 +152,23 @@ def write file_name
150152

151153
# Absolute X co-ordinate of the Axes. Top left corner.
152154
def abs_x
153-
@figure.top_spacing * @figure.abs_height + @figure.abs_x
155+
@figure.top_spacing * @figure.height + @figure.abs_x
154156
end
155157

156158
# Absolute Y co-ordinate of the Axes. Top left corner.
157159
def abs_y
158-
@figure.top_spacing * @figure.abs_height + @figure.abs_y
160+
@figure.top_spacing * @figure.height + @figure.abs_y
159161
end
162+
160163
# Absolute width of the Axes in pixels.
161-
def abs_width
162-
(@figure.left_spacing + @figure.right_spacing) * @figure.abs_width
164+
def width
165+
(1 - (@figure.left_spacing + @figure.right_spacing)) * @figure.width
163166
end
164167

165168
# Absolute height of the Axes in pixels.
166-
def abs_height
167-
(@figure.top_spacing + @figure.bottom_spacing) * @figure.abs_height
169+
# FIXME: expand for multiple axes on same figure. width too.
170+
def height
171+
(1 - (@figure.top_spacing + @figure.bottom_spacing)) * @figure.height
168172
end
169173

170174
private
@@ -186,15 +190,6 @@ def with_backend plot_type, *args
186190

187191
plot
188192
end
189-
190-
def prepare_xy_axes
191-
@x_axis = Rubyplot::Artist::XAxis.new(
192-
self, @x_title, @x_range[0], @x_range[1])
193-
@y_axis = Rubyplot::Artist::YAxis.new(
194-
self, @y_title, @y_range[0], @y_range[1])
195-
@x_axis.draw
196-
@y_axis.draw
197-
end
198193

199194
def prepare_title
200195
return if @geometry.hide_title || @title.nil?
@@ -207,7 +202,7 @@ def prepare_title
207202
width: @geometry.raw_columns,
208203
font: @font,
209204
color: @font_color,
210-
pointsize: @title_font_size * @scale,
205+
pointsize: @title_font_size,
211206
internal_label: "axes title."
212207
)
213208
@texts << t
@@ -218,24 +213,7 @@ def prepare_legend
218213
@legends = @plots.map(&:create_legend)
219214
@legends.each { |l| l.draw }
220215
end
221-
222-
def setup_default_theme
223-
defaults = {
224-
marker_color: 'white',
225-
font_color: 'black',
226-
background_image: nil
227-
}
228-
@geometry.theme_options = defaults.merge Themes::CLASSIC_WHITE
229-
@marker_color = @geometry.theme_options[:marker_color]
230-
@font_color = @geometry.theme_options[:font_color] || @marker_color
231-
@backend.set_base_image_gradient(
232-
@geometry.theme_options[:background_colors][0],
233-
@geometry.theme_options[:background_colors][1],
234-
@width,
235-
@height,
236-
@geometry.theme_options[:background_direction])
237-
end
238-
216+
239217
# Calculates size of drawable area and generates normalized data.
240218
#
241219
# * line markers
@@ -271,14 +249,10 @@ def normalize
271249
# It calcuates the measurments in pixels to figure out the positioning
272250
# gap pixels of Legends, Labels and Titles from the picture edge.
273251
def setup_graph_measurements
274-
calculate_font_spaces
275-
calculate_top_spacing
276-
calculate_left_spacing
277-
calculate_bottom_spacing
278-
calculate_right_spacing
279252
@marker_caps_height = @backend.caps_height @font, @marker_font_size
280253
@title_caps_height = @geometry.hide_title || @title.nil? ? 0 :
281-
@backend.caps_height(@font, @title_font_size) * @title.lines.to_a.size
254+
@backend.caps_height(@font, @title_font_size) *
255+
@title.lines.to_a.size
282256
@legend_caps_height = @backend.caps_height @font, @legend_font_size
283257

284258
# For now, the labels feature only focuses on the dot graph so it
@@ -337,10 +311,39 @@ def setup_graph_measurements
337311
@graph_height = @graph_bottom - @graph_top
338312
end
339313

340-
# Consider the various fonts that in use in this graph and calculate the
341-
# ratio of the space that they will occupy w.r.t the Axes.
342-
def calculate_font_spaces
343-
314+
# Figure out the co-ordinates of the title text w.r.t Axes.
315+
def configure_title
316+
@title = Rubyplot::Artist::Text.new(
317+
@title,
318+
self,
319+
abs_x: abs_x,
320+
abs_y: abs_y,
321+
font: @font,
322+
color: @font_color,
323+
pointsize: @title_font_size,
324+
internal_label: "axes title."
325+
)
326+
end
327+
328+
def calculate_xy_axes_origin
329+
@origin[0] = abs_x + @x_axis_margin
330+
@origin[1] = abs_y + height - @y_axis_margin
331+
puts "origin: #{@origin}"
332+
puts "h: #{height}"
333+
puts "w: #{width}"
334+
end
335+
336+
# Figure out co-ordinatees of the XAxis and YAxis
337+
def configure_xy_axes
338+
@x_axis = Rubyplot::Artist::XAxis.new(
339+
self, @x_title, @x_range[0], @x_range[1])
340+
@x_axis.draw
341+
# @y_axis = Rubyplot::Artist::YAxis.new(
342+
# self, @y_title, @y_range[0], @y_range[1])
343+
end
344+
345+
# Figure out co-ordinates of the legends
346+
def configure_legends
344347
end
345348

346349
# Return a formatted string representing a number value that should be

lib/rubyplot/artist/axis/base.rb

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,19 @@ module Rubyplot
22
module Artist
33
class Axis
44
class Base
5+
# Length in pixels of the arrow after the last major tick.
6+
FINISH_ARROW_LENGTH = 10.0
7+
58
attr_writer :label, :ticks, :major_ticks_count, :min_val, :max_val, :title
6-
attr_reader :backend
9+
attr_reader :abs_x1, :abs_x2, :abs_y1, :abs_y2, :backend
710

811
def initialize axes, title, min_val, max_val
912
@axes = axes
10-
@title = title.empty? ? '' : title
11-
@major_ticks_distance = 40.0
13+
@title = title
1214
@min_val = min_val
1315
@max_val = max_val
1416
@backend = @axes.backend
1517
end
16-
17-
def draw
18-
Rubyplot::Artist::Line2D.new(
19-
self, x1: @x1, y1: @y1, x2: @x2, y2: @y2, stroke_width: 2.0).draw
20-
end
2118
end # class Base
2219
end # class Axis
2320
end # class Artist

lib/rubyplot/artist/axis/x_axis.rb

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,56 @@ module Artist
55
class XAxis < Axis::Base
66
def initialize(*)
77
super
8-
@x1 = @axes.graph_left
9-
@y1 = @axes.graph_height + @axes.graph_top
10-
@x2 = @axes.graph_width
11-
@y2 = @axes.graph_height + @axes.graph_top
8+
@abs_x1 = @axes.origin[0]
9+
@abs_y1 = @axes.origin[1]
10+
@abs_x2 = @axes.abs_x + @axes.width - @axes.y_axis_margin
11+
@abs_y2 = @axes.origin[1]
12+
@major_ticks_count = 5
13+
@major_ticks_distance = (@abs_x2 - @abs_x1) / @major_ticks_count
1214
@x_ticks = []
13-
@major_ticks_count = ((@x2 - @x1) / @major_ticks_distance).to_i
15+
configure_axis_line
1416
populate_major_x_ticks
15-
set_title
17+
configure_title
1618
end
1719

1820
def draw
19-
super
21+
@line.draw
2022
@x_ticks.each(&:draw)
21-
@title.draw if @title
23+
@title.draw
2224
end
2325

2426
private
2527

28+
def configure_axis_line
29+
@line = Rubyplot::Artist::Line2D.new(
30+
self, abs_x1: @abs_x1, abs_y1: @abs_y1, abs_x2: @abs_x2, abs_y2: @abs_y2,
31+
stroke_width: 1.0)
32+
end
33+
2634
def populate_major_x_ticks
2735
value_distance = (@max_val - @min_val) / @major_ticks_count.to_f
2836
@major_ticks_count.times do |count|
2937
count += 1
3038
@x_ticks << Rubyplot::Artist::XTick.new(
3139
@axes,
32-
x: count * @major_ticks_distance + @x1,
33-
y: @y1,
40+
abs_x: count * @major_ticks_distance + @abs_x1,
41+
abs_y: @abs_y1,
3442
label: (count * value_distance),
3543
length: 6,
3644
label_distance: 10
3745
)
3846
end
3947
end
4048

41-
def set_title
42-
if @title
43-
@title = Rubyplot::Artist::Text.new(
44-
@title,
45-
self,
46-
pointsize: @axes.marker_font_size * @axes.scale,
47-
width: @axes.geometry.raw_columns,
48-
height: 1.0,
49-
y: @axes.graph_bottom + Artist::Axes::LABEL_MARGIN*2 + @axes.marker_caps_height,
50-
x: @axes.graph_left + (@x2-@x1)/2.0,
51-
gravity: :center
52-
)
53-
end
49+
def configure_title
50+
@title = Rubyplot::Artist::Text.new(
51+
@title,
52+
self,
53+
pointsize: @axes.marker_font_size,
54+
abs_y: @axes.origin[1],
55+
abs_x: @axes.origin[0] + (@axes.abs_x + @axes.abs_y)/2,
56+
gravity: :center
57+
)
5458
end
5559
end # class XAxis
5660
end # class Artist

0 commit comments

Comments
 (0)