gruff 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +9 -1
 - data/Rakefile +11 -1
 - data/lib/gruff.rb +4 -0
 - data/lib/gruff/bar.rb +0 -1
 - data/lib/gruff/base.rb +162 -12
 - data/lib/gruff/line.rb +3 -4
 - data/lib/gruff/net.rb +133 -0
 - data/lib/gruff/pie.rb +8 -46
 - data/lib/gruff/scene.rb +196 -0
 - data/lib/gruff/spider.rb +130 -0
 - data/test/area_test.rb +2 -6
 - data/test/bar_test.rb +61 -41
 - data/test/base_test.rb +8 -0
 - data/test/gruff_test_case.rb +13 -0
 - data/test/legend_test.rb +71 -0
 - data/test/line_test.rb +13 -54
 - data/test/net_test.rb +230 -0
 - data/test/photo_test.rb +2 -5
 - data/test/pie_test.rb +2 -6
 - data/test/scene_test.rb +94 -0
 - data/test/sidestacked_bar_test.rb +2 -6
 - data/test/spider_test.rb +216 -0
 - data/test/stacked_bar_test.rb +2 -6
 - metadata +54 -37
 
    
        data/CHANGELOG
    CHANGED
    
    | 
         @@ -1,11 +1,19 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            CHANGELOG
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            == 0.1.2
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            * minimum_value and maximum_value can be set after data() to manually scale the graph
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Fixed infinite loop bug when values are all equal
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Added experimental net and spider graphs
         
     | 
| 
      
 8 
     | 
    
         
            +
            * Added non-linear scene graph for a simple interface to complex layered graphs
         
     | 
| 
      
 9 
     | 
    
         
            +
            * Initial refactoring of tests
         
     | 
| 
      
 10 
     | 
    
         
            +
            * A host of other bug fixes
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
       3 
12 
     | 
    
         
             
            == 0.0.8
         
     | 
| 
       4 
13 
     | 
    
         | 
| 
       5 
14 
     | 
    
         
             
            * NEW Sidestacked Bar Graphs. [Alun Eyre]
         
     | 
| 
       6 
15 
     | 
    
         
             
            * baseline_value larger than data will now show correctly. [Mike Perham]
         
     | 
| 
       7 
16 
     | 
    
         
             
            * hide_dots and hide_lines are now options for line graphs.
         
     | 
| 
       8 
     | 
    
         
            -
            * 
         
     | 
| 
       9 
17 
     | 
    
         | 
| 
       10 
18 
     | 
    
         
             
            == 0.0.6
         
     | 
| 
       11 
19 
     | 
    
         | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -28,6 +28,15 @@ task :clean do 
     | 
|
| 
       28 
28 
     | 
    
         
             
              rm_rf 'doc'
         
     | 
| 
       29 
29 
     | 
    
         
             
            end
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
      
 31 
     | 
    
         
            +
            desc "Copy documentation to topfunky.com" 
         
     | 
| 
      
 32 
     | 
    
         
            +
            task :rdoc_deploy => [:rdoc] do
         
     | 
| 
      
 33 
     | 
    
         
            +
              dirs = %w{doc}
         
     | 
| 
      
 34 
     | 
    
         
            +
              onserver = "topfunky@topfunky.com:/home/topfunky/topfunky.com/clients/rails/gruff" 
         
     | 
| 
      
 35 
     | 
    
         
            +
              dirs.each do | dir|
         
     | 
| 
      
 36 
     | 
    
         
            +
                `rsync -avz -e ssh "#{`pwd`.chomp}/#{dir}" "#{onserver}" --exclude ".svn"`
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       31 
40 
     | 
    
         
             
            # Run the unit tests
         
     | 
| 
       32 
