ctioga 1.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/COPYING +340 -0
  2. data/ctioga/bin/ctable +28 -0
  3. data/ctioga/bin/ctioga +37 -0
  4. data/ctioga/doc/ctable.1 +156 -0
  5. data/ctioga/doc/ctioga.1 +2363 -0
  6. data/ctioga/examples/README +46 -0
  7. data/ctioga/examples/ctioga.gnuplot +4 -0
  8. data/ctioga/examples/ctioga_within_tioga.rb +53 -0
  9. data/ctioga/examples/ctiogarc.rb +24 -0
  10. data/ctioga/examples/include_1.rb +15 -0
  11. data/ctioga/examples/noise.dat +100 -0
  12. data/ctioga/examples/noise.rb +13 -0
  13. data/ctioga/examples/trig.csv +100 -0
  14. data/ctioga/examples/trig.dat +100 -0
  15. data/ctioga/examples/trig.rb +14 -0
  16. data/ctioga/examples/trigh.dat +100 -0
  17. data/ctioga/examples/trigh.rb +10 -0
  18. data/ctioga/examples/tutorial +763 -0
  19. data/ctioga/examples/tutorial.sh +269 -0
  20. data/ctioga/tests/README +14 -0
  21. data/ctioga/tests/axes.sh +40 -0
  22. data/ctioga/tests/basic.sh +11 -0
  23. data/ctioga/tests/draw.sh +24 -0
  24. data/ctioga/tests/histograms.sh +14 -0
  25. data/ctioga/tests/insets.sh +41 -0
  26. data/ctioga/tests/layouts.sh +29 -0
  27. data/ctioga/tests/legends.sh +113 -0
  28. data/ctioga/tests/styles.sh +43 -0
  29. data/ctioga/tests/test_style.sh +8 -0
  30. data/ctioga/tests/tests.sh +24 -0
  31. data/ctioga/tests/text_backend.sh +83 -0
  32. data/ctioga/tests/tioga_defaults.rb +18 -0
  33. data/lib/CTioga/axes.rb +904 -0
  34. data/lib/CTioga/backends.rb +88 -0
  35. data/lib/CTioga/boundaries.rb +224 -0
  36. data/lib/CTioga/ctable.rb +134 -0
  37. data/lib/CTioga/curve_style.rb +246 -0
  38. data/lib/CTioga/debug.rb +199 -0
  39. data/lib/CTioga/dimension.rb +133 -0
  40. data/lib/CTioga/elements.rb +17 -0
  41. data/lib/CTioga/elements/base.rb +84 -0
  42. data/lib/CTioga/elements/containers.rb +578 -0
  43. data/lib/CTioga/elements/curves.rb +368 -0
  44. data/lib/CTioga/elements/tioga_primitives.rb +440 -0
  45. data/lib/CTioga/layout.rb +595 -0
  46. data/lib/CTioga/legends.rb +29 -0
  47. data/lib/CTioga/legends/cmdline.rb +187 -0
  48. data/lib/CTioga/legends/item.rb +164 -0
  49. data/lib/CTioga/legends/style.rb +257 -0
  50. data/lib/CTioga/log.rb +73 -0
  51. data/lib/CTioga/movingarrays.rb +131 -0
  52. data/lib/CTioga/partition.rb +271 -0
  53. data/lib/CTioga/plot_style.rb +230 -0
  54. data/lib/CTioga/plotmaker.rb +1677 -0
  55. data/lib/CTioga/shortcuts.rb +69 -0
  56. data/lib/CTioga/structures.rb +82 -0
  57. data/lib/CTioga/styles.rb +140 -0
  58. data/lib/CTioga/themes.rb +581 -0
  59. data/lib/CTioga/themes/classical.rb +82 -0
  60. data/lib/CTioga/themes/demo.rb +63 -0
  61. data/lib/CTioga/themes/fits.rb +91 -0
  62. data/lib/CTioga/themes/mono.rb +33 -0
  63. data/lib/CTioga/tioga.rb +32 -0
  64. data/lib/CTioga/utils.rb +173 -0
  65. data/lib/MetaBuilder/Parameters/dates.rb +38 -0
  66. data/lib/MetaBuilder/Parameters/lists.rb +132 -0
  67. data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
  68. data/lib/MetaBuilder/Parameters/strings.rb +86 -0
  69. data/lib/MetaBuilder/Parameters/styles.rb +75 -0
  70. data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
  71. data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
  72. data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
  73. data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
  74. data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
  75. data/lib/MetaBuilder/descriptions.rb +603 -0
  76. data/lib/MetaBuilder/factory.rb +101 -0
  77. data/lib/MetaBuilder/group.rb +57 -0
  78. data/lib/MetaBuilder/metabuilder.rb +10 -0
  79. data/lib/MetaBuilder/parameter.rb +374 -0
  80. data/lib/MetaBuilder/parameters.rb +11 -0
  81. data/lib/MetaBuilder/qt4.rb +8 -0
  82. data/lib/SciYAG/Backends/backend.rb +379 -0
  83. data/lib/SciYAG/Backends/binner.rb +168 -0
  84. data/lib/SciYAG/Backends/cache.rb +102 -0
  85. data/lib/SciYAG/Backends/dataset.rb +158 -0
  86. data/lib/SciYAG/Backends/descriptions.rb +469 -0
  87. data/lib/SciYAG/Backends/filters.rb +25 -0
  88. data/lib/SciYAG/Backends/filters/average.rb +134 -0
  89. data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
  90. data/lib/SciYAG/Backends/filters/filter.rb +70 -0
  91. data/lib/SciYAG/Backends/filters/norm.rb +39 -0
  92. data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
  93. data/lib/SciYAG/Backends/filters/sort.rb +43 -0
  94. data/lib/SciYAG/Backends/filters/strip.rb +34 -0
  95. data/lib/SciYAG/Backends/filters/trim.rb +64 -0
  96. data/lib/SciYAG/Backends/gnuplot.rb +131 -0
  97. data/lib/SciYAG/Backends/math.rb +108 -0
  98. data/lib/SciYAG/Backends/mdb.rb +462 -0
  99. data/lib/SciYAG/Backends/multitext.rb +96 -0
  100. data/lib/SciYAG/Backends/source.rb +64 -0
  101. data/lib/SciYAG/Backends/text.rb +339 -0
  102. data/lib/SciYAG/backends.rb +16 -0
  103. metadata +191 -0
