ctioga 1.11.1

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.
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