41 
     | 
    
         
             
            Rake::TestTask.new { |t|
         
     | 
| 
       33 
42 
     | 
    
         
             
              t.libs << "test"
         
     | 
| 
         @@ -35,7 +44,6 @@ Rake::TestTask.new { |t| 
     | 
|
| 
       35 
44 
     | 
    
         
             
              t.verbose = true
         
     | 
| 
       36 
45 
     | 
    
         
             
            }
         
     | 
| 
       37 
46 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
47 
     | 
    
         
             
            # Genereate the RDoc documentation
         
     | 
| 
       40 
48 
     | 
    
         
             
            Rake::RDocTask.new { |rdoc|
         
     | 
| 
       41 
49 
     | 
    
         
             
              rdoc.rdoc_dir = 'doc'
         
     | 
| 
         @@ -197,3 +205,5 @@ task :release => [:package] do 
     | 
|
| 
       197 
205 
     | 
    
         
             
                end
         
     | 
| 
       198 
206 
     | 
    
         
             
              end
         
     | 
| 
       199 
207 
     | 
    
         
             
            end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
    
        data/lib/gruff.rb
    CHANGED
    
    | 
         @@ -6,6 +6,10 @@ require File.dirname(__FILE__) + '/gruff/area' 
     | 
|
| 
       6 
6 
     | 
    
         
             
            require File.dirname(__FILE__) + '/gruff/bar'
         
     | 
| 
       7 
7 
     | 
    
         
             
            require File.dirname(__FILE__) + '/gruff/line'
         
     | 
| 
       8 
8 
     | 
    
         
             
            require File.dirname(__FILE__) + '/gruff/pie'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require File.dirname(__FILE__) + '/gruff/spider'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require File.dirname(__FILE__) + '/gruff/net'
         
     | 
| 
       9 
11 
     | 
    
         
             
            require File.dirname(__FILE__) + '/gruff/stacked_bar'
         
     | 
| 
       10 
12 
     | 
    
         
             
            require File.dirname(__FILE__) + '/gruff/side_stacked_bar'
         
     | 
| 
       11 
13 
     | 
    
         
             
            require File.dirname(__FILE__) + '/gruff/photo_bar'
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            require File.dirname(__FILE__) + '/gruff/scene'
         
     | 
    
        data/lib/gruff/bar.rb
    CHANGED
    
    
    
        data/lib/gruff/base.rb
    CHANGED
    
    | 
         @@ -9,7 +9,8 @@ 
     | 
|
| 
       9 
9 
     | 
    
         
             
            # and also contributions by
         
     | 
| 
       10 
10 
     | 
    
         
             
            # Jarkko Laine, Mike Perham, Andreas Schwarz, 
         
     | 
| 
       11 
11 
     | 
    
         
             
            # Alun Eyre, Guillaume Theoret, David Stokar, 
         
     | 
| 
       12 
     | 
    
         
            -
            # Paul Rogers, Dave Woodward, Frank Oxener, 
     | 
| 
      
 12 
     | 
    
         
            +
            # Paul Rogers, Dave Woodward, Frank Oxener,
         
     | 
| 
      
 13 
     | 
    
         
            +
            # Kevin Clark, Cies Breijs, Richard Cowin,
         
     | 
| 
       13 
14 
     | 
    
         
             
            # and a cast of thousands.
         
     | 
| 
       14 
15 
     | 
    
         
             
            #
         
     | 
| 
       15 
16 
     | 
    
         | 
| 
         @@ -17,7 +18,7 @@ require 'RMagick' 
     | 
|
| 
       17 
18 
     | 
    
         | 
| 
       18 
19 
     | 
    
         
             
            module Gruff
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
              VERSION = '0.1. 
     | 
| 
      
 21 
     | 
    
         
            +
              VERSION = '0.1.2'
         
     | 
| 
       21 
22 
     | 
    
         | 
| 
       22 
23 
     | 
    
         
             
              class Base
         
     | 
| 
       23 
24 
     | 
    
         | 
| 
         @@ -58,10 +59,32 @@ module Gruff 
     | 
|
| 
       58 
59 
     | 
    
         
             
                # Will be scaled down if graph is smaller than 800px wide.
         
     | 
| 
       59 
60 
     | 
    
         
             
                attr_accessor :legend_font_size
         
     | 
| 
       60 
61 
     | 
    
         | 
| 
      
 62 
     | 
    
         
            +
                # The font size of the labels around the graph
         
     | 
| 
       61 
63 
     | 
    
         
             
                attr_accessor :marker_font_size
         
     | 
| 
       62 
64 
     | 
    
         | 
| 
      
 65 
     | 
    
         
            +
                # The color of the auxiliary labels and lines
         
     | 
| 
      
 66 
     | 
    
         
            +
                attr_accessor :marker_color
         
     | 
| 
      
 67 
     | 
    
         
            +
                
         
     | 
| 
      
 68 
     | 
    
         
            +
                # The font size of the large title at the top of the graph
         
     | 
| 
       63 
69 
     | 
    
         
             
                attr_accessor :title_font_size
         
     | 
| 
       64 
70 
     | 
    
         | 
| 
      
 71 
     | 
    
         
            +
                # You can manually set a minimum value instead of having the values guessed for you.
         
     | 
| 
      
 72 
     | 
    
         
            +
                #
         
     | 
| 
      
 73 
     | 
    
         
            +
                # Set it after you have given all your data to the graph object.
         
     | 
| 
      
 74 
     | 
    
         
            +
                attr_accessor :minimum_value
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                # You can manually set a maximum value, such as a percentage-based graph that always goes to 100.
         
     | 
| 
      
 77 
     | 
    
         
            +
                #
         
     | 
| 
      
 78 
     | 
    
         
            +
                # If you use this, you must set it after you have given all your data to the graph object.
         
     | 
| 
      
 79 
     | 
    
         
            +
                attr_accessor :maximum_value
         
     | 
| 
      
 80 
     | 
    
         
            +
                    
         
     | 
| 
      
 81 
     | 
    
         
            +
                # Experimental
         
     | 
| 
      
 82 
     | 
    
         
            +
                attr_accessor :additional_line_values
         
     | 
| 
      
 83 
     | 
    
         
            +
                
         
     | 
| 
      
 84 
     | 
    
         
            +
                # Experimental
         
     | 
| 
      
 85 
     | 
    
         
            +
                attr_accessor :stacked
         
     | 
| 
      
 86 
     | 
    
         
            +
                
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
       65 
88 
     | 
    
         
             
                # If one numerical argument is given, the graph is drawn at 4/3 ratio according to the given width (800 results in 800x600, 400 gives 400x300, etc.).
         
     | 
| 
       66 
89 
     | 
    
         
             
                #
         
     | 
| 
       67 
90 
     | 
    
         
             
                # Or, send a geometry string for other ratios ('800x400', '400x225'). 
         
     | 
| 
         @@ -100,6 +123,9 @@ module Gruff 
     | 
|
| 
       100 
123 
     | 
    
         | 
| 
       101 
124 
     | 
    
         
             
                  @hide_line_markers = @hide_legend = @hide_title = false
         
     | 
| 
       102 
125 
     | 
    
         | 
| 
      
 126 
     | 
    
         
            +
                  @additional_line_values = []      
         
     | 
| 
      
 127 
     | 
    
         
            +
                  @additional_line_colors = []
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
       103 
129 
     | 
    
         
             
                  reset_themes()
         
     | 
| 
       104 
130 
     | 
    
         
             
                  theme_keynote()
         
     | 
| 
       105 
131 
     | 
    
         
             
                end
         
     | 
| 
         @@ -121,6 +147,57 @@ module Gruff 
     | 
|
| 
       121 
147 
     | 
    
         
             
                  @colors = color_list
         
     | 
| 
       122 
148 
     | 
    
         
             
                end
         
     | 
| 
       123 
149 
     | 
    
         | 
| 
      
 150 
     | 
    
         
            +
                # You can set a theme manually. Assign a hash to this method before you send your data.
         
     | 
| 
      
 151 
     | 
    
         
            +
                #
         
     | 
| 
      
 152 
     | 
    
         
            +
                #  graph.theme = {
         
     | 
| 
      
 153 
     | 
    
         
            +
                #    :colors => %w(orange purple green white red),
         
     | 
| 
      
 154 
     | 
    
         
            +
                #    :marker_color => 'blue',
         
     | 
| 
      
 155 
     | 
    
         
            +
                #    :background_colors => %w(black grey)
         
     | 
| 
      
 156 
     | 
    
         
            +
                #  }
         
     | 
| 
      
 157 
     | 
    
         
            +
                #
         
     | 
| 
      
 158 
     | 
    
         
            +
                # :background_image => 'squirrel.png' is also possible.
         
     | 
| 
      
 159 
     | 
    
         
            +
                #
         
     | 
| 
      
 160 
     | 
    
         
            +
                # (Or hopefully something better looking than that.)
         
     | 
| 
      
 161 
     | 
    
         
            +
                #
         
     | 
| 
      
 162 
     | 
    
         
            +
                def theme=(options)
         
     | 
| 
      
 163 
     | 
    
         
            +
                  defaults = {
         
     | 
| 
      
 164 
     | 
    
         
            +
                    :colors => ['black', 'white'],
         
     | 
| 
      
 165 
     | 
    
         
            +
                    :additional_line_colors => ['grey'],
         
     | 
| 
      
 166 
     | 
    
         
            +
                    :marker_color => 'white',
         
     | 
| 
      
 167 
     | 
    
         
            +
                    :background_colors => nil,
         
     | 
| 
      
 168 
     | 
    
         
            +
                    :background_image => nil
         
     | 
| 
      
 169 
     | 
    
         
            +
                  }
         
     | 
| 
      
 170 
     | 
    
         
            +
                  options = defaults.merge options
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                  reset_themes()
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                  @colors = options[:colors]
         
     | 
| 
      
 175 
     | 
    
         
            +
                  @marker_color = options[:marker_color]
         
     | 
| 
      
 176 
     | 
    
         
            +
                  @additional_line_colors = options[:additional_line_colors]
         
     | 
| 
      
 177 
     | 
    
         
            +
                  if not options[:background_colors].nil?
         
     | 
| 
      
 178 
     | 
    
         
            +
                    @base_image = render_gradiated_background *options[:background_colors]
         
     | 
| 
      
 179 
     | 
    
         
            +
                  else
         
     | 
| 
      
 180 
     | 
    
         
            +
                    @base_image = render_image_background *options[:background_image]
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end
         
     | 
| 
      
 182 
     | 
    
         
            +
                end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                # Add a color to the list of available colors for lines.
         
     | 
| 
      
 185 
     | 
    
         
            +
                #
         
     | 
| 
      
 186 
     | 
    
         
            +
                # Example: 
         
     | 
| 
      
 187 
     | 
    
         
            +
                #  add_color('#c0e9d3')
         
     | 
| 
      
 188 
     | 
    
         
            +
                def add_color(colorname)
         
     | 
| 
      
 189 
     | 
    
         
            +
                  @colors << colorname
         
     | 
| 
      
 190 
     | 
    
         
            +
                end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                # Replace the entire color list with a new array of colors. You need to have one more color
         
     | 
| 
      
 193 
     | 
    
         
            +
                # than the number of datasets you intend to draw. Also aliased as the colors= setter method.
         
     | 
| 
      
 194 
     | 
    
         
            +
                #
         
     | 
| 
      
 195 
     | 
    
         
            +
                # Example: 
         
     | 
| 
      
 196 
     | 
    
         
            +
                #  replace_colors('#cc99cc', '#d9e043', '#34d8a2')
         
     | 
| 
      
 197 
     | 
    
         
            +
                def replace_colors(color_list=[])
         
     | 
| 
      
 198 
     | 
    
         
            +
                  @colors = color_list
         
     | 
| 
      
 199 
     | 
    
         
            +
                end
         
     | 
| 
      
 200 
     | 
    
         
            +
                
         
     | 
| 
       124 
201 
     | 
    
         
             
                # A color scheme similar to the popular presentation software.
         
     | 
| 
       125 
202 
     | 
    
         
             
                def theme_keynote
         
     | 
| 
       126 
203 
     | 
    
         
             
                  reset_themes()
         
     | 
| 
         @@ -228,7 +305,7 @@ module Gruff 
     | 
|
| 
       228 
305 
     | 
    
         
             
                    end
         
     | 
| 
       229 
306 
     | 
    
         
             
                    @minimum_value = less_than_min?(data_point) ? data_point : @minimum_value
         
     | 
| 
       230 
307 
     | 
    
         
             
                    if @minimum_value < 0
         
     | 
| 
       231 
     | 
    
         
            -
             
     | 
| 
      
 308 
     | 
    
         
            +
                 	  @has_data = true
         
     | 
| 
       232 
309 
     | 
    
         
             
                    end
         
     | 
| 
       233 
310 
     | 
    
         
             
                  end
         
     | 
| 
       234 
311 
     | 
    
         
             
                end
         
     | 
| 
         @@ -249,12 +326,50 @@ module Gruff 
     | 
|
| 
       249 
326 
     | 
    
         
             
                  end
         
     | 
| 
       250 
327 
     | 
    
         
             
                end
         
     | 
| 
       251 
328 
     | 
    
         | 
| 
      
 329 
     | 
    
         
            +
                def scale_measurements
         
     | 
| 
      
 330 
     | 
    
         
            +
                  setup_graph_measurements
         
     | 
| 
      
 331 
     | 
    
         
            +
                end
         
     | 
| 
      
 332 
     | 
    
         
            +
                
         
     | 
| 
      
 333 
     | 
    
         
            +
                def total_height
         
     | 
| 
      
 334 
     | 
    
         
            +
                  @rows + 10
         
     | 
| 
      
 335 
     | 
    
         
            +
                end
         
     | 
| 
      
 336 
     | 
    
         
            +
                
         
     | 
| 
      
 337 
     | 
    
         
            +
                def graph_top
         
     | 
| 
      
 338 
     | 
    
         
            +
                  @graph_top * @scale
         
     | 
| 
      
 339 
     | 
    
         
            +
                end
         
     | 
| 
      
 340 
     | 
    
         
            +
                
         
     | 
| 
      
 341 
     | 
    
         
            +
                def graph_height
         
     | 
| 
      
 342 
     | 
    
         
            +
                  @graph_height * @scale
         
     | 
| 
      
 343 
     | 
    
         
            +
                end
         
     | 
| 
      
 344 
     | 
    
         
            +
                
         
     | 
| 
      
 345 
     | 
    
         
            +
                def graph_left 
         
     | 
| 
      
 346 
     | 
    
         
            +
                  @graph_left * @scale 
         
     | 
| 
      
 347 
     | 
    
         
            +
                end
         
     | 
| 
      
 348 
     | 
    
         
            +
                
         
     | 
| 
      
 349 
     | 
    
         
            +
                def graph_width
         
     | 
| 
      
 350 
     | 
    
         
            +
                  @graph_width * @scale
         
     | 
| 
      
 351 
     | 
    
         
            +
                end
         
     | 
| 
      
 352 
     | 
    
         
            +
             
     | 
| 
      
 353 
     | 
    
         
            +
             
     | 
| 
       252 
354 
     | 
    
         
             
            protected
         
     | 
| 
       253 
355 
     | 
    
         | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
      
 357 
     | 
    
         
            +
                def make_stacked
         
     | 
| 
      
 358 
     | 
    
         
            +
                  stacked_values = Array.new(@column_count, 0)
         
     | 
| 
      
 359 
     | 
    
         
            +
                  @data.each do |value_set|
         
     | 
| 
      
 360 
     | 
    
         
            +
                    value_set[1].each_with_index do |value, index|
         
     | 
| 
      
 361 
     | 
    
         
            +
                      stacked_values[index] += value
         
     | 
| 
      
 362 
     | 
    
         
            +
                    end
         
     | 
| 
      
 363 
     | 
    
         
            +
                    value_set[1] = stacked_values.dup
         
     | 
| 
      
 364 
     | 
    
         
            +
                  end
         
     | 
| 
      
 365 
     | 
    
         
            +
                end
         
     | 
| 
      
 366 
     | 
    
         
            +
             
     | 
| 
      
 367 
     | 
    
         
            +
             
     | 
| 
       254 
368 
     | 
    
         
             
                # Overridden by subclasses to do the actual plotting of the graph.
         
     | 
| 
       255 
369 
     | 
    
         
             
                #
         
     | 
| 
       256 
370 
     | 
    
         
             
                # Subclasses should start by calling super() for this method.
         
     | 
| 
       257 
371 
     | 
    
         
             
                def draw
         
     | 
| 
      
 372 
     | 
    
         
            +
                  make_stacked if @stacked
         
     | 
| 
       258 
373 
     | 
    
         
             
                  setup_drawing()
         
     | 
| 
       259 
374 
     | 
    
         | 
| 
       260 
375 
     | 
    
         
             
                  # Subclasses will do some drawing here...
         
     | 
| 
         @@ -276,8 +391,9 @@ protected 
     | 
|
| 
       276 
391 
     | 
    
         
             
                  setup_graph_measurements()
         
     | 
| 
       277 
392 
     | 
    
         
             
                  sort_norm_data() # Sort norm_data with avg largest values set first (for display)
         
     | 
| 
       278 
393 
     | 
    
         | 
| 
       279 
     | 
    
         
            -
                  draw_line_markers()
         
     | 
| 
       280 
394 
     | 
    
         
             
                  draw_legend()
         
     | 
| 
      
 395 
     | 
    
         
            +
                  setup_graph_height()
         
     | 
| 
      
 396 
     | 
    
         
            +
                  draw_line_markers()
         
     | 
| 
       281 
397 
     | 
    
         
             
                  draw_title
         
     | 
| 
       282 
398 
     | 
    
         
             
                end
         
     | 
| 
       283 
399 
     | 
    
         | 
| 
         @@ -287,7 +403,7 @@ protected 
     | 
|
| 
       287 
403 
     | 
    
         
             
                    @norm_data = []
         
     | 
| 
       288 
404 
     | 
    
         
             
                    return unless @has_data
         
     | 
| 
       289 
405 
     | 
    
         
             
                    @spread = @maximum_value.to_f - @minimum_value.to_f
         
     | 
| 
       290 
     | 
    
         
            -
                    @spread =  
     | 
| 
      
 406 
     | 
    
         
            +
                    @spread = 20.0 if @spread == 0.0 # Protect from divide by zero
         
     | 
| 
       291 
407 
     | 
    
         
             
                    min_val = @minimum_value.to_f
         
     | 
| 
       292 
408 
     | 
    
         
             
                    @data.each do |data_row|
         
     | 
| 
       293 
409 
     | 
    
         
             
                      norm_data_points = []
         
     | 
| 
         @@ -303,6 +419,10 @@ protected 
     | 
|
| 
       303 
419 
     | 
    
         
             
                  end
         
     | 
| 
       304 
420 
     | 
    
         
             
                end
         
     | 
| 
       305 
421 
     | 
    
         | 
| 
      
 422 
     | 
    
         
            +
                def setup_graph_height
         
     | 
| 
      
 423 
     | 
    
         
            +
                  @graph_height = @graph_bottom - @graph_top
         
     | 
| 
      
 424 
     | 
    
         
            +
                end
         
     | 
| 
      
 425 
     | 
    
         
            +
             
     | 
| 
       306 
426 
     | 
    
         
             
                def setup_graph_measurements
         
     | 
| 
       307 
427 
     | 
    
         
             
                  # TODO Separate horizontal lines from line number labels so they can be shown or hidden independently
         
     | 
| 
       308 
428 
     | 
    
         
             
                  # TODO Get width of longest left-hand vertical text label and space left margin accordingly
         
     | 
| 
         @@ -320,7 +440,7 @@ protected 
     | 
|
| 
       320 
440 
     | 
    
         | 
| 
       321 
441 
     | 
    
         
             
                  @graph_top = 150.0
         
     | 
| 
       322 
442 
     | 
    
         
             
                  @graph_bottom = @raw_rows - @graph_bottom_margin
         
     | 
| 
       323 
     | 
    
         
            -
                   
     | 
| 
      
 443 
     | 
    
         
            +
                  setup_graph_height()
         
     | 
| 
       324 
444 
     | 
    
         
             
                end
         
     | 
| 
       325 
445 
     | 
    
         | 
| 
       326 
446 
     | 
    
         
             
                # Draws horizontal background lines and labels
         
     | 
| 
         @@ -333,12 +453,10 @@ protected 
     | 
|
| 
       333 
453 
     | 
    
         
             
                  number_of_lines = 4
         
     | 
| 
       334 
454 
     | 
    
         | 
| 
       335 
455 
     | 
    
         
             
                  # TODO Round maximum marker value to a round number like 100, 0.1, 0.5, etc.
         
     | 
| 
       336 
     | 
    
         
            -
                  
         
     | 
| 
       337 
     | 
    
         
            -
                  # added relative height
         
     | 
| 
       338 
456 
     | 
    
         
             
                  spread = @maximum_value.to_f - @minimum_value.to_f
         
     | 
| 
       339 
     | 
    
         
            -
                   
     | 
| 
      
 457 
     | 
    
         
            +
                  spread = spread > 0 ? spread : 1
         
     | 
| 
      
 458 
     | 
    
         
            +
                  increment = (spread > 0) ? significant(spread / number_of_lines) : 1
         
     | 
| 
       340 
459 
     | 
    
         
             
                  inc_graph = @graph_height.to_f / (spread / increment)
         
     | 
| 
       341 
     | 
    
         
            -
                  #inc_graph = @graph_height.to_f / increment
         
     | 
| 
       342 
460 
     | 
    
         | 
| 
       343 
461 
     | 
    
         
             
                  (0..number_of_lines).each do |index|
         
     | 
| 
       344 
462 
     | 
    
         
             
                    y = @graph_top + @graph_height - index.to_f * inc_graph
         
     | 
| 
         @@ -351,11 +469,32 @@ protected 
     | 
|
| 
       351 
469 
     | 
    
         
             
                    @d.stroke = 'transparent'
         
     | 
| 
       352 
470 
     | 
    
         
             
                    @d.pointsize = scale_fontsize(@marker_font_size)
         
     | 
| 
       353 
471 
     | 
    
         
             
                    @d.gravity = EastGravity
         
     | 
| 
      
 472 
     | 
    
         
            +
                    
         
     | 
| 
       354 
473 
     | 
    
         
             
                    @d = @d.annotate_scaled( @base_image, 
         
     | 
| 
       355 
474 
     | 
    
         
             
                                      100, 20,
         
     | 
| 
       356 
475 
     | 
    
         
             
                                      -10, y - (@marker_font_size/2.0), 
         
     | 
| 
       357 
476 
     | 
    
         
             
                                      marker_label.to_s, @scale)
         
     | 
| 
       358 
     | 
    
         
            -
             
     | 
| 
      
 477 
     | 
    
         
            +
                  end
         
     | 
| 
      
 478 
     | 
    
         
            +
                  i = 0
         
     | 
| 
      
 479 
     | 
    
         
            +
                  @additional_line_values.each do |value|
         
     | 
| 
      
 480 
     | 
    
         
            +
                    inc_graph = @graph_height.to_f / (@maximum_value.to_f / value)
         
     | 
| 
      
 481 
     | 
    
         
            +
                    
         
     | 
| 
      
 482 
     | 
    
         
            +
                    y = @graph_top + @graph_height - inc_graph
         
     | 
| 
      
 483 
     | 
    
         
            +
                    
         
     | 
| 
      
 484 
     | 
    
         
            +
                    @d = @d.stroke(@additional_line_colors[i])
         
     | 
| 
      
 485 
     | 
    
         
            +
                    @d = @d.line(@graph_left, y, @graph_right, y)
         
     | 
| 
      
 486 
     | 
    
         
            +
             
     | 
| 
      
 487 
     | 
    
         
            +
             
     | 
| 
      
 488 
     | 
    
         
            +
                    @d.fill = @additional_line_colors[i]
         
     | 
| 
      
 489 
     | 
    
         
            +
                    @d.font = @font if @font
         
     | 
| 
      
 490 
     | 
    
         
            +
                    @d.stroke = 'transparent'
         
     | 
| 
      
 491 
     | 
    
         
            +
                    @d.pointsize = scale_fontsize(@marker_font_size)
         
     | 
| 
      
 492 
     | 
    
         
            +
                    @d.gravity = EastGravity
         
     | 
| 
      
 493 
     | 
    
         
            +
                    @d = @d.annotate_scaled( @base_image, 
         
     | 
| 
      
 494 
     | 
    
         
            +
                                      100, 20,
         
     | 
| 
      
 495 
     | 
    
         
            +
                                      -10, y - (@marker_font_size/2.0), 
         
     | 
| 
      
 496 
     | 
    
         
            +
                                      "", @scale)
         
     | 
| 
      
 497 
     | 
    
         
            +
                    i += 1   
         
     | 
| 
       359 
498 
     | 
    
         
             
                  end
         
     | 
| 
       360 
499 
     | 
    
         
             
                end
         
     | 
| 
       361 
500 
     | 
    
         | 
| 
         @@ -468,6 +607,13 @@ protected 
     | 
|
| 
       468 
607 
     | 
    
         
             
                  end
         
     | 
| 
       469 
608 
     | 
    
         
             
                  image[0]
         
     | 
| 
       470 
609 
     | 
    
         
             
                end
         
     | 
| 
      
 610 
     | 
    
         
            +
                
         
     | 
| 
      
 611 
     | 
    
         
            +
                # Use with a theme to make a transparent background
         
     | 
| 
      
 612 
     | 
    
         
            +
                def render_transparent_background
         
     | 
| 
      
 613 
     | 
    
         
            +
                  Image.new(@columns, @rows) do
         
     | 
| 
      
 614 
     | 
    
         
            +
                    self.background_color = 'transparent'
         
     | 
| 
      
 615 
     | 
    
         
            +
                  end
         
     | 
| 
      
 616 
     | 
    
         
            +
                end
         
     | 
| 
       471 
617 
     | 
    
         | 
| 
       472 
618 
     | 
    
         
             
                def reset_themes
         
     | 
| 
       473 
619 
     | 
    
         
             
                  @color_index = 0
         
     | 
| 
         @@ -511,6 +657,7 @@ protected 
     | 
|
| 
       511 
657 
     | 
    
         
             
                end
         
     | 
| 
       512 
658 
     | 
    
         | 
| 
       513 
659 
     | 
    
         
             
                def significant(inc)
         
     | 
| 
      
 660 
     | 
    
         
            +
                  return 1.0 if inc == 0 # Keep from going into infinite loop
         
     | 
| 
       514 
661 
     | 
    
         
             
                  factor = 1.0
         
     | 
| 
       515 
662 
     | 
    
         
             
                  while (inc < 10)
         
     | 
| 
       516 
663 
     | 
    
         
             
                    inc *= 10
         
     | 
| 
         @@ -571,7 +718,10 @@ private 
     | 
|
| 
       571 
718 
     | 
    
         
             
                      @color_index += 1
         
     | 
| 
       572 
719 
     | 
    
         
             
                      return @colors[@color_index - 1]
         
     | 
| 
       573 
720 
     | 
    
         
             
                    else
         
     | 
| 
       574 
     | 
    
         
            -
                       
     | 
| 
      
 721 
     | 
    
         
            +
                      # Start over
         
     | 
| 
      
 722 
     | 
    
         
            +
                      @color_index = 0
         
     | 
| 
      
 723 
     | 
    
         
            +
                      return @colors[-1]
         
     | 
| 
      
 724 
     | 
    
         
            +
                      #raise(ColorlistExhaustedException, "There are no more colors left to use.")
         
     | 
| 
       575 
725 
     | 
    
         
             
                    end
         
     | 
| 
       576 
726 
     | 
    
         
             
                  end
         
     | 
| 
       577 
727 
     | 
    
         
             
                end
         
     | 
    
        data/lib/gruff/line.rb
    CHANGED
    
    | 
         @@ -29,8 +29,7 @@ class Gruff::Line < Gruff::Base 
     | 
|
| 
       29 
29 
     | 
    
         
             
                  super args.shift
         
     | 
| 
       30 
30 
     | 
    
         
             
                end
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                @hide_dots = false
         
     | 
| 
       33 
     | 
    
         
            -
                @hide_lines = args.empty? ? false : !args.shift # For backwards compatibility. Use g.hide_lines = true from now on.
         
     | 
| 
      
 32 
     | 
    
         
            +
                @hide_dots = @hide_lines = false
         
     | 
| 
       34 
33 
     | 
    
         
             
                @baseline_color = 'red'
         
     | 
| 
       35 
34 
     | 
    
         
             
              end
         
     | 
| 
       36 
35 
     | 
    
         | 
| 
         @@ -57,7 +56,7 @@ class Gruff::Line < Gruff::Base 
     | 
|
| 
       57 
56 
     | 
    
         
             
                end
         
     | 
| 
       58 
57 
     | 
    
         | 
| 
       59 
58 
     | 
    
         
             
                @norm_data.each do |data_row|
         
     | 
| 
       60 
     | 
    
         
            -
                  prev_x = prev_y =  
     | 
| 
      
 59 
     | 
    
         
            +
                  prev_x = prev_y = nil
         
     | 
| 
       61 
60 
     | 
    
         
             
                  @d = @d.stroke data_row[DATA_COLOR_INDEX]
         
     | 
| 
       62 
61 
     | 
    
         
             
                  @d = @d.fill data_row[DATA_COLOR_INDEX]
         
     | 
| 
       63 
62 
     | 
    
         | 
| 
         @@ -69,7 +68,7 @@ class Gruff::Line < Gruff::Base 
     | 
|
| 
       69 
68 
     | 
    
         | 
| 
       70 
69 
     | 
    
         
             
                    new_y = @graph_top + (@graph_height - data_point * @graph_height)
         
     | 
| 
       71 
70 
     | 
    
         | 
| 
       72 
     | 
    
         
            -
                    if !@hide_lines and prev_x  
     | 
| 
      
 71 
     | 
    
         
            +
                    if !@hide_lines and !prev_x.nil? and !prev_y.nil? then
         
     | 
| 
       73 
72 
     | 
    
         
             
                      @d = @d.line(prev_x, prev_y, new_x, new_y)
         
     | 
| 
       74 
73 
     | 
    
         
             
                    end
         
     | 
| 
       75 
74 
     | 
    
         
             
                    @d = @d.circle(new_x, new_y, new_x - circle_radius, new_y) unless @hide_dots
         
     | 
    
        data/lib/gruff/net.rb
    ADDED
    
    | 
         @@ -0,0 +1,133 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
            require File.dirname(__FILE__) + '/base'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # Experimental!!! See also the Spider graph.
         
     | 
| 
      
 5 
     | 
    
         
            +
            class Gruff::Net < Gruff::Base
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              def draw
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                super
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                return unless @has_data
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                @radius = @graph_height / 2.0
         
     | 
| 
      
 14 
     | 
    
         
            +
                @center_x = @graph_left + (@graph_width / 2.0)
         
     | 
| 
      
 15 
     | 
    
         
            +
                @center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                @x_increment = @graph_width / (@column_count - 1).to_f
         
     | 
| 
      
 18 
     | 
    
         
            +
                circle_radius = clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 2.5), 5.0)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                @d = @d.stroke_opacity 1.0
         
     | 
