From d2839f58394117b57e47dd765c388005808295ed Mon Sep 17 00:00:00 2001 From: arzoo14 Date: Thu, 18 Apr 2019 00:40:22 +0530 Subject: [PATCH] coding_work --- lib/rubyplot/artist/axes.rb | 116 ++++++++++++++++++++++-- lib/rubyplot/artist/axis.rb | 2 + lib/rubyplot/artist/axis/x_axis.rb | 2 +- lib/rubyplot/artist/axis/x_dash_axis.rb | 35 +++++++ lib/rubyplot/artist/axis/y_axis.rb | 2 +- lib/rubyplot/artist/axis/y_dash_axis.rb | 40 ++++++++ lib/rubyplot/artist/plot/line.rb | 5 +- lib/rubyplot/artist/plot/multi_bars.rb | 4 +- lib/rubyplot/artist/tick.rb | 2 + lib/rubyplot/artist/tick/x_dash_tick.rb | 27 ++++++ lib/rubyplot/artist/tick/y_dash_tick.rb | 26 ++++++ lib/rubyplot/artist/tick/y_tick.rb | 4 +- lib/rubyplot/backend/base.rb | 10 ++ lib/rubyplot/backend/gr_wrapper.rb | 94 +++++++++++++++++++ 14 files changed, 353 insertions(+), 16 deletions(-) create mode 100644 lib/rubyplot/artist/axis/x_dash_axis.rb create mode 100644 lib/rubyplot/artist/axis/y_dash_axis.rb create mode 100644 lib/rubyplot/artist/tick/x_dash_tick.rb create mode 100644 lib/rubyplot/artist/tick/y_dash_tick.rb create mode 100644 lib/rubyplot/backend/base.rb create mode 100644 lib/rubyplot/backend/gr_wrapper.rb diff --git a/lib/rubyplot/artist/axes.rb b/lib/rubyplot/artist/axes.rb index d2710c2..f91f9e6 100644 --- a/lib/rubyplot/artist/axes.rb +++ b/lib/rubyplot/artist/axes.rb @@ -19,18 +19,32 @@ class Axes < Base attr_reader :label_stagger_height # Rubyplot::Artist::XAxis object. attr_reader :x_axis + # Rubyplot::Artist::XDashAxis object. + attr_reader :x_dash_axis + # Rubyplot::Artist::YAxis object. attr_reader :y_axis +# Rubyplot::Artist::YDASHAxis object. + attr_reader :y_dash_axis + # Array of X ticks. attr_reader :x_ticks + # Array of X dash ticks. + attr_reader :x_dash_ticks # Array of Y ticks. attr_reader :y_ticks + # Array of Y dash ticks. + attr_reader :y_dash_ticks # Array denoting co-ordinates in pixels of the origin of X and Y axes. attr_reader :origin # Number of X ticks. attr_accessor :num_x_ticks + # Number of X dash ticks. + attr_accessor :num_x_dash_ticks # Number of Y ticks. attr_accessor :num_y_ticks + # Number of Y dash ticks. + attr_accessor :num_y_dash_ticks # Position of the legend box. attr_accessor :legend_box_position # Set true if title is to be hidden. @@ -41,6 +55,10 @@ class Axes < Base attr_accessor :y_axis_margin # Range of X axis. attr_accessor :x_range + # Range of X dash axis. + attr_accessor :x_dash_range + # Range of Y dash axis. + attr_accessor :y_dash_range # Range of Y axis. attr_accessor :y_range, :grid, :bounding_box, :title_shift # Main title for this Axes. @@ -55,7 +73,9 @@ def initialize(figure) @x_axis_margin = 40.0 @y_axis_margin = 40.0 @x_range = [nil, nil] + @x_dash_range = [nil, nil] @y_range = [nil, nil] + @y_dash_range = [nil, nil] @title = '' @title_shift = 0 @title_margin = TITLE_MARGIN @@ -80,11 +100,17 @@ def initialize(figure) @origin = [nil, nil] calculate_xy_axes_origin @x_axis = Rubyplot::Artist::XAxis.new(self) + @x_dash_axis = Rubyplot::Artist::XDashAxis.new(self) @y_axis = Rubyplot::Artist::YAxis.new(self) + @y_dash_axis = Rubyplot::Artist::YDashAxis.new(self) @x_ticks = nil + @x_dash_ticks = nil @y_ticks = nil + @y_dash_ticks = nil @num_x_ticks = 5 + @num_x_dash_ticks = 5 @num_y_ticks = 4 + @num_y_dash_ticks = 4 @legend_box_position = :top end @@ -113,6 +139,8 @@ def draw configure_title configure_legends assign_x_ticks + assign_x_dash_ticks + assign_y_dash_ticks assign_y_ticks actually_draw end @@ -185,10 +213,15 @@ def height def x_ticks= x_ticks @x_ticks = x_ticks end - + def x_dash_ticks= x_dash_ticks + @x_dash_ticks = x_dash_ticks + end def y_ticks= y_ticks @y_ticks = y_ticks end + def y_dash_ticks= y_dash_ticks + @y_dash_ticks = y_dash_ticks + end def x_title= x_title @x_axis.title = x_title @@ -210,7 +243,7 @@ def assign_default_label_colors end def assign_x_ticks - @inter_x_ticks_distance = @x_axis.length / (@num_x_ticks.to_f-1) + @inter_x_ticks_distance = @x_axis.length / (@num_x_ticks.to_f - 1) unless @x_ticks value_distance = (@x_range[1] - @x_range[0]) / (@num_x_ticks.to_f - 1) @x_ticks = @num_x_ticks.times.map do |i| @@ -232,6 +265,29 @@ def assign_x_ticks end end + def assign_x_dash_ticks + @inter_x_dash_ticks_distance = @x_dash_axis.length / (@num_x_dash_ticks.to_f - 1) + unless @x_dash_ticks + value_distance = (@x_dash_range[1] - @x_dash_range[0]) / (@num_x_dash_ticks.to_f - 1) + @x_dash_ticks = @num_x_dash_ticks.times.map do |i| + @x_dash_range[0] + i * value_distance + end + end + + unless @x_dash_ticks.all? { |t| t.is_a?(Rubyplot::Artist::XDashTick) } + @x_dash_ticks.map!.with_index do |tick_label, i| + Rubyplot::Artist::XDashTick.new( + self, + abs_x: -i * @inter_x_dash_ticks_distance + @x_dash_axis.abs_x1, + abs_y: @origin[1], + label: Rubyplot::Utils.format_label(-tick_label), + length: 6, + label_distance: 10 + ) + end + end + end + def assign_y_ticks unless @y_ticks val_distance = (@y_range[1] - @y_range[0]).abs / @num_y_ticks.to_f @@ -252,6 +308,26 @@ def assign_y_ticks end end + def assign_y_dash_ticks + unless @y_dash_ticks + val_distance = (@y_dash_range[1] - @y_dash_range[0]).abs / @num_y_dash_ticks.to_f + @y_dash_ticks = (@y_dash_range[0]..@y_dash_range[1]).step(val_distance).map { |i| i } + end + unless @y_dash_ticks.all? { |t| t.is_a?(Rubyplot::Artist::YDashTick) } + inter_ticks_distance = @y_dash_axis.length / (@num_y_dash_ticks - 1) + @y_dash_ticks.map!.with_index do |tick_label, i| + Rubyplot::Artist::YDashTick.new( + self, + abs_x: @origin[0], + abs_y: @y_dash_axis.abs_y1 - (-i * inter_ticks_distance), + label: Rubyplot::Utils.format_label(-tick_label), + length: 6, + label_distance: 50 + ) + end + end + end + def add_plot plot_type, *args, &block plot = with_backend plot_type, *args yield(plot) if block_given? @@ -279,8 +355,8 @@ def configure_title end def calculate_xy_axes_origin - @origin[0] = abs_x + @x_axis_margin - @origin[1] = abs_y + height - @y_axis_margin + @origin[0] = abs_x + width/2 + @origin[1] = abs_y + height/2 end # Figure out co-ordinates of the legends @@ -302,8 +378,12 @@ def normalize_plotting_data def actually_draw @x_axis.draw @x_ticks.each(&:draw) + @y_axis.draw @y_ticks.each(&:draw) - @y_axis.draw + @x_dash_axis.draw + @x_dash_ticks.each(&:draw) + @y_dash_axis.draw + @y_dash_ticks.each(&:draw) @texts.each(&:draw) @legend_box.draw @plots.each(&:draw) @@ -327,25 +407,45 @@ def consolidate_plots def set_axes_ranges set_xrange set_yrange + set_xdashrange + set_ydashrange end def set_xrange if @x_range[0].nil? && @x_range[1].nil? - @x_range[0] = @plots.map(&:x_min).min + @x_range[0] = 0 @x_range[1] = @plots.map(&:x_max).max end @x_axis.min_val = @x_range[0] @x_axis.max_val = @x_range[1] end - + + def set_xdashrange + if @x_dash_range[0].nil? && @x_dash_range[1].nil? + @x_dash_range[0] = 0 + @x_dash_range[1] = @plots.map(&:x_max).max + end + @x_dash_axis.min_val = @x_dash_range[1] + @x_dash_axis.max_val = @x_dash_range[0] + end + def set_yrange if @y_range[0].nil? && @y_range[1].nil? - @y_range[0] = @plots.map { |p| p.y_min }.min + @y_range[0] = 0 @y_range[1] = @plots.map { |p| p.y_max }.max end @y_axis.min_val = @y_range[0] @y_axis.max_val = @y_range[1] end + + def set_ydashrange + if @y_dash_range[0].nil? && @y_dash_range[1].nil? + @y_dash_range[0] = 0 + @y_dash_range[1] = @plots.map { |p| p.y_max }.max + end + @y_dash_axis.min_val = @y_dash_range[0] + @y_dash_axis.max_val = @y_dash_range[1] + end end # class Axes end # moudle Artist end # module Rubyplot diff --git a/lib/rubyplot/artist/axis.rb b/lib/rubyplot/artist/axis.rb index 6484c5d..94773cd 100644 --- a/lib/rubyplot/artist/axis.rb +++ b/lib/rubyplot/artist/axis.rb @@ -1,3 +1,5 @@ require_relative 'axis/base' require_relative 'axis/x_axis' +require_relative 'axis/x_dash_axis' require_relative 'axis/y_axis' +require_relative 'axis/y_dash_axis' diff --git a/lib/rubyplot/artist/axis/x_axis.rb b/lib/rubyplot/artist/axis/x_axis.rb index 5c35e18..eb3b98a 100644 --- a/lib/rubyplot/artist/axis/x_axis.rb +++ b/lib/rubyplot/artist/axis/x_axis.rb @@ -6,7 +6,7 @@ class XAxis < Axis::Base def initialize axes super @abs_x1 = @axes.origin[0] - @abs_x2 = @axes.abs_x + @axes.width - @axes.y_axis_margin + @abs_x2 = @axes.origin[0] + @axes.width/2 - @axes.y_axis_margin @major_ticks_distance = (@abs_x2 - @abs_x1) / @major_ticks_count @length = (@abs_x2 - @abs_x1).abs configure_axis_line diff --git a/lib/rubyplot/artist/axis/x_dash_axis.rb b/lib/rubyplot/artist/axis/x_dash_axis.rb new file mode 100644 index 0000000..fd2e3c5 --- /dev/null +++ b/lib/rubyplot/artist/axis/x_dash_axis.rb @@ -0,0 +1,35 @@ +require_relative 'base' + +module Rubyplot + module Artist + class XDashAxis < Axis::Base + def initialize axes + super + @abs_x1 = @axes.origin[0] + @abs_x2 = @axes.abs_x + @axes.y_axis_margin + @major_ticks_distance = (@abs_x2 - @abs_x1) / @major_ticks_count + @length = (@abs_x2 - @abs_x1).abs + configure_axis_line + end + + private + + def configure_axis_line + @lines << Rubyplot::Artist::Line2D.new( + self, abs_x1: @abs_x1, abs_y1: @axes.origin[1], abs_x2: @abs_x2, abs_y2: @axes.origin[1], + stroke_width: @stroke_width + ) + end + + def configure_title + @texts << Rubyplot::Artist::Text.new( + @title, + self, + pointsize: @axes.marker_font_size, + abs_y: @axes.origin[1] + 20, + abs_x: @axes.origin[0] + (@abs_x2 - @abs_x1)/2 + ) + end + end # class XAxis + end # class Artist +end # module Rubyplot diff --git a/lib/rubyplot/artist/axis/y_axis.rb b/lib/rubyplot/artist/axis/y_axis.rb index ead4ab5..993e2d7 100644 --- a/lib/rubyplot/artist/axis/y_axis.rb +++ b/lib/rubyplot/artist/axis/y_axis.rb @@ -6,7 +6,7 @@ def initialize(*) @abs_x1 = @axes.origin[0] @abs_y1 = @axes.origin[1] @abs_x2 = @axes.origin[0] - @abs_y2 = @axes.origin[1] - (@axes.height - @axes.x_axis_margin) + @abs_y2 = @axes.origin[1] - (@axes.height/2 - @axes.x_axis_margin) @y_ticks = [] @length = (@abs_y1 - @abs_y2).abs configure_axis_line diff --git a/lib/rubyplot/artist/axis/y_dash_axis.rb b/lib/rubyplot/artist/axis/y_dash_axis.rb new file mode 100644 index 0000000..8dfab43 --- /dev/null +++ b/lib/rubyplot/artist/axis/y_dash_axis.rb @@ -0,0 +1,40 @@ +module Rubyplot + module Artist + class YDashAxis < Axis::Base + def initialize(*) + super + @abs_x1 = @axes.origin[0] + @abs_y1 = @axes.origin[1] + @abs_x2 = @axes.origin[0] + @abs_y2 = @axes.origin[1] + (@axes.height/2 - @axes.x_axis_margin) + @y_ticks = [] + @length = (@abs_y1 - @abs_y2).abs + configure_axis_line + end + + private + + def configure_axis_line + @lines << Rubyplot::Artist::Line2D.new( + self, + abs_x1: @abs_x1, + abs_y1: @abs_y1, + abs_x2: @abs_x2, + abs_y2: @abs_y2, + stroke_width: @stroke_width + ) + end + + def configure_title + @texts << Rubyplot::Artist::Text.new( + @title, + self, + rotation: -90.0, + abs_x: @axes.origin[0] - 10, + abs_y: (@abs_y1 - @abs_y2) / 2, + pointsize: @axes.marker_font_size + ) + end + end # class YDashAxis + end # class Artist +end # module Rubyplot diff --git a/lib/rubyplot/artist/plot/line.rb b/lib/rubyplot/artist/plot/line.rb index b40201d..d21578b 100644 --- a/lib/rubyplot/artist/plot/line.rb +++ b/lib/rubyplot/artist/plot/line.rb @@ -36,9 +36,8 @@ def draw_lines iy = @normalized_data[:y_values][idx_ix] next if ix.nil? || iy.nil? - new_x = ix * (@axes.x_axis.abs_x2 - @axes.x_axis.abs_x1).abs + @axes.abs_x + - @axes.y_axis_margin - new_y = (@axes.y_axis.length - iy * @axes.y_axis.length) + @axes.abs_y + new_x = ix * (@axes.x_axis.length) + @axes.origin[0] + new_y = @axes.origin[1] - iy * @axes.y_axis.length unless prev_x.nil? && prev_y.nil? Rubyplot::Artist::Line2D.new( diff --git a/lib/rubyplot/artist/plot/multi_bars.rb b/lib/rubyplot/artist/plot/multi_bars.rb index 22a3ce8..b7c6a63 100644 --- a/lib/rubyplot/artist/plot/multi_bars.rb +++ b/lib/rubyplot/artist/plot/multi_bars.rb @@ -52,7 +52,7 @@ def configure_x_ticks @axes.x_ticks = labels.map.with_index do |label, i| Rubyplot::Artist::XTick.new( @axes, - abs_x: @axes.abs_x + @axes.y_axis_margin + i * @max_slot_width + @max_slot_width / 2, + abs_x: @axes.abs_x + @axes.y_axis_margin*9 + i * @max_slot_width + @max_slot_width / 2, abs_y: @axes.origin[1], label: label, length: 6, @@ -64,7 +64,7 @@ def configure_x_ticks def set_bar_dims bar_plot, index bar_plot.bar_width = @max_bars_width / @bars_per_slot @num_max_slots.times do |i| - bar_plot.abs_x_left[i] = @axes.abs_x + @axes.y_axis_margin + + bar_plot.abs_x_left[i] = @axes.abs_x + @axes.y_axis_margin*9 + i * @max_slot_width + @padding / 2 + index * bar_plot.bar_width bar_plot.abs_y_left[i] = @axes.origin[1] - @axes.x_axis.stroke_width end diff --git a/lib/rubyplot/artist/tick.rb b/lib/rubyplot/artist/tick.rb index c16fea3..cefff9e 100644 --- a/lib/rubyplot/artist/tick.rb +++ b/lib/rubyplot/artist/tick.rb @@ -1,3 +1,5 @@ require_relative 'tick/base' require_relative 'tick/x_tick' +require_relative 'tick/x_dash_tick' require_relative 'tick/y_tick' +require_relative 'tick/y_dash_tick' diff --git a/lib/rubyplot/artist/tick/x_dash_tick.rb b/lib/rubyplot/artist/tick/x_dash_tick.rb new file mode 100644 index 0000000..e0836e8 --- /dev/null +++ b/lib/rubyplot/artist/tick/x_dash_tick.rb @@ -0,0 +1,27 @@ +module Rubyplot + module Artist + class XDashTick < Tick::Base + def initialize(*) + super + @label = Rubyplot::Artist::Text.new( + @label_text.to_s, + @owner, + abs_x: @abs_x - 5, + abs_y: @abs_y + @length + @label_distance, + pointsize: @owner.marker_font_size + ) + end + + def draw + @backend.draw_line( + x1: @abs_x, y1: @abs_y, x2: @abs_x, y2: @abs_y + @length, + stroke_opacity: @tick_opacity, + stroke_width: @tick_width + ) + if @label_text != '-0.00' + @label.draw + end + end + end # class XDashTick + end # class Artist +end # module Rubyplot diff --git a/lib/rubyplot/artist/tick/y_dash_tick.rb b/lib/rubyplot/artist/tick/y_dash_tick.rb new file mode 100644 index 0000000..2657f56 --- /dev/null +++ b/lib/rubyplot/artist/tick/y_dash_tick.rb @@ -0,0 +1,26 @@ +module Rubyplot + module Artist + class YDashTick < Tick::Base + def initialize(*) + super + @label = Rubyplot::Artist::Text.new( + @label_text.to_s, + @owner, + abs_x: @abs_x - 5 - @label_distance, + abs_y: @abs_y + @length, + pointsize: @owner.marker_font_size, + ) + end + + def draw + @backend.draw_line( + x1: @abs_x, y1: @abs_y, x2: @abs_x - @length, y2: @abs_y, + stroke_opacity: @tick_opacity, + stroke_width: @tick_width) + if @label_text !='-0.00' + @label.draw + end + end + end # class YTick + end # module Artist +end # module Rubyplot diff --git a/lib/rubyplot/artist/tick/y_tick.rb b/lib/rubyplot/artist/tick/y_tick.rb index c019a91..51ac5cc 100644 --- a/lib/rubyplot/artist/tick/y_tick.rb +++ b/lib/rubyplot/artist/tick/y_tick.rb @@ -17,7 +17,9 @@ def draw x1: @abs_x, y1: @abs_y, x2: @abs_x - @length, y2: @abs_y, stroke_opacity: @tick_opacity, stroke_width: @tick_width) - @label.draw + if @label_text != '0.00' + @label.draw + end end end # class YTick end # module Artist diff --git a/lib/rubyplot/backend/base.rb b/lib/rubyplot/backend/base.rb new file mode 100644 index 0000000..e46ceb3 --- /dev/null +++ b/lib/rubyplot/backend/base.rb @@ -0,0 +1,10 @@ +module Rubyplot + module Backend + class Base + # Total height and width of the canvas in pixels. + attr_accessor :canvas_height, :canvas_width + + attr_accessor :active_axes, :figure + end # class Base + end # module Backend +end # module Rubyplot diff --git a/lib/rubyplot/backend/gr_wrapper.rb b/lib/rubyplot/backend/gr_wrapper.rb new file mode 100644 index 0000000..d1391fb --- /dev/null +++ b/lib/rubyplot/backend/gr_wrapper.rb @@ -0,0 +1,94 @@ +require_relative '../../grruby.so' + +module Rubyplot + module Backend + # Wrapper around a GR backend. Works differently than the Image Magick wrapper + # since in GR there is no one-one mapping between Rubyplot artists and things + # that are to be drawn on the backend. + class GRWrapper < Base + def initialize + # Mapping between viewports and their respective Axes. + @axes_maps = {} + @file_name = nil + @xspread = Rubyplot::MAX_X.abs + Rubyplot::MIN_X.abs + @yspread = Rubyplot::MAX_Y.abs + Rubyplot::MIN_Y.abs + end + + # Draw X axis for the currently selected Axes. + def draw_x_axis(minor_ticks:, origin:, major_ticks:, tick_size:) + @axes_maps[@active_axes] = { + x_minor_ticks: minor_ticks, + x_origin: origin, + x_major_ticks: major_ticks, + x_tick_size: tick_size + } + end + + # Draw Y axis for currently selected Axes. + def draw_y_axis(minor_ticks:, origin:, major_ticks:, tick_size:) + @axes_maps[@active_axes] = { + y_minor_ticks: minor_ticks, + y_origin: origin, + y_major_ticks: major_ticks, + y_tick_size: tick_size + } + end + + def draw_text + + end + + def draw_line + + end + + def draw + draw_axes + end + + def init_output_device file_name + @file_name = file_name + Rubyplot::GR.beginprint file_name + end + + def stop_output_device + Rubyplot::GR.endprint + end + + def write file_name + draw + end + + private + + # Set the window on the canvas within which the plotting will take place + # and then call the passed block for actual plotting. + def within_window &block + vp_min_x = (@active_axes.abs_x + @active_axes.left_spacing) / @xspread + vp_min_y = (@active_axes.abs_y + @active_axes.bottom_spacing) / @yspread + vp_max_x = (@active_axes.abs_x + @active_axes.width - + @active_axes.right_spacing) / @xspread + vp_max_y = (@active_axes.abs_y + @active_axes.height - + @active_axes.top_spacing) / @yspread + + Rubyplot::GR.setviewport(vp_min_x, vp_max_x, vp_min_y, vp_max_y) + Rubyplot::GR.setwindow( + @active_axes.xrange[0], + @active_axes.xrange[1], + @active_axes.yrange[0], + @active_axes.yrange[1] + ) + block.call + end + + def draw_axes + @axes_maps.each do |k, v| + @active_axes = k + within_window do + # Call Rubyplot::GR.axes with whatever args. + end + end + end + end # class GRWrapper + end # module Backend +end # module Rubyplot