Skip to content

Commit d40d123

Browse files
committed
standardize Rubyplot coordinate system and allow changes on a per Figure basis
1 parent 3601ea1 commit d40d123

File tree

8 files changed

+69
-42
lines changed

8 files changed

+69
-42
lines changed

CONTRIBUTING.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
Rubyplot assumes that the co-ordinate system has the origin at the bottom left corner
66
of the graph. This helps in keeping all pixel co-ordinates positive values. The bottom
7-
left corner is `(Rubyplot::MIN_X, Rubyplot::MIN_X)` and the upper left corner is
8-
`(Rubyplot::MAX_X, Rubyplot::MAX_Y)`. The backend should be accomodated to work with
7+
left corner is `(Rubyplot.min_x, Rubyplot.min_x)` and the upper left corner is
8+
`(Rubyplot::max_x, Rubyplot.max_y)`. The backend should be accomodated to work with
99
this system. They are also denoted as and . This system is known as the
1010
`Rubyplot Artist Co-ordinates` system internally within the codebase.
1111

@@ -72,6 +72,8 @@ in human-understadable format and uses the appropriate GR primitives to do its j
7272

7373
GR does not allow changing the internal DPI setting as of now (which is 600).
7474
The size of the figure can be set using the `setwsviewport` or `setwswindow` functions.
75+
Therefore, `setwsviewport(0, 6 * 0.0254, 0, 3 * 0.0254)` would create a 6 by 3
76+
inch image at 600 dpi.
7577

7678
## setwindow and setviewport
7779

lib/rubyplot.rb

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,7 @@ module Rubyplot
124124
:hatch
125125
].freeze
126126

127-
# Min. co-ordinates of the lower left corner.
128-
MIN_X = 0
129-
MIN_Y = 0
130-
131-
# Max. co-ordinates of the upper right corner.
132-
MAX_X = 100
133-
MAX_Y = 100
134-
class << self
127+
class << self
135128
def backend
136129
@backend
137130
end
@@ -146,3 +139,4 @@ def set_backend b
146139
end
147140
end
148141
end # module Rubyplot
142+

lib/rubyplot/artist/axes.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ class Axes < Base
3838
attr_accessor :abs_x
3939
# Y co-ordinate of lower left corner of this Axes.
4040
attr_accessor :abs_y
41-
# Width of this Axes object. Between Rubyplot::MIN_X and MAX_X.
41+
# Width of this Axes object. Between Rubyplot.min_x and max_x.
4242
attr_accessor :width
43-
# Height of this Axes object. Between Rubyplot::MIN_Y and MAX_Y.
43+
# Height of this Axes object. Between Rubyplot.min_y and MAX_Y.
4444
attr_accessor :height
4545

4646
# @param figure [Rubyplot::Figure] Figure object to which this Axes belongs.

lib/rubyplot/artist/figure.rb

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module Rubyplot
22
module Artist
33
class Figure < Artist::Base
4-
DEFAULT_TARGET_WIDTH = 800
5-
4+
DEFAULT_CANVAS_DIM = 40.0
5+
66
# Title on the figure.
77
attr_reader :title
88
# Number of Axes objects in the rows.
@@ -29,23 +29,31 @@ class Figure < Artist::Base
2929
attr_reader :marker_color
3030
# Font color specified as a Symbol from Rubyplot::COLOR::COLOR_INDEX.
3131
attr_reader :font_color
32+
# Unit of the figure size.
33+
attr_reader :figsize_unit
34+
attr_reader :max_x
35+
attr_reader :max_y
36+
attr_reader :min_x
37+
attr_reader :min_y
3238