| 
      
 21 
     | 
    
         
            +
                @d = @d.stroke_width clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 4), 5.0)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                if (defined?(@norm_baseline)) then
         
     | 
| 
      
 24 
     | 
    
         
            +
                  level = @graph_top + (@graph_height - @norm_baseline * @graph_height)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @d = @d.push
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @d.stroke_color @baseline_color
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @d.fill_opacity 0.0
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @d.stroke_dasharray(10, 20)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @d.stroke_width 5
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @d.line(@graph_left, level, @graph_left + @graph_width, level)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @d = @d.pop
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                @norm_data.each do |data_row|
         
     | 
| 
      
 35 
     | 
    
         
            +
                  prev_x = prev_y = nil
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @d = @d.stroke data_row[DATA_COLOR_INDEX]
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @d = @d.fill data_row[DATA_COLOR_INDEX]
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  data_row[1].each_with_index do |data_point, index|
         
     | 
| 
      
 40 
     | 
    
         
            +
                    next if data_point.nil?
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    rad_pos = index * Math::PI * 2 / @column_count
         
     | 
| 
      
 43 
     | 
    
         
            +
                    point_distance = data_point * @radius
         
     | 
| 
      
 44 
     | 
    
         
            +
                    start_x = @center_x + Math::sin(rad_pos) * point_distance
         
     | 