@@ -0,0 +1,595 @@
1
+ # layout.rb, copyright (c) 2006 by Vincent Fourmond:
2
+ # The core of the layout computation
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 'Tioga/Utils'
15
+ require 'CTioga/log'
16
+ require 'CTioga/dimension'
17
+ require 'CTioga/boundaries'
18
+
19
+
20
+ module CTioga
21
+
22
+ Version::register_svn_info('$Revision: 836 $', '$Date: 2008-10-10 01:27:54 +0200 (Fri, 10 Oct 2008) $')
23
+
24
+ # This class merely stores various information about the layout,
25
+ # such as the desired mimimum amount of padding on the sides,
26
+ # the desired distance between plots and legends, the size and
27
+ # position of the legend...
28
+ #
29
+ # For now, this class is hardly more than a Struct, but that is
30
+ # a very convenient way to carry across several objects and keep the
31
+ # documentation.
32
+ #
33
+ # Every Container should have a LayoutPreference object in its
34
+ # layout_preferences attribute.
35
+ class LayoutPreferences
36
+
37
+ LayoutDefaults = {
38
+ :padding => [Dimension.new(0.05),
39
+ Dimension.new(0.05),
40
+ Dimension.new(0.05),
41
+ Dimension.new(0.05)],
42
+ :legend_size => Dimension.new(0.15),
43
+ :legend_position => :right,
44
+ :legend_glue => Dimension.new("2pt")
45
+ }
46
+
47
+ # Padding: (left, right, top, bottom)
48
+ # An array of Dimensions specifiying the desired amount of padding
49
+ # around a given object.
50
+ attr_accessor :padding
51
+
52
+ # The legend position: :left, :right, :top, :bottom or :inside
53
+ attr_accessor :legend_position
54
+
55
+ # The size of the legend. Not applicable in the case of an
56
+ # :inside legend specification.
57
+ attr_accessor :legend_size
58
+
59
+ # The full legend specification, in the case of an inside legend.
60
+ attr_accessor :legend_spec
61
+
62
+ # The minimum space between the graph and the legend, when the latter
63
+ # is on one of the sides of the graph.
64
+ attr_accessor :legend_glue
65
+
66
+ def initialize
67
+ for key,val in LayoutDefaults
68
+ self.send("#{key}=", val)
69
+ end
70
+ end
71
+
72
+
73
+ # Converts the padding to values relative to the given
74
+ # frames
75
+ def self.padding_relative_values(p, frames)
76
+ ret_val = []
77
+ 4.times do |i|
78
+ if p[i]
79
+ ret_val[i] = p[i].to_relative(frames,
80
+ i < 2 ? :horizontal : :vertical)
81
+ else
82
+ ret_val[i] = 0 # safely default to 0 ???
83
+ end
84
+ end
85
+ return ret_val
86
+ end
87
+
88
+ def padding_relative_values(frames)
89
+ return LayoutPreferences.padding_relative_values(@padding, frames)
90
+ end
91
+ end
92
+
93
+ # Handles the gory details of placement of objects. It can be called
94
+ # to provide any of the specification for a subplot/subfigure, or
95
+ # the hash necessary to be fed to show_plot_with_legend.
96
+ #
97
+ # Every layout object has a root object (object), the base one,
98
+ # the one which
99
+ # limits its capabilities. No layout object should touch anything
100
+ # that is not a descendant of the root object -- or the root object
101
+ # itself.
102
+ #
103
+ # In addition to that, the layout can handle many children of this
104
+ # root object that request it.
105
+ class Layout
106
+
107
+ include Log
108
+
109
+ # The object the layout is handling. Should never be null.
110
+ attr_accessor :object
111
+
112
+ # The layouts that would wish to report extensions information
113
+ # to this one.
114
+ attr_accessor :child_layouts
115
+
116
+ # The parent layout. Can be nil. Most of the layouts won't
117
+ # reall take something from this.
118
+ attr_accessor :parent
119
+
120
+ # Performs basic initialization. All descendants from this class
121
+ # should have at least three arguments, and at most three mandatory
122
+ # ones, always the same. Else, the convert_layout function
123
+ # won't work, and that means trouble.
124
+ def initialize(ro, ch = [], children = [] )
125
+ @object = ro
126
+ @object.layout = self
127
+ @child_layouts = []
128
+ for c in children
129
+ add_child(c)
130
+ end
131
+ @parent = nil
132
+ end
133
+
134
+ # Converts self into another layout, whose class is _cls_.
135
+ def convert_layout(cls)
136
+ return cls.new(object, @child_layouts)
137
+ end
138
+
139
+ # Ask this layout to manage a child. No check is done to
140
+ # actually ensure that the child really is a child.
141
+ def add_child(child)
142
+ child.parent = self
143
+ @child_layouts << child
144
+ end
145
+
146
+ # Returns the specs to be fed to show_plot_with_legend
147
+ # for the given _object_.
148
+ #
149
+ # Defaults to full plot.
150
+ def legend_specs(object, t = nil)
151
+ return [0,0,0,0].to_frame("plot_%s_margin")
152
+ end
153
+
154
+ # Now, various utility functions that should make the
155
+ # life much easier for derived classes (or at least
156
+ # to the person who'll be writing them).
157
+
158
+ # A utility function that adds an extension as reported by
159
+ # Axes#extension (or any other, for what matters)
160
+ # to an already existing array of _extensions_.
161
+ # It will replace the current element unless it is smaller.
162
+ def update_extension_with_object(extensions, object, t, scale = 1)
163
+ new_ext = object.extension(t,scale)
164
+ return update_extensions(extensions, new_ext)
165
+ end
166
+
167
+ # Simply updates the given extensions with the new ones, making
168
+ # sure that nall the objects can be crammed inside.
169
+ def update_extensions(extensions, new_ext)
170
+ return Dimension.update_extensions(extensions, new_ext)
171
+ end
172
+
173
+ # Returns the extension of the layout
174
+ def extension(t)
175
+ extensions = [0,0,0,0]
176
+ # TODO: maybe this code should move within the PlotStyle object
177
+ for a in [:title, :xlabel, :ylabel]
178
+ update_extension_with_object(extensions,
179
+ @object.plot_style.send(a),
180
+ t, @object.rescale || 1)
181
+ end
182
+ update_extension_with_object(extensions, @object.plot_style.edges,t)
183
+ debug "Layout #{"%x" % self.object_id} extensions #{extensions.inspect}"
184
+ return extensions
185
+ end
186
+
187
+ # Computes the extension for the legend, according to the
188
+ # position. Does not check if the object actually needs to
189
+ # display any legend whatsoever.
190
+ def legend_extension(t)
191
+ frame = @object.outer_frame(t)
192
+ case @object.layout_preferences.legend_position
193
+ when :left
194
+ return [@object.layout_preferences.
195
+ legend_size.to_absolute(frame, :horizontal),0,0,0]
196
+ when :right
197
+ return [0,@object.layout_preferences.
198
+ legend_size.to_absolute(frame, :horizontal),0,0]
199
+ when :top
200
+ return [0,0,@object.layout_preferences.
201
+ legend_size.to_absolute(frame, :vertical),0]
202
+ when :bottom
203
+ return [0,0,0,
204
+ @object.layout_preferences.
205
+ legend_size.to_absolute(frame, :vertical)]
206
+ when :inside
207
+ return [0,0,0,0] # Inside doesn't count !
208
+ else
209
+ warn "Invalid legend position specification"
210
+ return [0,0,0,0] # Does not count.
211
+ end
212
+ end
213
+
214
+ # All the things about legends here should eventually be
215
+ # moved to their own file, very much like the axes things. There
216
+ # is no real reason to differentiate them.
217
+
218
+ # Converts the legend extension to a full legend specification.
219
+ def legend_only_specs(t, padding)
220
+ frame = @object.outer_frame(t)
221
+ case @object.layout_preferences.legend_position
222
+ when :inside
223
+ # Use the legend_spec without any other modification.
224
+ return @object.layout_preferences.legend_spec
225
+ else
226
+ ext = legend_extension(t)
227
+ mar = Dimension.absolute_to_relative(ext, frame)
228
+ end
229
+
230
+ target = [0,0,0,0]
231
+ 4.times do |i|
232
+ if mar[i] > 0
233
+ target[(i/2) * 2 + ((i+1) % 2)] = 1 - mar[i]
234
+ end
235
+ end
236
+
237
+ # Now, the legend spec is nearly ready; we just need to
238
+ # modify it so that it matches the sides of the plot.
239
+ case @object.layout_preferences.legend_position
240
+ when :left, :right
241
+ target[2] = padding[2]
242
+ target[3] = padding[3]
243
+ when :top, :bottom
244
+ target[0] = padding[0]
245
+ target[1] = padding[1]
246
+ end
247
+
248
+ return target.to_frame("legend_%s_margin")
249
+ end
250
+
251
+ # Returns the padding for the given object, taking into
252
+ # account the position of legends:
253
+ def compute_padding(object)
254
+ prefs = @object.layout_preferences
255
+ padding = @object.layout_preferences.padding.dup
256
+
257
+ # If the object has a legend
258
+ if @object.display_legend? && Utils::side?(prefs.legend_position)
259
+ padding[Utils::side(prefs.legend_position)] =
260
+ prefs.legend_glue.dup
261
+ end
262
+ return padding
263
+ end
264
+
265
+ end
266
+
267
+ # Now come the interesting part: the layouts !!!!
268
+
269
+
270
+ # This class takes care of simple plots. All children
271
+ # are considered for computing the extension of the plots, but
272
+ # they won't receive anything else than a default full frame.
273
+ #
274
+ # For now, the legend is not taken into consideration -- and the
275
+ # children as well ;-) !!
276
+ #
277
+ # The SimpleLayout class is also meant as a base class for any
278
+ # layout that would need to know its extensions, but that will
279
+ # not get positionned at all in the same way.
280
+ class SimpleLayout < Layout
281
+
282
+ # A hash setting the 'bounding box' for the plot. It is relative
283
+ # to the Container#outer_frame. Defaults to 0: the object occupies
284
+ # all the frame.
285
+ attr_accessor :bounding_box
286
+
287
+ def initialize(ro, ch = [], children = [] )
288
+ super
289
+ @bounding_box = [0.0, 0.0, 0.0, 0.0]
290
+ end
291
+
292
+ # Returns the object's frame.
293
+ def legend_specs(t = nil)
294
+ a = extension(t)
295
+ frames = @object.outer_frame(t)
296
+ padding = Dimension.absolute_to_relative(a, frames)
297
+
298
+ padding_prefs =
299
+ LayoutPreferences.padding_relative_values(compute_padding(@object),
300
+ frames)
301
+
302
+ update_extensions(padding, padding_prefs)
303
+
304
+ # If we display a legend, add the legend to the side.
305
+ if @object.display_legend?
306
+ a = Dimension.absolute_to_relative(legend_extension(t),frames)
307
+ 4.times do |i|
308
+ padding[i] += a[i]
309
+ end
310
+ end
311
+
312
+ specs = {}
313
+ i = 0
314
+ for side in %w(left right top bottom)
315
+ specs["plot_#{side}_margin"] = padding[i] + @bounding_box[i]
316
+ i += 1
317
+ end
318
+ if @object.display_legend?
319
+ a = legend_only_specs(t, padding)
320
+ return specs.merge(a)
321
+ else
322
+ return specs
323
+ end
324
+ end
325
+
326
+ # Returns this layout's extension. By default, it also
327
+ # includes the children's extensions, unless the
328
+ # @ignore_children_extensions variable is set.
329
+ def extension(t)
330
+ # We use the simple extensions computation from Layout
331
+ a = super
332
+ # We update it with the children's informations:
333
+ unless @ignore_children_extensions
334
+ for f in @child_layouts
335
+ update_extensions(a, f.extension(t))
336
+ end
337
+ end
338
+ return a
339
+ end
340
+
341
+ end
342
+
343
+ # This class implements a layout for objects that only need to report
344
+ # extension information to the parent but don't need to really take
345
+ # this into consideration for themselves. A good example is the
346
+ # 'alternative axes' things.
347
+ class FixedLayout < SimpleLayout
348
+
349
+ # The margins of the given object, either an array or a hash
350
+ # with plot_%s_margin
351
+ attr_accessor :position
352
+
353
+ # Creates a Fixed layout for the given object.
354
+ def initialize(ro, ch = [], children = [])
355
+ super
356
+ @position = [0, 0, 0, 0]
357
+ end
358
+
359
+ # Return default plot specifications: we don't take the
360
+ # legend into consideration, for instance.
361
+ def legend_specs(object, t = nil)
362
+
363
+ # First, the position of the layout:
364
+ if @position.is_a? Hash
365
+ specs = @position.dup
366
+ else
367
+ specs = @position.to_frame("plot_%s_margin")
368
+ end
369
+
370
+
371
+ # Then handling of the legends if applicable:
372
+ # TODO: this will fail for normal legends....
373
+ if @object.display_legend?
374
+ case @object.layout_preferences.legend_position
375
+ when :inside
376
+ a = @object.layout_preferences.legend_spec
377
+
378
+ legend = Utils::frame_to_array(a, "legend_%s_margin")
379
+ frame = Utils::frame_to_array(specs, "plot_%s_margin")
380
+
381
+ target = Utils::compose_margins(frame, legend)
382
+ a = target.to_frame("legend_%s_margin")
383
+ else
384
+ warn "Legend position #{@object.layout_preferences.legend_position.inspect} not implemented for FixedLayout"
385
+ a = {}
386
+ end
387
+ return specs.merge(a)
388
+ else
389
+ return specs
390
+ end
391
+ end
392
+
393
+ end
394
+
395
+ # A layout that will handle objects aligned on a grid. All objects
396
+ # belonging to one row of the grid will share the same vertical padding.
397
+ # All the ones belonging to a column will share the same
398
+ # horizontal padding. All layouts added to this one should
399
+ # be a GridItemLayout or a descendant, or at least something
400
+ # behaving correctly in that respect.
401
+ class GridLayout < SimpleLayout
402
+
403
+ def initialize(*a)
404
+ super
405
+ # We don't want the children's extensions messing
406
+ # with this layout's extension
407
+ @ignore_children_extensions = true
408
+ end
409
+
410
+ # Some various 'accessors' for rows and columns:
411
+
412
+ # Sets the number of rows. The number of columns will be computed
413
+ # automatically.
414
+ def rows=(nb)
415
+ @rows = nb
416
+ @columns = false
417
+ end
418
+
419
+ # Returns the number of rows
420
+ def rows
421
+ return @rows if @rows
422
+ return (@child_layouts.size.to_f / @columns).ceil
423
+ end
424
+
425
+ # Sets the number of columns. The number of rows will be computed
426
+ # automatically.
427
+ def columns=(nb)
428
+ @columns = nb
429
+ @rows = false
430
+ end
431
+
432
+ # Returns the number of rows
433
+ def columns
434
+ return @columns if @columns
435
+ return (@child_layouts.size.to_f / @rows).ceil
436
+ end
437
+
438
+ # Returns the position of the given child layout in terms of
439
+ # [row, col]. The layouts are added first in columns
440
+ def child_position(child)
441
+ i = @child_layouts.index(child)
442
+ return [i % rows, i / rows]
443
+ end
444
+
445
+ # Returns the child at position (row, col)
446
+ def child(row, col)
447
+ return @child_layouts[col * rows + row ]
448
+ end
449
+
450
+ # Computes the various extensions for each columns and row
451
+ def compute_extensions(t)
452
+ @rows_extensions = []
453
+ rows.times do |i|
454
+ a = [0,0,0,0]
455
+ columns.times do |j|
456
+ update_extensions(a, child(i,j).extension(t)) if child(i,j)
457
+ end
458
+ @rows_extensions[i] = a
459
+ end
460
+ debug "Grid layout rows #{@rows_extensions.inspect}"
461
+ @columns_extensions = []
462
+ columns.times do |j|
463
+ a = [0,0,0,0]
464
+ columns.times do |i|
465
+ update_extensions(a, child(i,j).extension(t)) if child(i,j)
466
+ end
467
+ @columns_extensions[j] = a
468
+ end
469
+ debug "Grid layout columns #{@columns_extensions.inspect}"
470
+ end
471
+
472
+ # Computes the positions of the various columns/rows, so that
473
+ # all the objects in the same line/column share the same size
474
+ # in both directions -- that is, all plots are identical.
475
+ def compute_positions(t)
476
+ compute_extensions(t) unless @rows_extensions
477
+ frame = @object.outer_frame(t)
478
+ @rows_positions = []
479
+ vert_ext = 0
480
+ vert_top = []
481
+ vert_bottom = []
482
+ for f in @rows_extensions
483
+ a = Dimension.absolute_to_relative(f,frame)
484
+ vert_top << a[2]
485
+ vert_bottom << a[3]
486
+ vert_ext += a[2] + a[3]
487
+ end
488
+
489
+ # Now, vert_top and vert_bottom contain the necessary
490
+ # spacing above and below each element, in relative size.
491
+ height = (1.0 - vert_ext)/rows
492
+ debug "Grid layout height #{height}"
493
+ current = 0
494
+ rows.times do |i|
495
+ new = current + vert_top[i] + height + vert_bottom[i]
496
+ @rows_positions << [0,0, # ignored
497
+ current, 1 - new]
498
+ current = new
499
+ end
500
+
501
+ debug "Grid layout rows positions #{@rows_positions.inspect}"
502
+
503
+ # Now, rows_positions contains the vertical position of
504
+ # the given row.
505
+
506
+ # We can now do the same for the horizontal positions:
507
+ @columns_positions = []
508
+ horiz_ext = 0
509
+ horiz_left = []
510
+ horiz_right = []
511
+ for f in @columns_extensions
512
+ a = Dimension.absolute_to_relative(f,frame)
513
+ horiz_left << a[0]
514
+ horiz_right << a[1]
515
+ horiz_ext += a[0] + a[1]
516
+ end
517
+ debug "Grid horiz left #{ horiz_left.inspect }"
518
+ debug "Grid horiz right #{horiz_right.inspect}"
519
+
520
+ # Now, horiz_left and horiz_right contain the necessary
521
+ # spacing above and below each element, in relative size.
522
+ width = (1.0 - horiz_ext)/columns
523
+ debug "Grid layout width #{width}"
524
+ current = 0
525
+ columns.times do |i|
526
+ new = current + horiz_left[i] + width + horiz_right[i]
527
+ @columns_positions << [current, 1 - new, 0, 0]
528
+ current = new
529
+ end
530
+ debug "Grid layout columns positions #{@columns_positions.inspect}"
531
+
532
+ end
533
+
534
+ # Returns the extension for the given child.
535
+ def child_extension(child,t)
536
+ compute_extensions(t) unless @columns_extensions
537
+ row,col = child_position(child)
538
+ a = @rows_extensions[row].dup
539
+ # We override the horizontal extensions with the
540
+ # @columns_extensions
541
+ a[0] = @columns_extensions[col][0]
542
+ a[1] = @columns_extensions[col][1]
543
+ return a
544
+ end
545
+
546
+ # Returns the frame allocated for one item of the layout.
547
+ def child_frame(t, row,col = nil)
548
+ if row.is_a? Layout # Row is a child, actually...
549
+ return child_frame(t, *child_position(row))
550
+ end
551
+
552
+ compute_positions(t) unless @rows_positions
553
+ a = @rows_positions[row].dup
554
+ a[0] = @columns_positions[col][0]
555
+ a[1] = @columns_positions[col][1]
556
+ return a
557
+ end
558
+
559
+
560
+ end
561
+
562
+ # The layout that should be used for every single object
563
+ # in a grid. Should.
564
+ class GridItemLayout < SimpleLayout
565
+
566
+ # Returns the object's frame.
567
+ def legend_specs(t = nil)
568
+ # To compute the legend specification,
569
+ a = parent.child_extension(self, t)
570
+
571
+ frames = @object.outer_frame(t)
572
+ padding = Dimension.absolute_to_relative(a, frames)
573
+
574
+ padding_prefs = @object.layout_preferences.
575
+ padding_relative_values(frames)
576
+
577
+ update_extensions(padding, padding_prefs)
578
+ my_frame = parent.child_frame(t, self)
579
+
580
+ debug "Child frame #{my_frame.inspect}"
581
+ debug "Child padding #{padding.inspect}"
582
+ debug "Child padding prefs #{padding_prefs.inspect}"
583
+
584
+ specs = {}
585
+ i = 0
586
+ for s in %w(left right top bottom)
587
+ specs["plot_#{s}_margin"] = padding[i] + my_frame[i]
588
+ i += 1
589
+ end
590
+ debug "Child specs #{specs.inspect}"
591
+ return specs
592
+ end
593
+ end
594
+
595
+ end