class CTioga2::Graphics::Elements::Subplot

A subplot. It features:

Attributes

computed_boundaries[RW]

Computed boundaries. It also is a hash axis -> SimpleRange, just as user_boundaries. Its value is not defined as long as real_do hasn't been entered into.

style[RW]

Various stylistic aspects of the plot, as a Styles::PlotStyle object.

user_boundaries[RW]

User-specified boundaries. It is a hash axis -> SimpleRange, where the axis is a valid return value of PlotStyle#get_axis_key

Public Class Methods

new(parent, root, opts) click to toggle source
# File lib/ctioga2/graphics/elements/subplot.rb, line 42
def initialize(parent, root, opts)
  super(parent, root, opts)

  @subframe = Types::MarginsBox.new("2.8dy", "2.8dy", 
                                    "2.8dy", "2.8dy")

  @subframe = nil       # Automatic by default.

  @prev_subframe = nil

  @style = Styles::PlotStyle.new(self)

  @user_boundaries = {}

end

Public Instance Methods

actual_subframe(t) click to toggle source
# File lib/ctioga2/graphics/elements/subplot.rb, line 85
def actual_subframe(t)
  if @subframe
    return @subframe
  else
    if @prev_subframe
      @style.compute_margins(t, @prev_subframe)
    else
      @prev_subframe = @style.estimate_margins(t)
      return @prev_subframe
    end
  end
end
count_boundaries?() click to toggle source

In general, subplot's boundaries do not count for the parent plot.

# File lib/ctioga2/graphics/elements/subplot.rb, line 100
def count_boundaries?
  return false
end
get_boundaries() click to toggle source

Returns the boundaries of the default axes. Plotting functions may safely assume that they are drawn using these boundaries, unless they asked for being drawn onto different axes.

# File lib/ctioga2/graphics/elements/subplot.rb, line 74
def get_boundaries
  return get_given_boundaries(style.xaxis_location, 
                              style.yaxis_location)       
end
get_el_boundaries(el) click to toggle source

Returns the boundaries that apply for the given curve – it reads the curve's axes. compute_boundaries must have been called beforehand, which means that it will only work from within real_do.

todo This should not only apply to curves, but to any object. That also means that there should be a way to specify axes for them too.

# File lib/ctioga2/graphics/elements/subplot.rb, line 66
def get_el_boundaries(el)
  return get_given_boundaries(* el.location.get_axis_keys(style))
end
set_user_boundaries(axis, bounds) click to toggle source

Sets the user boundaries for the given (named) axis:

# File lib/ctioga2/graphics/elements/subplot.rb, line 80
def set_user_boundaries(axis, bounds)
  key = @style.get_axis_key(axis)
  @user_boundaries[key] = Types::SimpleRange.new(bounds)
end

Protected Instance Methods

clip_and_plot(t, elements) click to toggle source
# File lib/ctioga2/graphics/elements/subplot.rb, line 150
def clip_and_plot(t, elements)
  clusters = Utils::cluster_by_value(elements, :clipped)
  
  for clst in clusters
    t.context do
      if clst[0].clipped
        t.clip_to_frame
      end
      for element in clst 
        t.context do 
          t.set_bounds(get_el_boundaries(element).to_a)
          element.do(t)
        end
      end
    end
  end
end
compute_boundaries() click to toggle source
# File lib/ctioga2/graphics/elements/subplot.rb, line 122
def compute_boundaries
  # raw boundaries
  bounds = get_elements_boundaries
  if @style.plot_margin
    for k,b in bounds
      b.apply_margin!(@style.plot_margin)
    end
  end
  for k,b in @user_boundaries
    bounds[k] ||= Types::SimpleRange.new(nil,nil)
    bounds[k].override(b)
  end
  for k, b in bounds
    if ! b.valid?
      if b.nan?
        error { "Invalid computed range, you have NaNs in your data (missing data ?)" }
      elsif b.infinite?
        error { "Infinite computed range, you have infinite numbers in your data" }
      else
        error { "Invalid computed range, you probably have only empty datasets" }
      end
      bounds[k] = Types::SimpleRange.new(0.0,1.0)
    end
  end
  return bounds