| 
      
 45 
     | 
    
         
            +
                    start_y = @center_y - Math::cos(rad_pos) * point_distance
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    next_index = index + 1 < data_row[1].length ? index + 1 : 0
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    next_rad_pos = next_index * Math::PI * 2 / @column_count
         
     | 
| 
      
 50 
     | 
    
         
            +
                    next_point_distance = data_row[1][next_index] * @radius
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end_x = @center_x + Math::sin(next_rad_pos) * next_point_distance
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end_y = @center_y - Math::cos(next_rad_pos) * next_point_distance
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    @d = @d.line(start_x, start_y, end_x, end_y)
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    @d = @d.circle(start_x, start_y, start_x - circle_radius, start_y) unless @hide_dots
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                @d.draw(@base_image)
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              # the lines connecting in the center, with the first line vertical
         
     | 
| 
      
 66 
     | 
    
         
            +
              def draw_line_markers
         
     | 
| 
      
 67 
     | 
    
         
            +
                return if @hide_line_markers
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                # have to do this here (AGAIN)... see draw() in this class
         
     | 
| 
      
 71 
     | 
    
         
            +
                # because this funtion is called before the @radius, @center_x and @center_y are set
         
     | 
| 
      
 72 
     | 
    
         
            +
                @radius = @graph_height / 2.0
         
     | 
