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.
- checksums.yaml +5 -5
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +133 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.md +9 -0
- data/CONTRIBUTING.md +48 -0
- data/Gemfile +6 -0
- data/README.md +47 -0
- data/Rakefile +32 -0
- data/ext/grruby/extconf.rb +6 -0
- data/ext/grruby/grruby.c +1163 -0
- data/ext/grruby/grruby.h +135 -0
- data/lib/rubyplot.rb +30 -1
- data/lib/rubyplot/artist.rb +13 -0
- data/lib/rubyplot/artist/axes.rb +328 -0
- data/lib/rubyplot/artist/axis.rb +3 -0
- data/lib/rubyplot/artist/axis/base.rb +34 -0
- data/lib/rubyplot/artist/axis/x_axis.rb +35 -0
- data/lib/rubyplot/artist/axis/y_axis.rb +40 -0
- data/lib/rubyplot/artist/base.rb +14 -0
- data/lib/rubyplot/artist/circle.rb +28 -0
- data/lib/rubyplot/artist/figure.rb +90 -0
- data/lib/rubyplot/artist/legend.rb +59 -0
- data/lib/rubyplot/artist/legend_box.rb +89 -0
- data/lib/rubyplot/artist/line2d.rb +24 -0
- data/lib/rubyplot/artist/plot.rb +9 -0
- data/lib/rubyplot/artist/plot/area.rb +38 -0
- data/lib/rubyplot/artist/plot/bar.rb +69 -0
- data/lib/rubyplot/artist/plot/bar_type.rb +31 -0
- data/lib/rubyplot/artist/plot/base.rb +67 -0
- data/lib/rubyplot/artist/plot/bubble.rb +41 -0
- data/lib/rubyplot/artist/plot/line.rb +61 -0
- data/lib/rubyplot/artist/plot/multi_bars.rb +75 -0
- data/lib/rubyplot/artist/plot/multi_stacked_bar.rb +78 -0
- data/lib/rubyplot/artist/plot/scatter.rb +29 -0
- data/lib/rubyplot/artist/plot/stacked_bar.rb +69 -0
- data/lib/rubyplot/artist/polygon.rb +21 -0
- data/lib/rubyplot/artist/rectangle.rb +39 -0
- data/lib/rubyplot/artist/text.rb +49 -0
- data/lib/rubyplot/artist/tick.rb +3 -0
- data/lib/rubyplot/artist/tick/base.rb +35 -0
- data/lib/rubyplot/artist/tick/x_tick.rb +25 -0
- data/lib/rubyplot/artist/tick/y_tick.rb +24 -0
- data/lib/rubyplot/backend.rb +2 -0
- data/lib/rubyplot/backend/gr_wrapper.rb +7 -0
- data/lib/rubyplot/backend/magick_wrapper.rb +141 -0
- data/lib/rubyplot/color.rb +992 -0
- data/lib/rubyplot/figure.rb +2 -0
- data/lib/rubyplot/spi.rb +8 -0
- data/lib/rubyplot/subplot.rb +4 -0
- data/lib/rubyplot/themes.rb +47 -0
- data/lib/rubyplot/utils.rb +14 -0
- data/lib/rubyplot/version.rb +1 -1
- data/rubyplot.gemspec +10 -0
- data/spec/axes_spec.rb +477 -0
- data/spec/figure_spec.rb +12 -0
- data/spec/spec_helper.rb +62 -0
- data/spec/spi/multi_plot_graph_spec.rb +33 -0
- data/spec/spi/single_plot_graph_spec.rb +227 -0
- data/spec/spi/subplots_spec.rb +55 -0
- 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
|