3339
# Initialize a Rubyplot::Artist::Figure object.
3440
# @param height [Integer] nil Height in pixels of the complete Figure.
3541
# @param width [Integer] nil Width in pixels of the complete Figure.
36-
def initialize(height: nil, width: nil)
42+
def initialize(height: nil, width: nil, figsize_unit: :cm)
3743
super(0, 0)
3844
@title = ''
3945
@nrows = 1
4046
@ncols = 1
41-
@width = width || DEFAULT_TARGET_WIDTH
42-
@height = height || @width * 0.75
47+
@width = (width || DEFAULT_CANVAS_DIM).to_f
48+
@height = (height || DEFAULT_CANVAS_DIM).to_f
4349
@top_spacing = 5
4450
@bottom_spacing = 5
4551
@left_spacing = 5
4652
@right_spacing = 5
4753
@subplots = nil
4854
@n = 0
55+
@figsize_unit = figsize_unit
56+
set_rubyplot_artist_coords!
4957
setup_default_theme
5058
add_subplots! @nrows, @ncols
5159
end
@@ -68,12 +76,12 @@ def add_subplots!(nrows, ncols)
6876
# @param ncol [Integer] Y co-ordinate of the subplot.
6977
def add_subplot!(nrow, ncol)
7078
# FIXME: make this work for mutliple subplots.
71-
plottable_width = Rubyplot::MAX_X - (@left_spacing + @right_spacing)
72-
plottable_length = Rubyplot::MAX_Y - (@top_spacing + @bottom_spacing)
79+
plottable_width = (@max_x - (@left_spacing + @right_spacing)).to_f
80+
plottable_length = (@max_y - (@top_spacing + @bottom_spacing)).to_f
7381
@subplots[nrow][ncol] = Rubyplot::Artist::Axes.new(
7482
self,
75-
abs_x: @left_spacing + (plottable_width.to_f / @ncols) * ncol,
76-
abs_y: @bottom_spacing + (plottable_length.to_f / @nrows) * nrow,
83+
abs_x: @left_spacing + (plottable_width / @ncols) * ncol,
84+
abs_y: @bottom_spacing + (plottable_length / @nrows) * nrow,
7785
width: plottable_width / @ncols,
7886
height: plottable_length / @nrows
7987
)
@@ -106,6 +114,20 @@ def write(file_name, device: :file)
106114

107115
private
108116

