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,199 @@
1
+ # debug.rb, copyright (c) 2006 by Vincent Fourmond:
2
+ # The debugging functions for PlotMaker
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 'CTioga/log'
15
+
16
+
17
+ module CTioga
18
+
19
+ Version::register_svn_info('$Revision: 653 $', '$Date: 2007-11-15 21:58:42 +0100 (Thu, 15 Nov 2007) $')
20
+
21
+ # This module contains code to be included by PlotMaker for debugging.
22
+ # Even if it doesn't deserve a real module of it's own, it makes sense
23
+ # to have it separated anyway.
24
+ module Debug
25
+
26
+ include Tioga::FigureConstants
27
+ include Log
28
+
29
+ # These variables are scoped at the module level so
30
+ # that they are shared among all bits of code - as they can only be
31
+ # set in one place.
32
+ @@debug = false
33
+ @@debug_indent = 0
34
+ @@debug_indent_string = " "
35
+
36
+ # The function to use to display debugging information.
37
+ # Checks if we are in debug mode.
38
+ def debug_puts(str)
39
+ if @@debug
40
+ debug @@debug_indent_string * @@debug_indent +
41
+ str
42
+ end
43
+ end
44
+
45
+ # A function using the hooks defined by Bill to display what is
46
+ # happenning inside Tioga
47
+ def debug_figmaker(t)
48
+ t.def_enter_context_function {
49
+ debug_puts "Tioga: enter new context\t" +
50
+ frames_str(t)
51
+ @@debug_indent += 1
52
+ }
53
+ t.def_exit_context_function {
54
+ @@debug_indent -=1
55
+ debug_puts "Tioga: exit context\t" + frames_str(t)
56
+ }
57
+
58
+ t.def_enter_show_plot_function { |bounds|
59
+ debug_puts "Tioga: enter show_plot #{bounds[0]} #{bounds[1]} " +
60
+ "#{bounds[2]} #{bounds[3]}"
61
+ @@debug_indent += 1
62
+ }
63
+
64
+ t.def_exit_show_plot_function { |bounds|
65
+ @@debug_indent -=1
66
+ debug_puts "Tioga: exit show_plot #{bounds[0]} #{bounds[1]} " +
67
+ "#{bounds[2]} #{bounds[3]}"
68
+ }
69
+
70
+ t.def_enter_subfigure_function { |margins|
71
+ debug_puts "Tioga: enter subfigure #{margins[0]} #{margins[1]} " +
72
+ "#{margins[2]} #{margins[3]}"
73
+ debug_puts "Tioga: frames " + frames_str(t)
74
+ }
75
+
76
+ t.def_enter_subplot_function { |margins|
77
+ debug_puts "Tioga: enter subplot #{margins[0]} #{margins[1]} " +
78
+ "#{margins[2]} #{margins[3]}"
79
+ debug_puts "Tioga: frames " + frames_str(t)
80
+ }
81
+ end
82
+
83
+ def debug_patterns(t)
84
+ t.def_figure('Test_patterns') {
85
+ setup_real_size_bop(t)
86
+ test_pattern(t)
87
+ }
88
+ t.make_preview_pdf(t.figure_index('Test_patterns'))
89
+
90
+ t.def_figure('Test_patterns_right') {
91
+ setup_real_size_bop(t)
92
+ test_pattern_right(t)
93
+ }
94
+ t.make_preview_pdf(t.figure_index('Test_patterns_right'))
95
+ end
96
+
97
+ # This function is taken from Tioga; it is used to check the alignements
98
+ # of the TeX text and PDF image.
99
+ def test_pattern(t)
100
+ t.line_width = 1
101
+ t.stroke_frame
102
+ # we make it asymmetric to catch other kinds of problems
103
+ t.set_subframe('left' => 0.1 , 'right' => 0.05,
104
+ 'top' => 0.05, 'bottom' => 0.1)
105
+ centerx = t.bounds_xmin + 0.5 * t.bounds_width
106
+ chr_ht = t.default_text_height_dy
107
+ t.stroke_color = Red
108
+ t.line_width = 0.2
109
+ t.stroke_line(t.bounds_xmax, t.bounds_ymin, t.bounds_xmax, t.bounds_ymax)
110
+ t.stroke_line(centerx, t.bounds_ymin, centerx, t.bounds_ymax)
111
+ t.stroke_line(t.bounds_xmin, t.bounds_ymin, t.bounds_xmin, t.bounds_ymax)
112
+ y = t.bounds_ymin; dy = t.bounds_height / 10
113
+ scale = 0.85;
114
+ 10.times do
115
+ t.stroke_line(t.bounds_xmin, y, t.bounds_xmax, y)
116
+ t.show_text('text'=>"Left", 'x'=> t.bounds_xmin, 'y' => y,
117
+ 'scale'=>0.5+scale, 'justification' => LEFT_JUSTIFIED)
118
+ t.show_text('text'=>"Center", 'x'=> centerx, 'y' => y,
119
+ 'scale'=>1.8-scale, 'justification' => CENTERED)
120
+ t.show_text('text'=>"Right", 'x'=> t.bounds_xmax, 'y' => y,
121
+ 'scale'=>0.5+scale, 'justification' => RIGHT_JUSTIFIED)
122
+ scale = scale - 0.1
123
+ y = y + dy
124
+ end
125
+ t.stroke_line(t.bounds_xmin, t.bounds_ymax, t.bounds_xmax, t.bounds_ymax)
126
+ t.justification = CENTERED
127
+ t.show_text('text'=>"TeX and PDF Alignment Test Pattern",
128
+ 'side'=> TOP, 'pos' => 0.5, 'shift'=>0.3)
129
+ t.show_text('text'=>"Check for Proper Registration of Text and Graphics",
130
+ 'side'=>BOTTOM, 'pos' => 0.5, 'shift'=> 1.6, 'scale'=>0.4)
131
+ t.show_text(
132
+ 'text'=>'If needed, adjust by changing ' +
133
+ '{\sffamily FigureMaker.tex\_xoffset} ' +
134
+ 'and {\sffamily FigureMaker.tex\_yoffset}.',
135
+ 'side'=>BOTTOM, 'pos' => 0.5, 'shift'=> 3.9, 'scale'=>0.3)
136
+ end
137
+
138
+ # This function is taken from Tioga; it is used to check the alignements
139
+ # of the TeX text and PDF image.
140
+ def test_pattern_right(t)
141
+ t.line_width = 1
142
+ # t.stroke_frame
143
+ margin = 0.1
144
+ # we make it asymmetric to catch other kinds of problems
145
+ t.set_subframe('left' => 0.1 , 'right' => 0.05,
146
+ 'top' => 0.05, 'bottom' => 0.1)
147
+ centerx = t.bounds_xmin + 0.5 * t.bounds_width
148
+ chr_ht = t.default_text_height_dy
149
+ t.stroke_color = Red
150
+ t.line_width = 1
151
+ t.stroke_line(t.bounds_xmax, t.bounds_ymin, t.bounds_xmax, t.bounds_ymax)
152
+ t.stroke_line(centerx, t.bounds_ymin, centerx, t.bounds_ymax)
153
+
154
+ y = t.bounds_ymin; dy = t.bounds_height / 10
155
+ scale = 0.85;
156
+ 10.times do
157
+ t.stroke_line(centerx, y, t.bounds_xmax, y)
158
+ t.show_text('text'=>"Left", 'x'=> t.bounds_xmin, 'y' => y,
159
+ 'scale'=>0.5+scale, 'justification' => LEFT_JUSTIFIED)
160
+ t.show_text('text'=>"Center", 'x'=> centerx, 'y' => y,
161
+ 'scale'=>1.8-scale, 'justification' => CENTERED)
162
+ t.show_text('text'=>"Right", 'x'=> t.bounds_xmax, 'y' => y,
163
+ 'scale'=>0.5+scale, 'justification' => RIGHT_JUSTIFIED)
164
+ scale = scale - 0.1
165
+ y = y + dy
166
+ end
167
+ t.stroke_line(centerx, t.bounds_ymax, t.bounds_xmax, t.bounds_ymax)
168
+ t.justification = CENTERED
169
+ t.show_text('text'=>"TeX and PDF Alignment Test Pattern",
170
+ 'side'=> TOP, 'pos' => 0.5, 'shift'=>0.3)
171
+ t.show_text('text'=>"Check for Proper Registration of Text and Graphics",
172
+ 'side'=>BOTTOM, 'pos' => 0.5, 'shift'=> 1.6, 'scale'=>0.4)
173
+ t.show_text(
174
+ 'text'=>'If needed, adjust by changing ' +
175
+ '{\sffamily FigureMaker.tex\_xoffset} ' +
176
+ 'and {\sffamily FigureMaker.tex\_yoffset}.',
177
+ 'side'=>BOTTOM, 'pos' => 0.5, 'shift'=> 3.9, 'scale'=>0.3)
178
+ end
179
+
180
+ # Returns a string representing the 'options' of the FigureMaker object
181
+ # given.
182
+ def figmaker_options(t)
183
+ return '' unless @@debug
184
+ s = ""
185
+ for meth in t.methods.sort
186
+ if t.respond_to?((meth + '=').to_sym)
187
+ begin
188
+ s += "\n#{meth}: " + t.send(meth).to_s
189
+ rescue
190
+ # In case the method did need some arguments...
191
+ end
192
+ end
193
+ end
194
+ return s
195
+ end
196
+
197
+ end
198
+
199
+ end
@@ -0,0 +1,133 @@
1
+ # dimension.rb: a class handling relative/absolute dimensions
2
+ # copyright (c) 2007,2008 by Vincent Fourmond:
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details (in the COPYING file).
13
+
14
+ require 'CTioga/log'
15
+
16
+ module CTioga
17
+
18
+ Version::register_svn_info('$Revision: 831 $', '$Date: 2008-08-19 14:23:54 +0200 (Tue, 19 Aug 2008) $')
19
+
20
+ # A class which represents a dimension, either absolute or
21
+ # relative.
22
+ class Dimension
23
+
24
+ # TODO: we should extend this class to provide a comprehensive
25
+ # way to specify the position of a point and provide coordinate
26
+ # conversions to frame (including in postscript points) and
27
+ # figure coordinates.
28
+
29
+ include Tioga::Utils
30
+
31
+ # The type of the dimension :
32
+ # * :abs means absolute
33
+ # * :rel is relative
34
+ attr_accessor :type
35
+
36
+ # The actual dimension. Its meaning depends on the
37
+ # value of type:
38
+ # * :abs : the value is in bp points
39
+ # * :rel : the value is a fraction of the corresponding size
40
+ # in the given plot.
41
+ attr_accessor :value
42
+
43
+ # Creates a dimension. If the dimension contains a letter,
44
+ # it will be interpreted in terms of a TeX size.
45
+ # If it contains a percent - or nothing, it will be interpreted as
46
+ # a relative measure. If it is a float, it will be interpreted
47
+ # as a relative measure unless _type_ is not nil.
48
+ def initialize(spec, type = nil)
49
+ case spec
50
+ when /^\s*[\d.]+[a-zA-Z]+\s*$/
51
+ @type = :abs
52
+ @value = tex_dimension_to_bp(spec)
53
+ when /^\s*([\d.]+)\s*%\s*$/
54
+ @type = :rel
55
+ @value = $1.to_f * 0.01 # Value in percents
56
+ when Float
57
+ @value = spec
58
+ @type = type || :rel
59
+ when /^\s*([\d.]+)/
60
+ @value = spec.to_f
61
+ @type = type || :rel
62
+ end
63
+ end
64
+
65
+ # Converts self to a relative dimension. _frames_ is the frames
66
+ # of the current object (outer_frames) expressed in big points.
67
+ def to_relative(frames, side = :horizontal)
68
+ # The easy thing first:
69
+ return @value if @type == :rel
70
+ if side == :horizontal
71
+ return @value/((frames[1] - frames[0]).abs)
72
+ else
73
+ return @value/((frames[3] - frames[2]).abs)
74
+ end
75
+ end
76
+
77
+ # Converts self to an absolute dimension. _frames_ is the frames
78
+ # of the current object (outer_frames) expressed in big points.
79
+ def to_absolute(frames, side = :horizontal)
80
+ # The easy thing first:
81
+ return @value if @type == :abs
82
+ if side == :horizontal
83
+ return @value * ((frames[1] - frames[0]).abs)
84
+ else
85
+ return @value * ((frames[3] - frames[2]).abs)
86
+ end
87
+ end
88
+
89
+ # Scales the dimension by the given factor
90
+ def scale!(fact)
91
+ @value *= fact
92
+ return self
93
+ end
94
+
95
+
96
+ # Converts an absolute dimension in terms of a relative
97
+ # one, using the given frames.
98
+ def Dimension.absolute_to_relative(dimensions, frames)
99
+ ret_val = []
100
+ ret_val[0] = dimensions[0]/((frames[1] - frames[0]).abs)
101
+ ret_val[1] = dimensions[1]/((frames[1] - frames[0]).abs)
102
+ ret_val[2] = dimensions[2]/((frames[3] - frames[2]).abs)
103
+ ret_val[3] = dimensions[3]/((frames[3] - frames[2]).abs)
104
+ return ret_val
105
+ end
106
+
107
+ def self.update_extensions(extensions, new_ext)
108
+ 4.times do |i|
109
+ extensions[i] = new_ext[i] unless new_ext[i] < extensions[i]
110
+ end
111
+ return extensions
112
+ end
113
+ end
114
+
115
+ # A dimension that represents text sizes. Absolute has the same meaning,
116
+ # but relative values are relative to default_text_height_x/y
117
+ class TextDimension < Dimension
118
+
119
+ # These two functions don't make any sense here
120
+ undef :to_relative, :to_absolute
121
+
122
+ # Converts the dimension to figure coordinates in the given direction
123
+ def to_figure(t, direction)
124
+ if @type == :rel
125
+ return @value * t.send("default_text_height_d#{direction.to_s}")
126
+ else
127
+ return t.send("convert_output_to_figure_d#{direction.to_s}",
128
+ @value * 10)
129
+ end
130
+ end
131
+ end
132
+
133
+ end
@@ -0,0 +1,17 @@
1
+ # elements.rb, copyright (c) 2006 by Vincent Fourmond:
2
+ # The class describing a Tioga element
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 'CTioga/elements/base'
15
+ require 'CTioga/elements/curves'
16
+ require 'CTioga/elements/containers'
17
+ require 'CTioga/elements/tioga_primitives'
@@ -0,0 +1,84 @@
1
+ # base.rb: basis of all plottable elements
2
+ # copyright (c) 2006,2007,2008 by Vincent Fourmond:
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details (in the COPYING file).
13
+
14
+ require 'Dobjects/Dvector'
15
+ require 'CTioga/debug'
16
+ require 'CTioga/log'
17
+
18
+ module CTioga
19
+
20
+ Version::register_svn_info('$Revision: 818 $', '$Date: 2008-06-23 23:20:47 +0200 (Mon, 23 Jun 2008) $')
21
+
22
+ # The base class for every single object that has to appear at one
23
+ # point as a command in the tioga plot. It provides only one function:
24
+ # do(f), which will be used by subclasses to do what they need.
25
+
26
+ class TiogaElement
27
+ include Debug
28
+ include Log
29
+
30
+ # The parent element.
31
+ attr_accessor :parent
32
+
33
+ # this function must be called with a FigureMaker object to
34
+ # draw the contents of the TiogaElement onto it. It calls
35
+ # the function real_do, which should be redefined by the
36
+ # children. You can redefine do too if you need another
37
+ # debugging output.
38
+ def do(f)
39
+ debug "plotting #{self.inspect}"
40
+ real_do(f)
41
+ end
42
+
43
+ # this function returns true if the object needs a CurveStyle parameter.
44
+ def need_style?
45
+ return false
46
+ end
47
+
48
+ # We plot everything but parent. If a prefix is given, it is prepended
49
+ # to all lines but the first (for indentation)
50
+ def inspect(prefix="")
51
+ ret = "#<#{self.class.name}:\n"
52
+ for i in instance_variables
53
+ next if i == "@parent"
54
+ var = instance_variable_get(i)
55
+ ret += "#{prefix} - #{i} -> "
56
+ if var.is_a? TiogaElement
57
+ ret += "#{var.inspect("#{prefix} ")}\n"
58
+ else
59
+ ret += "#{var.inspect}\n"
60
+ end
61
+ end
62
+ ret += "#{prefix}>"
63
+ return ret
64
+ end
65
+ end
66
+
67
+
68
+ # A class to represent a small function call to FigureMaker. Can represent
69
+ # basically anything.
70
+ class TiogaFuncall < TiogaElement
71
+
72
+ # _symbol_ is the symbol to be called, and the remainder will be used
73
+ # as arguments for the call.
74
+ def initialize(symbol, *args)
75
+ @symbol = symbol
76
+ @args = args
77
+ end
78
+
79
+ def real_do(f)
80
+ f.send(@symbol, *@args)
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,578 @@
1
+ # containers.rb: plottable elements that hold other plottable elements
2
+ # copyright (c) 2006,2007,2008 by Vincent Fourmond:
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details (in the COPYING file).
13
+
14
+ require 'Dobjects/Dvector'
15
+ require 'CTioga/debug'
16
+ require 'CTioga/log'
17
+ require 'CTioga/plot_style'
18
+ require 'CTioga/layout'
19
+ require 'CTioga/legends'
20
+
21
+ module CTioga
22
+
23
+ Version::register_svn_info('$Revision: 839 $', '$Date: 2008-10-11 14:47:08 +0200 (Sat, 11 Oct 2008) $')
24
+
25
+ # A class that can hold several 'child' elements, such as
26
+ # the SubPlot class and the Region class. A missed funcall will
27
+ # result in a try to call the parent, if it exists.
28
+ class Container < TiogaElement
29
+ # The various properties defining the class
30
+ attr_reader :elements, :funcalls
31
+
32
+ # A rescale attribute
33
+ attr_accessor :rescale
34
+
35
+ # Whether or not to show legend for the current plot
36
+ attr_accessor :show_legend
37
+
38
+ # If this attribute is set, all legends here will be completely
39
+ # ignored.
40
+ attr_accessor :disable_legend
41
+
42
+ # The frame of the element, if that element is the root. Expressed
43
+ # in big points. Supposedly, nothing should stick out of this frame.
44
+ # If the element is not root, this should be nil.
45
+ #
46
+ # As usual, [left, right, top, bottom]
47
+ attr_accessor :root_frame
48
+
49
+ # The layout preferences for the object, that is, useful
50
+ # information that will be used by the object to make decisions
51
+ # about layouts.
52
+ attr_accessor :layout_preferences
53
+
54
+ # The layout !!! This is the one handling the object, but
55
+ # that does not necessarily mean that self == layout.root_object.
56
+ # This might even seldom be the case.
57
+ attr_accessor :layout
58
+
59
+ # Override the layouts opinion about where the
60
+ # object should be placed. Please note that there is no
61
+ # need for a layout in the case when this attribute is not nil
62
+ # and not empty. Used with care ;-) !
63
+ attr_accessor :force_position
64
+
65
+ # If true, the plot will keep its legend element, and
66
+ # the ones communicated by the children for itself.
67
+ # If false, legend informations are sent to the parent and simply
68
+ # ignored otherwise.
69
+ attr_accessor :accept_legend
70
+
71
+ def initialize(p = nil)
72
+ @parent = p
73
+
74
+ # elements to be given to tioga
75
+ @elements = []
76
+
77
+ # Used to add initial function calls.
78
+ @funcalls = []
79
+
80
+ # Used to store legends internally. It is an array of CurveStyle
81
+ @legend_info = []
82
+ # Whether to accept legends or forward them to the parent
83
+ @accept_legend = false
84
+ # Whether or not to show them
85
+ @show_legend = false
86
+
87
+ # A rescale factor:
88
+ @rescale = false
89
+
90
+ # The root frame:
91
+ @root_frame = nil
92
+
93
+ @layout_preferences = LayoutPreferences.new
94
+ end
95
+
96
+ # Converts the current layout using Layout#convert_layout
97
+ def convert_layout(cls)
98
+ if @layout
99
+ @layout = @layout.convert_layout(cls)
100
+ else
101
+ @layout = cls.new(self)
102
+ end
103
+ end
104
+
105
+ def add_elem(elem)
106
+ elem.parent = self
107
+ @elements << elem
108
+
109
+ # If the element has a style, then we assume it is a curve,
110
+ # and add legend accordingly
111
+ if elem.respond_to?(:style) and elem.style.has_legend?
112
+ add_legend_info(CurveLegend.new(elem.style))
113
+ end
114
+ end
115
+
116
+ def add_funcall(funcall)
117
+ funcall.parent = self
118
+ @funcalls << funcall
119
+ end
120
+
121
+ # If legends are disable, we drop the legend altogether.
122
+ # If we accept legends, add the legend, if it is not false.
123
+ # Else, forward it to the parents.
124
+ #
125
+ # _info_ may be either a CurveStyle or a LegendLine object.
126
+ def add_legend_info(info)
127
+ return unless info.is_a?(LegendItem)
128
+ return if @disable_legend
129
+ if @accept_legend
130
+ debug "Adding legend information #{info.inspect}"
131
+ @legend_info << info if info
132
+ elsif parent
133
+ debug "Forwarding legend to the parent"
134
+ parent.add_legend_info(info)
135
+ else
136
+ info "Legend information dropped - this might not be expected"
137
+ end
138
+ end
139
+
140
+ # Do the funcalls and some other initialization as well
141
+ def make_funcalls(t)
142
+ t.rescale(@rescale) if @rescale
143
+ for f in @funcalls
144
+ f.do(t)
145
+ end
146
+ end
147
+
148
+ def has_plots?
149
+ @elements.each {|e|
150
+ return true if e.is_a?(Curve2D)
151
+ if e.respond_to? :has_plots? and e.has_plots?
152
+ return true
153
+ end
154
+ }
155
+ return false
156
+ end
157
+
158
+ # The internal_get_boundaries is the function responsible for
159
+ # doing the real work behind getting the boundaries for objects.
160
+ # Some objects might want to use some boundaries but report other
161
+ # to their parents; they can rely on internal_get_boundaries to
162
+ # give the right result for them and modify get_boundaries to suit
163
+ # their needs.
164
+ def internal_get_boundaries
165
+ bounds = []
166
+ @elements.each do |e|
167
+ bounds.push(e.get_boundaries) if e.respond_to? :get_boundaries
168
+ end
169
+ b = Curve2D.compute_boundaries(bounds)
170
+ return b
171
+ end
172
+
173
+ # This function can be used by external objects to query
174
+ # the boundaries this object would like to report. The
175
+ # default behaviour is to not report anything to the parent.
176
+ def get_boundaries
177
+ return [0.0/0.0, 0.0/0.0, 0.0/0.0, 0.0/0.0]
178
+ end
179
+
180
+
181
+ # We redirect missed funcall to the @parent
182
+ def method_missing(meth, *args)
183
+ if @parent
184
+ debug "Redirecting call #{meth} to parent"
185
+ @parent.send(meth,*args)
186
+ else
187
+ raise NoMethodError.new("No such method: #{meth}",meth,args)
188
+ end
189
+ end
190
+
191
+ # Whether or not we should display a legend
192
+ def display_legend?
193
+ @show_legend && has_plots? && has_legends? && ( not @disable_legend)
194
+ end
195
+
196
+ end
197
+
198
+ # This class represent a subplot, a subfigure or the main plot.
199
+
200
+ class SubPlot < Container
201
+
202
+ # # Various textual objects laying around:
203
+ # attr_accessor :title, :xlabel, :ylabel
204
+
205
+ # # Ticks
206
+ # attr_accessor :xticks, :yticks
207
+
208
+ # various important things:
209
+ attr_accessor :legend_specs
210
+
211
+ # The actual boundaries used for making the plot. This element is only
212
+ # filled in at the exact moment of the plotting. It is provided so that
213
+ # subplots or curves may use it.
214
+ attr_reader :effective_bounds
215
+
216
+ # The amount of space that should be left between the given curve and
217
+ # the box. It is an array [left,right,top,bottom].
218
+ attr_accessor :plot_margins
219
+
220
+ # A PlotSyle object carrying information about the style of the plot,
221
+ # such as titles/legends/background color...
222
+ attr_accessor :plot_style
223
+
224
+
225
+ # # The appearance of the edgesax
226
+ # attr_accessor :edges
227
+
228
+ def number
229
+ return @elements.length
230
+ end
231
+
232
+ # Returns a purified version of #legend_specs with only plot margins
233
+ def plot_margins(specs)
234
+ ret = {}
235
+ for k in specs.keys
236
+ if k =~ /plot_(left|right|top|bottom)_margin/
237
+ ret[$1] = specs[k]
238
+ end
239
+ end
240
+ return ret
241
+ end
242
+
243
+ def initialize(type = :subplot, parent = nil)
244
+ super(parent)
245
+ # @type is the type of the element, :subfigure, :subplot or nil
246
+ # to indicate that it is the main plot ;-) !
247
+
248
+ # the direct parent of that element. If nil, it means that it's
249
+ # directly a subchild of the main plot.
250
+ @parent = parent
251
+
252
+ # Defaults to no margins (looks somehow less nice in many cases)
253
+ @plot_margins = [0.0,0.0,0.0,0.0]
254
+
255
+ # Some legend tweaking:
256
+ if type == :subplot || type == nil
257
+ @show_legend = true
258
+ @accept_legend = true
259
+ else
260
+ @show_legend = false
261
+ @accept_legend = false
262
+ end
263
+
264
+ # @title = Label.new(:title)
265
+ # @title.label = "A nice plot" unless parent
266
+
267
+ # @xlabel = Label.new(:xlabel)
268
+ # @xlabel.label = "$x$" unless parent
269
+
270
+ # @ylabel = Label.new(:ylabel)
271
+ # @ylabel.label = "$y$" unless parent
272
+
273
+ # @xticks = TickLabels.new(:xaxis_numeric_label)
274
+ # @yticks = TickLabels.new(:yaxis_numeric_label)
275
+
276
+
277
+ # the hash used by show_plot_with_legend: we inherit it from the parent
278
+ # if it exists
279
+ @legend_specs = if parent
280
+ parent.legend_specs.dup
281
+ else
282
+ {}
283
+ end
284
+
285
+ # User-specified boundaries (left, right, top, bottom)
286
+ @bounds = [nil,nil,nil,nil]
287
+ @effective_bounds = nil
288
+
289
+ # @edges = EdgesAndAxes.new(@xticks, @yticks)
290
+
291
+ @plot_style = PlotStyle.new(self, parent ? true : false)
292
+ end
293
+
294
+ # Returns the inner frames of the object in terms of absolute
295
+ # big points.
296
+ def inner_frame(t)
297
+ frame = outer_frame(t)
298
+
299
+ h = t.legend_defaults.dup
300
+ h.update @legend_specs
301
+ frame = Utils::apply_margin_to_frame(frame, h) if display_legend?
302
+ return frame
303
+ end
304
+
305
+ # The outer frame is the reference frame for the object. It is
306
+ # either the root frame (the whole plot) or the inner frame of the
307
+ # parent object. Objects should refrain from writing anything
308
+ # outside this outer frame.
309
+ def outer_frame(t)
310
+ # The outer frame is either the root frame
311
+ # or the inner_frame of the parent.
312
+ return @root_frame || parent.inner_frame(t)
313
+ end
314
+
315
+ def bound_left=(a)
316
+ @bounds[0] = a
317
+ end
318
+
319
+ def bound_right=(a)
320
+ @bounds[1] = a
321
+ end
322
+
323
+ def bound_top=(a)
324
+ @bounds[2] = a
325
+ end
326
+
327
+ def bound_bottom=(a)
328
+ @bounds[3] = a
329
+ end
330
+
331
+ # Used by children to notify that an axis shoudn't be used
332
+ # anymore.
333
+ #
334
+ # TODO: move this to plot_style...
335
+ def disable_axis(axis)
336
+ case axis
337
+ when :x
338
+ add_funcall(TiogaFuncall.new(:top_edge_type=, AXIS_LINE_ONLY))
339
+ when :y
340
+ add_funcall(TiogaFuncall.new(:right_edge_type=, AXIS_LINE_ONLY))
341
+ end
342
+ end
343
+
344
+
345
+ def set_margin(which, what)
346
+ @margins[which.to_s] = what.to_f
347
+ end
348
+
349
+ # this function computes the boundaries for the given plot...
350
+ def compute_boundaries
351
+ # We want to use the internal version of get_boundaries
352
+ bounds = internal_get_boundaries
353
+
354
+ # Increase the boundary with the margins
355
+ width = bounds[1] - bounds[0]
356
+ bounds[0] -= width * @plot_margins[0]
357
+ bounds[1] += width * @plot_margins[1]
358
+ height = bounds[2] - bounds[3]
359
+ bounds[2] += height * @plot_margins[2]
360
+ bounds[3] -= height * @plot_margins[3]
361
+
362
+ # overriding with user-specified values if applicable
363
+ nb_nans = 0
364
+ 4.times do |i|
365
+ if @bounds[i]
366
+ bounds[i] = @bounds[i]
367
+ end
368
+ # Replace NaNs with 0
369
+ if bounds[i].nan?
370
+ bounds[i] = 0
371
+ nb_nans += 1
372
+ end
373
+ end
374
+ # In the case all boundaries are NaN, we provide a 0->1 mapping
375
+ if nb_nans == 4
376
+ return [0,1,1,0]
377
+ end
378
+ return bounds
379
+ end
380
+
381
+ # Does the plot have legends ?
382
+ def has_legends?
383
+ return (not @legend_info.empty?)
384
+ end
385
+
386
+
387
+ # this function takes a FigureMaker object and returns the block of
388
+ # instructions corresponding to the current contents of the SubPlot.
389
+ # To actually plot it correctly, it just needs a further wrapping
390
+ # in subplot or subfigure.
391
+
392
+ def make_main_block(t)
393
+ if has_plots?
394
+ block = proc {
395
+ t.context {
396
+ # First compute the boundaries:
397
+ # We first check if the object was required to be somewhere
398
+ if @force_position and !@force_position.empty?
399
+ legend_specs = @force_position.to_frame("plot_%s_margin")
400
+ else
401
+ # Ask the layout.
402
+ legend_specs = @layout.legend_specs(t)
403
+ end
404
+ # We always wrap the plot in a show_plot_with_legend,
405
+ # even if there is no legends. That, anyway, is the
406
+ # job of the layout to see this.
407
+ debug "Plot specs: #{legend_specs.inspect}"
408
+ debug "Plot legends #{display_legend?.inspect}: "
409
+ debug " - @show_legend = #{@show_legend.inspect}"
410
+ debug " - @disable_legend = #{@disable_legend.inspect}"
411
+ debug " - has_plots? = #{has_plots?.inspect}"
412
+ debug " - has_legends? = #{has_legends?.inspect}"
413
+ debug "Reference frame (#{self.class}): #{Utils::frames_str(t)}"
414
+
415
+ margins = plot_margins(legend_specs)
416
+
417
+ t.context do
418
+ # The call has to be wrapped within a
419
+ # context call, as it turns the frame reference into
420
+ # the legend subframe specification afterwards...
421
+ t.set_subframe(margins)
422
+ @effective_bounds = compute_boundaries
423
+ debug "Plot boundaries: #{@effective_bounds.inspect}"
424
+ debug "Box frame (#{self.class}): #{Utils::frames_str(t)}"
425
+
426
+ @plot_style.show_edges(t, self)
427
+
428
+ t.show_plot(@effective_bounds) {
429
+ # output frame debug information./
430
+ debug "Plot frame (#{self.class}): #{Utils::frames_str(t)}"
431
+ if root_frame
432
+ debug "Root frame: #{Utils::framespec_str(root_frame)}"
433
+ end
434
+
435
+ debug "Inner frame: #{Utils::framespec_str(inner_frame(t))}"
436
+
437
+ @plot_style.show_background(t, self)
438
+ @plot_style.show_labels(t, self)
439
+
440
+ @elements.each do |e|
441
+ e.do(t)
442
+ end
443
+ # We remove all the legends here, as they should all be
444
+ # handled by the children, or forwarded here.
445
+ t.reset_legend_info
446
+ debug "Current figure has #{@legend_info.size} legends"
447
+ debug "Legends #{@legend_info.inspect}"
448
+ # for s in @legend_info
449
+ # t.save_legend_info(s.legend_info)
450
+ # end
451
+ }
452
+
453
+ # Now, additional axes, if applicable
454
+ t.context do
455
+ t.set_bounds(@effective_bounds)
456
+ # Call the stuff for additional axes.
457
+ @plot_style.edges.show_additional_axes(t, self)
458
+ end
459
+
460
+ end
461
+ @plot_style.legend_style.show_legends(t, legend_specs,
462
+ margins, @legend_info)
463
+ }
464
+ }
465
+ else
466
+ block = proc do
467
+ @elements.each do |e|
468
+ e.do(t)
469
+ end
470
+ end
471
+ end
472
+
473
+ return proc do
474
+ make_funcalls(t)
475
+ block.call
476
+ end
477
+
478
+ end
479
+
480
+ # this function sets up the appropriate environment and calls the block...
481
+ def do(t)
482
+ debug "Making main block for object: #{identify(self)}"
483
+ make_main_block(t).call
484
+ end
485
+
486
+ end
487
+
488
+ # The Region class handles the cases where the user wants some parts
489
+ # between curves to be coloured. In this case, all the curves which
490
+ # are included between the --region and --end options will
491
+ class Region < Container
492
+
493
+ # Style attributes
494
+ attr_accessor :region_color, :region_transparency
495
+
496
+
497
+ # Some general options
498
+ attr_accessor :dont_display, :region_debug
499
+
500
+ # Filling rules:
501
+ attr_accessor :invert_rule, :fill_twice
502
+
503
+ # We want to call the parent's disable legend.
504
+ undef :disable_legend, :disable_legend=
505
+ # And as well for rescale.
506
+ undef :rescale=, :rescale
507
+
508
+ def initialize(parent = nil)
509
+ super
510
+ @region_color = [0.9,0.9,0.9] # Very light gray.
511
+ @region_transparency = 0
512
+ @dont_display = false
513
+ end
514
+
515
+ # Fills the regions using the index to choose the closing path
516
+ # for the first curve and opposed ones for next curves.
517
+ def fill_region(t,i)
518
+ # We need to wrap in a context, else the clipping path screws up
519
+ # everyting
520
+ t.context do
521
+ debug "Filling with parameter #{i}"
522
+ bound = internal_get_boundaries
523
+ for el in @elements
524
+ if el.respond_to?(:make_path)
525
+ el.make_path(t)
526
+ # We close the path with a line coming back alternatively to
527
+ # the top and the bottom
528
+ close_to = bound[i % 2 + 2]
529
+ el.close_path(t, close_to)
530
+ i += 1
531
+
532
+ # If the debugging flag is set, we heavily tamper with the
533
+ # style of the elements to force the fill where
534
+ # we would clip.
535
+ if @region_debug
536
+ el.style.fill_type = close_to
537
+ el.style.fill_transparency = 0.8
538
+ el.style.fill_color = el.style.color || el.style.marker_color
539
+ end
540
+ end
541
+ t.clip
542
+ end
543
+
544
+ t.fill_color = @region_color
545
+ t.fill_transparency = @region_transparency
546
+ t.fill_rect(bound[0], bound[3], bound[1] - bound[0],
547
+ bound[2] - bound[3])
548
+ end
549
+ end
550
+
551
+ # Region must return its true boundaries.
552
+ def get_boundaries
553
+ internal_get_boundaries
554
+ end
555
+
556
+ def do(t)
557
+ debug "Region: plotting elements"
558
+ make_funcalls(t)
559
+ # We fill first, as it looks way better
560
+ if @fill_twice or (! @invert_rule)
561
+ fill_region(t,0)
562
+ end
563
+ if @fill_twice or @invert_rule
564
+ fill_region(t,1)
565
+ end
566
+ # Then, we plot the elements, unless we were told not to
567
+ # by --region-dont-display
568
+ unless @dont_display
569
+ @elements.each do |e|
570
+ e.do(t)
571
+ end
572
+ end
573
+
574
+ end
575
+
576
+ end
577
+
578
+ end