~ubuntu-branches/ubuntu/oneiric/ctioga2/oneiric

« back to all changes in this revision

Viewing changes to lib/ctioga2/graphics/elements/subplot.rb

  • Committer: Bazaar Package Importer
  • Author(s): Vincent Fourmond
  • Date: 2011-01-24 21:36:06 UTC
  • Revision ID: james.westby@ubuntu.com-20110124213606-9ettx0ugl83z0bzp
Tags: upstream-0.1
ImportĀ upstreamĀ versionĀ 0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# subplot.rb: a subplot
 
2
# copyright (c) 2006, 2007, 2008, 2009 by Vincent Fourmond
 
3
  
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
  
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details (in the COPYING file).
 
13
 
 
14
require 'ctioga2/utils'
 
15
require 'ctioga2/log'
 
16
 
 
17
module CTioga2
 
18
 
 
19
  Version::register_svn_info('$Revision: 196 $', '$Date: 2010-11-25 20:15:32 +0100 (Thu, 25 Nov 2010) $')
 
20
 
 
21
  module Graphics
 
22
 
 
23
    module Elements
 
24
 
 
25
      # A subplot. It features:
 
26
      # * inclusion of curves
 
27
      # * legends
 
28
      # * a way to set/get its figure boundaries.
 
29
      class Subplot < Container
 
30
 
 
31
        # Various stylistic aspects of the plot, as a
 
32
        # Styles::PlotStyle object.
 
33
        attr_accessor :style
 
34
 
 
35
        # User-specified boundaries. It is a hash axis -> SimpleRange,
 
36
        # where the axis is a valid return value of PlotStyle#get_axis_key
 
37
        attr_accessor :user_boundaries
 
38
 
 
39
        # Computed boundaries. It also is a hash axis -> SimpleRange,
 
40
        # just as #user_boundaries. Its value is not defined as long
 
41
        # as #real_do hasn't been entered into.
 
42
        attr_accessor :computed_boundaries
 
43
 
 
44
        def initialize(parent, root, style)
 
45
          super(parent, root)
 
46
 
 
47
          @subframe = Types::MarginsBox.new("2.8dy", "2.8dy", 
 
48
                                            "2.8dy", "2.8dy")
 
49
 
 
50
          @subframe = nil       # Automatic by default.
 
51
 
 
52
          @style = style || Styles::PlotStyle.new
 
53
 
 
54
          @user_boundaries = {}
 
55
        end
 
56
 
 
57
        # Returns the boundaries that apply for the given _curve_ --
 
58
        # it reads the curve's axes. #compute_boundaries must have
 
59
        # been called beforehand, which means that it will only work
 
60
        # from within #real_do.
 
61
        #
 
62
        # \todo This should not only apply to curves, but to any
 
63
        # object. That also means that there should be a way to
 
64
        # specify axes for them too.
 
65
        def get_el_boundaries(el)
 
66
          return get_given_boundaries(* el.location.get_axis_keys(style))
 
67
        end
 
68
 
 
69
        # Returns the boundaries of the *default* axes. Plotting
 
70
        # functions may safely assume that they are drawn using these
 
71
        # boundaries, unless they asked for being drawn onto different
 
72
        # axes.
 
73
        def get_boundaries
 
74
          return get_given_boundaries(style.xaxis_location, 
 
75
                                      style.yaxis_location)       
 
76
        end 
 
77
 
 
78
        # Sets the user boundaries for the given (named) axis:
 
79
        def set_user_boundaries(axis, bounds)
 
80
          key = @style.get_axis_key(axis)
 
81
          @user_boundaries[key] = Types::SimpleRange.new(bounds)
 
82
        end
 
83
 
 
84
        def actual_subframe(t)
 
85
          return @subframe || @style.estimate_margins(t)
 
86
        end
 
87
 
 
88
        # In general, subplot's boundaries do not count for the parent
 
89
        # plot.
 