| 
      
 73 
     | 
    
         
            +
                @center_x = @graph_left + (@graph_width / 2.0)
         
     | 
| 
      
 74 
     | 
    
         
            +
                @center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                # Draw horizontal line markers and annotate with numbers
         
     | 
| 
      
 78 
     | 
    
         
            +
                @d = @d.stroke(@marker_color)
         
     | 
| 
      
 79 
     | 
    
         
            +
                @d = @d.stroke_width 1
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                (0..@column_count-1).each do |index|
         
     | 
| 
      
 83 
     | 
    
         
            +
                  rad_pos = index * Math::PI * 2 / @column_count
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  @d = @d.line(@center_x, @center_y, @center_x + Math::sin(rad_pos) * @radius, @center_y - Math::cos(rad_pos) * @radius)
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  marker_label = labels[index] ? labels[index].to_s : '000'
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  draw_label(@center_x, @center_y, rad_pos * 360 / (2 * Math::PI), @radius, marker_label)
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
            private
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
              def draw_label(center_x, center_y, angle, radius, amount)
         
     | 
| 
      
 97 
     | 
    
         
            +
                r_offset = 1.1
         
     | 
| 
      
 98 
     | 
    
         
            +
                x_offset = center_x # + 15 # The label points need to be tweaked slightly
         
     | 
