Class: CTioga::SubPlot

Inherits:
Container show all
Defined in:
lib/CTioga/elements/containers.rb

Overview

This class represent a subplot, a subfigure or the main plot.

Direct Known Subclasses

SharedAxisPlot

Instance Attribute Summary collapse

Attributes inherited from Container

#accept_legend, #disable_legend, #elements, #force_position, #funcalls, #layout, #layout_preferences, #rescale, #root_frame, #show_legend

Attributes inherited from TiogaElement

#parent

Instance Method Summary collapse

Methods inherited from Container

#add_elem, #add_funcall, #add_legend_info, #convert_layout, #display_legend?, #get_boundaries, #has_plots?, #internal_get_boundaries, #make_funcalls, #method_missing

Methods inherited from TiogaElement

#inspect, #need_style?

Methods included from Log

#identify, #init_logger, #logger, #logger_options, #spawn

Methods included from Debug

#debug_figmaker, #debug_patterns, #debug_puts, #figmaker_options, #test_pattern, #test_pattern_right

Constructor Details

#initialize(type = :subplot, parent = nil) ⇒ SubPlot

Returns a new instance of SubPlot.



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/CTioga/elements/containers.rb', line 243

def initialize(type = :subplot, parent = nil)
  super(parent)
  # @type is the type of the element, :subfigure, :subplot or nil
  # to indicate that it is the main plot ;-) !

  # the direct parent of that element. If nil, it means that it's
  # directly a subchild of the main plot.
  @parent = parent

  # Defaults to no margins (looks somehow less nice in many cases)
  @plot_margins = [0.0,0.0,0.0,0.0]
  
  # Some legend tweaking:
  if type == :subplot || type == nil
    @show_legend = true
    @accept_legend = true
  else
    @show_legend = false
    @accept_legend = false
  end

#       @title = Label.new(:title)
#       @title.label = "A nice plot" unless parent

#       @xlabel = Label.new(:xlabel)
#       @xlabel.label = "$x$" unless parent

#       @ylabel = Label.new(:ylabel)
#       @ylabel.label = "$y$" unless parent

#       @xticks = TickLabels.new(:xaxis_numeric_label)
#       @yticks = TickLabels.new(:yaxis_numeric_label)


  # the hash used by show_plot_with_legend: we inherit it from the parent
  # if it exists
  @legend_specs = if parent 
                    parent.legend_specs.dup
                  else
                    {}
                  end
 
  # User-specified boundaries (left, right, top, bottom)
  @bounds = [nil,nil,nil,nil]
  @effective_bounds = nil

#      @edges = EdgesAndAxes.new(@xticks, @yticks)

  @plot_style = PlotStyle.new(self, parent ? true : false)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class CTioga::Container

Instance Attribute Details

#effective_boundsObject (readonly)

The actual boundaries used for making the plot. This element is only filled in at the exact moment of the plotting. It is provided so that subplots or curves may use it.



214
215
216
# File 'lib/CTioga/elements/containers.rb', line 214

def effective_bounds
  @effective_bounds
end

#legend_specsObject

various important things:



209
210
211
# File 'lib/CTioga/elements/containers.rb', line 209

def legend_specs
  @legend_specs
end

#plot_margins(specs) ⇒ Object

Returns a purified version of #legend_specs with only plot margins



218
219
220
# File 'lib/CTioga/elements/containers.rb', line 218

def plot_margins
  @plot_margins
end

#plot_styleObject

A PlotSyle object carrying information about the style of the plot, such as titles/legends/background color…



222
223
224
# File 'lib/CTioga/elements/containers.rb', line 222

def plot_style
  @plot_style
end

Instance Method Details

#bound_bottom=(a) ⇒ Object



327
328
329
# File 'lib/CTioga/elements/containers.rb', line 327

def bound_bottom=(a)
  @bounds[3] = a
end

#bound_left=(a) ⇒ Object



315
316
317
# File 'lib/CTioga/elements/containers.rb', line 315

def bound_left=(a)
  @bounds[0] = a
end

#bound_right=(a) ⇒ Object



319
320
321
# File 'lib/CTioga/elements/containers.rb', line 319

def bound_right=(a)
  @bounds[1] = a
end

#bound_top=(a) ⇒ Object



323
324
325
# File 'lib/CTioga/elements/containers.rb', line 323

def bound_top=(a)
  @bounds[2] = a
end

#compute_boundariesObject

this function computes the boundaries for the given plot…



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/CTioga/elements/containers.rb', line 350

def compute_boundaries
  # We want to use the internal version of get_boundaries
  bounds = internal_get_boundaries

  # Increase the boundary with the margins
  width = bounds[1] - bounds[0]
  bounds[0] -= width * @plot_margins[0]
  bounds[1] += width * @plot_margins[1]
  height = bounds[2] - bounds[3]
  bounds[2] += height * @plot_margins[2]
  bounds[3] -= height * @plot_margins[3]

  # overriding with user-specified values if applicable
  nb_nans = 0
  4.times do |i|
    if @bounds[i] 
      bounds[i] = @bounds[i] 
    end
    # Replace NaNs with 0
    if bounds[i].nan?
      bounds[i] = 0 
      nb_nans += 1
    end
  end
  # In the case all boundaries are NaN, we provide a 0->1 mapping
  if nb_nans == 4
    return [0,1,1,0]
  end
  return bounds