90
        def count_boundaries?
 
91
          return false
 
92
        end
 
93
 
 
94
        protected
 
95
 
 
96
        # Makes up a Boundaries object from two axes keys
 
97
        def get_given_boundaries(horiz, vert)
 
98
          if @computed_boundaries
 
99
            return Types::Boundaries.from_ranges(@computed_boundaries[horiz],
 
100
                                                 @computed_boundaries[vert])
 
101
          else
 
102
            return nil
 
103
          end
 
104
        end
 
105
 
 
106
        def compute_boundaries
 
107
          # raw boundaries
 
108
          bounds = get_elements_boundaries
 
109
          if @style.plot_margin
 
110
            for k,b in bounds
 
111
              b.apply_margin!(@style.plot_margin)
 
112
            end
 
113
          end
 
114
          for k,b in @user_boundaries
 
115
            bounds[k] ||= Types::SimpleRange.new(nil,nil)
 
116
            bounds[k].override(b)
 
117
          end
 
118
          return bounds
 
119
        end
 
120
 
 
121
 
 
122
 
 
123
        # Plots all the objects inside the plot.
 
124
        def real_do(t)
 
125
          # First thing, we setup the boundaries
 
126
          @computed_boundaries = compute_boundaries
 
127
 
 
128
          real_boundaries = get_boundaries
 
129
 
 
130
          frames = actual_subframe(t)
 
131
 
 
132
          # We wrap the call within a subplot
 
133
          t.subplot(frames.to_frame_margins(t)) do
 
134
 
 
135
            # Setup various aspects of the figure maker object.
 
136
            @style.setup_figure_maker(t)
 
137
            
 
138
            # Manually creating the plot:
 
139
            t.set_bounds(real_boundaries.to_a)
 
140
 
 
141
            # Drawing the background elements:
 
142
            t.context do
 
143
              t.clip_to_frame
 
144
 
 
145
              @style.background.draw_background(t)
 
146
 
 
147
              @style.draw_all_background_lines(t)
 
148
              i = 0
 
149
              for element in @elements 
 
150
                t.context do 
 
151
                  t.set_bounds(get_el_boundaries(element).to_a)
 
152
                  element.do(t)
 
153
                end
 
154
                i += 1
 
155
              end
 
156
            end
 
157
            @style.draw_all_axes(t, @computed_boundaries)
 
158
 
 
159
            # Now drawing legends:
 
160
            if @legend_area
 
161
              a, b = @legend_area.partition_frame(t, self)
 
162
              t.context do 
 
163
                t.set_subframe(b) 
 
164
                @legend_area.display_legend(t, self)
 
165
              end
 
166
            end
 
167
          end
 
168
        end
 
169
 
 
170
        
 
171
        # Returns the boundaries of all the elements of this plot.
 
172
        def get_elements_boundaries
 
173
          boundaries = {}
 
174
          for el in @elements
 
175
            if el.respond_to? :get_boundaries
 
176
              if el.respond_to?(:count_boundaries?) && ! (el.count_boundaries?)
 
177
                # Ignoring
 
178
              else
 
179
                bounds = el.get_boundaries
 
180
                xaxis, yaxis = *el.location.get_axis_keys(style)
 
181
                if bounds.is_a? Hash
 
182
                  ## \todo see if there will ever be a need for a hash
 
183
                  ## ?
 
184
                  raise "Not done yet"
 
185
                else
 
186
                  boundaries[xaxis] ||= Types::SimpleRange.new(nil,nil)
 
187
                  boundaries[xaxis].extend(bounds.horizontal)
 
188
                  boundaries[yaxis] ||= Types::SimpleRange.new(nil,nil)
 
189
                  boundaries[yaxis].extend(bounds.vertical)
 
190
                end
 
191
              end
 
192
            end
 
193
          end
 
194
          return boundaries
 
195
        end
 
196
 
 
197
      end
 
198
    end
 
199
  end
 
200
end