| 
      
 99 
     | 
    
         
            +
                y_offset = center_y # + 0  # This one doesn't though
         
     | 
| 
      
 100 
     | 
    
         
            +
                x = x_offset + (radius * r_offset * Math.sin(angle.deg2rad))
         
     | 
| 
      
 101 
     | 
    
         
            +
                y = y_offset - (radius * r_offset * Math.cos(angle.deg2rad))
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                # Draw label
         
     | 
| 
      
 104 
     | 
    
         
            +
                @d.fill = @marker_color
         
     | 
| 
      
 105 
     | 
    
         
            +
                @d.font = @font if @font
         
     | 
| 
      
 106 
     | 
    
         
            +
                @d.pointsize = scale_fontsize(20)
         
     | 
| 
      
 107 
     | 
    
         
            +
                @d.stroke = 'transparent'
         
     | 
| 
      
 108 
     | 
    
         
            +
                @d.font_weight = BoldWeight
         
     | 
| 
      
 109 
     | 
    
         
            +
                s = angle.deg2rad / (2*Math::PI)
         
     | 
| 
      
 110 
     | 
    
         
            +
                @d.gravity = SouthGravity     if s >= 0.96 or s < 0.04
         
     | 
| 
      
 111 
     | 
    
         
            +
                @d.gravity = SouthWestGravity if s >= 0.04 or s < 0.21
         
     | 