end
get_elements_boundaries() click to toggle source

Returns the boundaries of all the elements of this plot.

# File lib/ctioga2/graphics/elements/subplot.rb, line 254
def get_elements_boundaries
  boundaries = {}
  for el in @elements
    if el.respond_to? :get_boundaries
      if el.respond_to?(:count_boundaries?) && ! (el.count_boundaries?)
        # Ignoring
      else
        bounds = el.get_boundaries
        xaxis, yaxis = *el.location.get_axis_keys(style)
        if bounds.is_a? Hash
          ## \todo see if there will ever be a need for a hash
          ## ?
          raise "Not done yet"
        else
          boundaries[xaxis] ||= Types::SimpleRange.new(nil,nil)
          boundaries[xaxis].extend(bounds.horizontal)
          boundaries[yaxis] ||= Types::SimpleRange.new(nil,nil)
          boundaries[yaxis].extend(bounds.vertical)
        end
      end
    end
  end
  return boundaries
end
get_given_boundaries(horiz, vert) click to toggle source

Makes up a Boundaries object from two axes keys

# File lib/ctioga2/graphics/elements/subplot.rb, line 107
def get_given_boundaries(horiz, vert)
  if @computed_boundaries
    if @computed_boundaries.key?(horiz) and 
        @computed_boundaries.key?(vert)
      return Types::Boundaries.from_ranges(@computed_boundaries[horiz],
                                           @computed_boundaries[vert])
    else
      error { "A subplot element doesn't have inner bounds -- which probably means that no curves were defined" }
      return Types::Boundaries.new(0.0,1.0,0.0,1.0)
    end
  else
    return nil
  end
end
real_do(t) click to toggle source

Plots all the objects inside the plot.

# File lib/ctioga2/graphics/elements/subplot.rb, line 170
def real_do(t)
  # First thing, we setup the boundaries
  @computed_boundaries = compute_boundaries

  real_boundaries = get_boundaries

  frames = actual_subframe(t)

  # We wrap the call within a subplot
  t.subplot(frames.to_frame_margins(t)) do


    # Setup various aspects of the figure maker object.
    @style.setup_figure_maker(t)
    
    #
    if @style.frame_real_size
      t.set_bounds([0.0, 1.0, 0.0, 1.0])
      dx = t.convert_figure_to_output_dx(1.0)
      dy = t.convert_figure_to_output_dy(1.0)
      
      frm = [0.0, dx/(@style.frame_real_size * t.scaling_factor), 
             -dy/(@style.frame_real_size * t.scaling_factor), 0.0]
      @computed_boundaries = {
        :bottom => Types::SimpleRange.new(frm[0], frm[1]),
        :left => Types::SimpleRange.new(frm[2], frm[3]),
      }                
      t.set_bounds(frm)
    else
      bnds = real_boundaries.to_a
      t.set_bounds(bnds)
    end

    # First, gather up all elements by depth

    els_by_depth = Utils::sort_by_value(@elements, :depth)

    background = []
    mid = []
    fore = []

    # Organize by depth
    for depth in els_by_depth.keys.sort.reverse
      v = els_by_depth[depth]
      if depth && (depth >= 90)
        background += v
      elsif depth && depth <= 10
        fore += v
      else
        mid += v
      end
    end

    # Drawing the background elements:

    clip_and_plot(t, background)

    t.context do
      t.clip_to_frame

      @style.background.draw_background(t)

      @style.draw_all_background_lines(t)
    end

    clip_and_plot(t, mid)

    @style.draw_all_axes(t, @computed_boundaries)

    clip_and_plot(t, fore)

    # Now drawing legends:
    if @legend_area
      a, b = @legend_area.partition_frame(t, self)
      t.context do 
        t.set_subframe(b) 
        @legend_area.display_legend(t, self)
      end
    end
  end
end