117+
def set_rubyplot_artist_coords!
118+
@max_x = 100.0
119+
@max_y = 100.0
120+
@min_x = 0.0
121+
@min_y = 0.0
122+
if @height > @width
123+
aspect_ratio = @height / @width
124+
@max_y = @max_y * aspect_ratio
125+
elsif @height < @width
126+
aspect_ratio = @width / @height
127+
@max_x = @max_x * aspect_ratio
128+
end
129+
end
130+
109131
def setup_default_theme
110132
defaults = {
111133
marker_color: :white,

lib/rubyplot/backend/gr_wrapper.rb

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,14 @@ class GRWrapper < Base
147147
def initialize
148148
@axes_map = {} # Mapping between viewports and their respective Axes.
149149
@file_name = nil
150-
@xspread = Rubyplot::MAX_X.abs + Rubyplot::MIN_X.abs
151-
@yspread = Rubyplot::MAX_Y.abs + Rubyplot::MIN_Y.abs
150+
end
151+
152+
def xspread
153+
(@figure.max_x.abs + @figure.min_x.abs).to_f
154+
end
155+
156+
def yspread
157+
(@figure.max_y.abs + @figure.min_y.abs).to_f
152158
end
153159

154160
# Draw X axis for the currently selected Axes.
@@ -348,17 +354,17 @@ def to_rgb color
348354

349355
# Transform a X quantity to Normalized Device Co-ordinates.
350356
def transform_x_ndc coord
351-
coord.to_f / @xspread
357+
coord.to_f / xspread
352358
end
353359

354360
# Transform a Y quantity to Normalized Device Co-ordinates.
355361
def transform_y_ndc coord
356-
coord.to_f / @yspread
362+
coord.to_f / yspread
357363
end
358364

359365
# Transform a quanitity that represents neither X nor Y co-ordinate into NDC.
360366
def transform_avg_ndc coord
361-
coord / ((@xspread + @yspread) / 2)
367+
coord / ((xspread + yspread) / 2)
362368
end
363369

364370
# Set the window on the canvas within which the plotting will take place
@@ -368,13 +374,13 @@ def within_window(abs=false, &block)
368374
GR.setviewport(0,1,0,1)
369375
GR.setwindow(0,1,0,1)
370376
else
371-
vp_min_x = (@active_axes.abs_x + @active_axes.left_margin) / @xspread
372-
vp_min_y = (@active_axes.abs_y + @active_axes.bottom_margin) / @yspread
377+
vp_min_x = (@active_axes.abs_x + @active_axes.left_margin) / xspread
378+
vp_min_y = (@active_axes.abs_y + @active_axes.bottom_margin) / yspread
373379
vp_max_x = (@active_axes.abs_x + @active_axes.width -
374-
@active_axes.right_margin) / @xspread
380+
@active_axes.right_margin) / xspread
375381
vp_max_y = (@active_axes.abs_y + @active_axes.height -
376-
@active_axes.top_margin) / @yspread
377-
382+
@active_axes.top_margin) / yspread
383+
378384
GR.setviewport(vp_min_x, vp_max_x, vp_min_y, vp_max_y)
379385
GR.setwindow(
380386
@active_axes.x_range[0],

lib/rubyplot/backend/magick_wrapper.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,11 @@ def render_gradient(top_color, bottom_color, width, height, direct)
166166

167167
# Transform X co-ordinate.
168168
def transform_x x
169-
(@canvas_width * x) / Rubyplot::MAX_X
169+
(@canvas_width * x) / Rubyplot.max_x
170170
end
171171

172172
def transform_y y
173-
(@canvas_height * (Rubyplot::MAX_Y - y)) / Rubyplot::MAX_Y
173+
(@canvas_height * (Rubyplot.max_y - y)) / Rubyplot.max_y
174174
end
175175

176176
# Transform quantity that depends on X and Y.

spec/axes_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@
303303
axes.x_ticks = [ '5/6', '5/15', '5/24', '5/36' ]
304304
end
305305

306-
it "adds multiple bar plots for wide graph" do
306+
it "adds multiple bar plots for wide graph", focus: true do
307307
@figure = Rubyplot::Figure.new(height: 400, width: 800)
308308
axes = @figure.add_subplot! 0,0
309309
@planet_data.each do |name, nums|
@@ -387,7 +387,7 @@
387387
end
388388

389389
it "adds a simple scatter plot." do
390-
@figure = Rubyplot::Figure.new
390+
@figure = Rubyplot::Figure.new
391391
axes = @figure.add_subplot! 0,0
392392
axes.scatter! do |p|
393393
p.data @x1, @y1

spec/figure_spec.rb

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
describe Rubyplot::Figure do
44
context ".new" do
5-
it "accepts figsize in centimeter (default)", focus: true do
5+
it "accepts figsize in centimeter (default)" do
66
fig = Rubyplot::Figure.new(width: 30, height: 40)
77

88
expect(fig.width).to eq(30)
99
expect(fig.height).to eq(40)
1010
expect(fig.figsize_unit).to eq(:cm)
11+
12+
expect(fig.max_x).to eq(100.0)
13+
expect(fig.max_y).to be_within(0.01).of(133.33)
1114
end
1215

1316
it "accepts figsize in pixels" do
@@ -37,18 +40,18 @@
3740
it "changes Rubyplot Artist Co-ordinates as per aspect ratio." do
3841
fig = Rubyplot::Figure.new(width: 20, height: 20)
3942

40-
expect(Rubyplot.max_x).to eq(100.0)
41-
expect(Rubyplot.max_y).to eq(100.0)
43+
expect(fig.max_x).to eq(100.0)
44+
expect(fig.max_y).to eq(100.0)
4245

4346
fig = Rubyplot::Figure.new(width: 30, height: 20)
4447

45-
expect(Rubyplot.max_x).to eq(150.0)
46-
expect(Rubyplot.max_y).to eq(100.0)
48+
expect(fig.max_x).to eq(150.0)
49+
expect(fig.max_y).to eq(100.0)
4750

4851
fig = Rubyplot::Figure.new(width: 20, height: 30)
4952

50-
expect(Rubyplot.max_x).to eq(100.0)
51-
expect(Rubyplot.max_y).to eq(150.0)
53+
expect(fig.max_x).to eq(100.0)
54+
expect(fig.max_y).to eq(150.0)
5255
end
5356
end
5457

0 commit comments

Comments
 (0)