| 
      
 112 
     | 
    
         
            +
                @d.gravity = WestGravity      if s >= 0.21 or s < 0.29
         
     | 
| 
      
 113 
     | 
    
         
            +
                @d.gravity = NorthWestGravity if s >= 0.29 or s < 0.46
         
     | 
| 
      
 114 
     | 
    
         
            +
                @d.gravity = NorthGravity     if s >= 0.46 or s < 0.54
         
     | 
| 
      
 115 
     | 
    
         
            +
                @d.gravity = NorthEastGravity if s >= 0.54 or s < 0.71
         
     | 
| 
      
 116 
     | 
    
         
            +
                @d.gravity = EastGravity      if s >= 0.71 or s < 0.79
         
     | 
| 
      
 117 
     | 
    
         
            +
                @d.gravity = SouthEastGravity if s >= 0.79 or s < 0.96
         
     | 
| 
      
 118 
     | 
    
         
            +
            #     @d.gravity = NorthGravity
         
     | 
| 
      
 119 
     | 
    
         
            +
                @d.annotate_scaled(@base_image, 0, 0, x, y, amount, @scale)
         
     | 
| 
      
 120 
     | 
    
         
            +
              end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
            end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
            class Float
         
     | 
| 
      
 126 
     | 
    
         
            +
              # Used for degree => radian conversions
         
     | 
| 
      
 127 
     | 
    
         
            +
              def deg2rad
         
     | 
| 
      
 128 
     | 
    
         
            +
                self * (Math::PI/180.0)
         
     | 
| 
      
 129 
     | 
    
         
            +
              end
         
     | 
| 
      
 130 
     | 
    
         
            +
            end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
             
     |