end

#disable_axis(axis) ⇒ Object

Used by children to notify that an axis shoudn’t be used anymore.

TODO: move this to plot_style…



335
336
337
338
339
340
341
342
# File 'lib/CTioga/elements/containers.rb', line 335

def disable_axis(axis)
  case axis
  when :x
    add_funcall(TiogaFuncall.new(:top_edge_type=, AXIS_LINE_ONLY))
  when :y
    add_funcall(TiogaFuncall.new(:right_edge_type=, AXIS_LINE_ONLY))
  end
end

#do(t) ⇒ Object

this function sets up the appropriate environment and calls the block…



481
482
483
484
# File 'lib/CTioga/elements/containers.rb', line 481

def do(t)
  debug "Making main block for object: #{identify(self)}"
  make_main_block(t).call
end

#has_legends?Boolean

Does the plot have legends ?

Returns:

  • (Boolean)


382
383
384
# File 'lib/CTioga/elements/containers.rb', line 382

def has_legends?
  return (not @legend_info.empty?)
end

#inner_frame(t) ⇒ Object

Returns the inner frames of the object in terms of absolute big points.



296
297
298
299
300
301
302
303
# File 'lib/CTioga/elements/containers.rb', line 296

def inner_frame(t)
  frame = outer_frame(t)

  h = t.legend_defaults.dup
  h.update @legend_specs
  frame = Utils::apply_margin_to_frame(frame, h) if display_legend?
  return frame
end

#make_main_block(t) ⇒ Object

this function takes a FigureMaker object and returns the block of instructions corresponding to the current contents of the SubPlot. To actually plot it correctly, it just needs a further wrapping in subplot or subfigure.



392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/CTioga/elements/containers.rb', line 392

def make_main_block(t)
  if has_plots?
    block = proc {
      t.context {
        # First compute the boundaries:
        # We first check if the object was required to be somewhere
        if @force_position and !@force_position.empty?
          legend_specs = @force_position.to_frame("plot_%s_margin")
        else
          # Ask the layout.
          legend_specs = @layout.legend_specs(t)
        end
        # We always wrap the plot in a show_plot_with_legend,
        # even if there is no legends. That, anyway, is the
        # job of the layout to see this.
        debug "Plot specs: #{legend_specs.inspect}"
        debug "Plot legends #{display_legend?.inspect}: "
        debug " - @show_legend = #{@show_legend.inspect}"
        debug " - @disable_legend = #{@disable_legend.inspect}"
        debug " - has_plots? = #{has_plots?.inspect}"
        debug " - has_legends? = #{has_legends?.inspect}"
        debug "Reference frame (#{self.class}): #{Utils::frames_str(t)}"

        margins = plot_margins(legend_specs)
        
        t.context do
          # The call has to be wrapped within a
          # context call, as it turns the frame reference into
          # the legend subframe specification afterwards...
          t.set_subframe(margins)
          @effective_bounds = compute_boundaries
          debug "Plot boundaries: #{@effective_bounds.inspect}"
          debug "Box frame (#{self.class}): #{Utils::frames_str(t)}"
          
          @plot_style.show_edges(t, self)
          
          t.show_plot(@effective_bounds) {
            # output frame debug information./
            debug "Plot frame (#{self.class}): #{Utils::frames_str(t)}"
            if root_frame
              debug "Root frame: #{Utils::framespec_str(root_frame)}"
            end
            
            debug "Inner frame: #{Utils::framespec_str(inner_frame(t))}"

            @plot_style.show_background(t, self)
            @plot_style.show_labels(t, self)

            @elements.each do |e|
              e.do(t)
            end
            # We remove all the legends here, as they should all be
            # handled by the children, or forwarded here.
            t.reset_legend_info
            debug "Current figure has #{@legend_info.size} legends"
            debug "Legends #{@legend_info.inspect}"
            #                 for s in @legend_info
            #                   t.save_legend_info(s.legend_info)
            #                 end
          } 
            
          # Now, additional axes, if applicable
          t.context do 
            t.set_bounds(@effective_bounds)
            # Call the stuff for additional axes.
            @plot_style.edges.show_additional_axes(t, self)
          end

        end
        @plot_style.legend_style.show_legends(t, legend_specs, 
                                              margins, @legend_info)
      }
    }
  else
    block = proc do
      @elements.each do |e|
        e.do(t)
      end
    end
  end

  return proc do
    make_funcalls(t)
    block.call
  end

end

#numberObject

# The appearance of the edgesax

attr_accessor :edges


228
229
230
# File 'lib/CTioga/elements/containers.rb', line 228

def number
  return @elements.length
end

#outer_frame(t) ⇒ Object

The outer frame is the reference frame for the object. It is either the root frame (the whole plot) or the inner frame of the parent object. Objects should refrain from writing anything outside this outer frame.



309
310
311
312
313
# File 'lib/CTioga/elements/containers.rb', line 309

def outer_frame(t)
  # The outer frame is either  the root frame
  # or the inner_frame of the parent.
  return @root_frame || parent.inner_frame(t)
end

#set_margin(which, what) ⇒ Object



345
346
347
# File 'lib/CTioga/elements/containers.rb', line 345

def set_margin(which, what)
  @margins[which.to_s] = what.to_f
end