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,230 @@
1
+ # plot_style.rb : an abstraction for styles of whole plots
2
+ # Copyright (C) 2008 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.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ require 'CTioga/utils'
19
+ require 'CTioga/debug'
20
+ require 'CTioga/log'
21
+ require 'CTioga/axes'
22
+ require 'CTioga/legends'
23
+
24
+ module CTioga
25
+
26
+ Version::register_svn_info('$Revision: 966 $', '$Date: 2009-06-02 19:02:50 +0200 (Tue, 02 Jun 2009) $')
27
+
28
+ # The PlotStyle class is an abstraction for whole-plot styles,
29
+ # storing for instance information about axes/ticks (delegated
30
+ # to an EdgesAndAxes object), background color, and so on...
31
+ class PlotStyle
32
+
33
+ include Debug
34
+ include Log
35
+
36
+ # An EdgesAndAxes object attached to the plot
37
+ attr_accessor :edges
38
+
39
+ # Various textual objects laying around:
40
+ attr_accessor :title, :xlabel, :ylabel
41
+
42
+ # X and Y tick labels
43
+ attr_accessor :xticks, :yticks
44
+
45
+ # The target SubPlot object
46
+ attr_accessor :target_plot
47
+
48
+ # The background color, if applicable
49
+ attr_accessor :background_color
50
+
51
+ # The style with which to display legends
52
+ attr_accessor :legend_style
53
+
54
+ # A watermark in the background
55
+ attr_accessor :watermark_text
56
+
57
+ # The color of the watermark
58
+ attr_accessor :watermark_color
59
+
60
+ # The scale of the watermark
61
+ attr_accessor :watermark_scale
62
+
63
+ def initialize(target_plot = nil, subplot = false)
64
+ @title = Label.new(:title)
65
+ @title.label = "A nice plot" unless subplot
66
+
67
+ @xlabel = Label.new(:xlabel)
68
+ @xlabel.label = "$x$" unless subplot
69
+
70
+ @ylabel = Label.new(:ylabel)
71
+ @ylabel.label = "$y$" unless subplot
72
+
73
+ @xticks = TickLabels.new(:xaxis_numeric_label)
74
+ @yticks = TickLabels.new(:yaxis_numeric_label)
75
+
76
+ @edges = EdgesAndAxes.new(@xticks, @yticks)
77
+
78
+ @target_plot = target_plot
79
+
80
+ @background_color = false
81
+
82
+ @legend_style = LegendStyle.new
83
+
84
+ @watermark_scale = 0.15
85
+ end
86
+
87
+ # Displays edges and ticks for the given object
88
+ def show_edges(t, container = @target_plot)
89
+ @edges.setup(t, container)
90
+ for l in [@xticks, @yticks]
91
+ l.show(t)
92
+ end
93
+ end
94
+
95
+ # Sets up the various parameters for titles and labels
96
+ def show_labels(t, container = @target_plot)
97
+ # Show labels
98
+ for l in [@title, @xlabel, @ylabel]
99
+ l.show(t)
100
+ debug "Extension -> #{l.extension(t).inspect}"
101
+ end
102
+ end
103
+
104
+ # Displays background (background color, grids)
105
+ def show_background(t, container = @target_plot)
106
+ if @background_color
107
+ t.fill_color = @background_color
108
+ t.fill_frame
109
+ end
110
+
111
+ # We draw a watermark text at the back of the plot.
112
+ if @watermark_text
113
+ x = t.convert_frame_to_figure_x(0.5)
114
+ y = t.convert_frame_to_figure_y(0.5)
115
+
116
+ delta_y = t.convert_frame_to_figure_dy(@watermark_scale)
117
+ text_scale = delta_y/t.default_text_height_dy
118
+
119
+ lines = @watermark_text.split(/\n|\\n/)
120
+ i = + (lines.size-1)/2.0
121
+ for text in lines
122
+ t.show_marker('string' => text,
123
+ 'color' => @watermark_color || [0.5,0.5,0.5],
124
+ 'x' => x, 'y' => y + delta_y * i,
125
+ 'scale' => text_scale)
126
+ i -= 1
127
+ end
128
+ end
129
+
130
+ edges.show_axis_lines(t, container)
131
+ end
132
+
133
+ # Hides axis and all edges for the given sides.
134
+ # Careful, as this also disables the children's axes and edges
135
+ def disable_all_axis_and_edges(*which)
136
+ for w in which
137
+ @edges.set_edges_visibility(w, false)
138
+ @edges.axis(w).visible = false
139
+ end
140
+ end
141
+
142
+
143
+ # Sets quickly all X and Y labels
144
+ def set_xy_labels(xlabel, ylabel)
145
+ @xlabel.label = xlabel
146
+ @ylabel.label = ylabel
147
+ end
148
+
149
+
150
+ def hide_axis_and_edges(*which)
151
+ for w in which
152
+ @edges.set_axis_and_edges_style(w, AXIS_HIDDEN)
153
+ end
154
+ end
155
+
156
+
157
+ # TODO (IMPORTANT !): simplify the notion of edges and axes: the user
158
+ # shouldn't need to know the difference between edges and axes.
159
+
160
+
161
+ # A very nice-and-convenient way to set quickly axes properties.
162
+ # _which_ is either :x or :y, *or* :left, :top, :right, and :bottom,
163
+ # in which case the style applies only to the given edge or axis
164
+ #
165
+ def set_axis_style(which, style)
166
+ # We set various parameters according to the given style
167
+ for s in style.split(/,/)
168
+ case s
169
+ when /none/i
170
+ hide_axis_and_edges(which)
171
+ when /[xy]?=?0/i, /ori?g(in)?/i
172
+ @edges.set_edges_visibility(which, false)
173
+ @edges.axis(which).ticks_inside = false
174
+ @edges.axis(which).ticks_outside = true
175
+ @edges.axis(which).loc = (which == :x ? AT_Y_ORIGIN : AT_X_ORIGIN)
176
+ when /both/ # Both sides visible
177
+ # We make sure the edges are visible
178
+ @edges.set_edges_visibility(which, true)
179
+ when /left/i, /right/i
180
+ if which == :x
181
+ warn "Axis style #{s} can only apply to the Y axis, ignoring"
182
+ else
183
+ @edges.set_edges_visibility(which, false)
184
+ @edges.axis(which).loc = ( s =~ /left/i ? LEFT : RIGHT)
185
+ end
186
+ when /top/i, /bottom/i
187
+ if which == :y
188
+ warn "Axis style #{s} can only apply to the X axis, ignoring"
189
+ else
190
+ @edges.set_edges_visibility(which, false)
191
+ @edges.axis(which).loc = ( s =~ /top/i ? TOP : BOTTOM)
192
+ end
193
+ # Now, stylistic information rather than position:
194
+ when /hidden/i
195
+ @edges.set_axis_and_edges_style(which, AXIS_HIDDEN)
196
+ when /line/i
197
+ @edges.set_axis_and_edges_style(which, AXIS_LINE_ONLY)
198
+ when /ticks/i
199
+ @edges.set_axis_and_edges_style(which, AXIS_WITH_TICKS_ONLY)
200
+ when /majornum/i
201
+ @edges.
202
+ set_axis_and_edges_style(which,
203
+ AXIS_WITH_MAJOR_TICKS_AND_NUMERIC_LABELS)
204
+ when /major/i
205
+ @edges.set_axis_and_edges_style(which, AXIS_WITH_MAJOR_TICKS_ONLY)
206
+ when /full/i
207
+ @edges.set_axis_and_edges_style(which,
208
+ AXIS_WITH_TICKS_AND_NUMERIC_LABELS)
209
+ else
210
+ warn "Axis style #{s} not understood, ignoring"
211
+ end
212
+ end
213
+ end
214
+
215
+ # Creates a deep copy of the style object, and give it a new
216
+ # container.
217
+ def deep_copy(new_target = nil)
218
+ old_target = @target_plot
219
+ @target_plot = nil
220
+ new_object = Marshal::load(Marshal::dump(self))
221
+ new_object.target_plot = new_target
222
+ @target_plot = old_target
223
+ return new_object
224
+ end
225
+
226
+
227
+
228
+ end
229
+
230
+ end
@@ -0,0 +1,1677 @@
1
+ # plotmaker.rb: The main class for making plots
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
+
15
+ # TODO, the main one:
16
+ #
17
+ # It currently is a pain to make complex plots with ctioga. A real
18
+ # pain. What could be done to improve the situation ?
19
+ #
20
+ # * hide the difference between edges and axes.
21
+ # * the layout mechanism is not comfortable enough to work with, especially
22
+ # with the need for relative positioning.
23
+ #
24
+ # Would it be possible to allow for the 'real size' to be determined
25
+ # *afterwards* ?
26
+
27
+ # TODO, an even bigger one:
28
+ # Switch to a real command-based plotting program:
29
+ # - any single operation that is realized by ctioga would be a command
30
+ # - every single of these commands would take a given (fixed) number of
31
+ # parameters (we should take care about boolean stuff)
32
+ # - every command would be of course reachable as command-line options
33
+ # but it could also be within files
34
+ # - in these files, provide an additional mechanism for quickly defining
35
+ # variables and do variable substitution.
36
+ # - one command (plus arguments) per line, with provisions for
37
+ # line-splitting
38
+ # - allow some kind of 'include' directives (that would also be used for
39
+ # cmdline inclusion of files)
40
+ # - command-line arguments and command files could intermix (that *would*
41
+ # be fun, since it would allow very little changes to a command-line
42
+ # to change significantly the look of a file...!)
43
+ # - command files would be specified using @file ?
44
+ # - in the absence of --name commands, output would go to the named file ?
45
+ # - LONG TERM: allow conditionals and variable
46
+ # definition/substitution on command-line ?
47
+ #
48
+ # Each command could take *typed* arguments. That would allow typed
49
+ # variables along with a string-to-type conversion ? (is that useful ?)
50
+ #
51
+ # Provide *optional* hash-like arguments that probably could not be used
52
+ # in the command-line, but could be in the file.
53
+ #
54
+ # Provide self-documentation in each and every command
55
+ #
56
+ # Manipulations of a buffer stack - including mathematical
57
+ # expressions; provide commands to only *load* a file, but not
58
+ # necessarily import it.
59
+ #
60
+ # Provide a way to 'save' a command-line into a command-file.
61
+ #
62
+ # Write as many test suites as possible ??
63
+ #
64
+ # Merge Metabuilder and Backends into the ctioga code base. There's
65
+ # no need for extra complexity.
66
+ #
67
+ # That requires a huge amount of work, but on the other hand, that
68
+ # would be much more satisfactory than the current mess.
69
+ #
70
+ # Commands would be part of "groups".
71
+ #
72
+ # Release a new version of ctioga before that.
73
+ #
74
+ # Don't rely on huge mess of things !
75
+ #
76
+ # Then, I could have some small fun and write a
77
+
78
+
79
+
80
+ # Very important for command-line parsing
81
+ require 'optparse'
82
+ require 'Tioga/tioga'
83
+ require 'Tioga/Utils'
84
+
85
+ # Information about style
86
+ require 'CTioga/styles'
87
+ # Moving arrays
88
+ # require 'CTioga/movingarrays'
89
+ # And, most important of all, elements
90
+ require 'CTioga/elements'
91
+
92
+ # The debugging facility
93
+ require 'CTioga/debug'
94
+ require 'CTioga/log'
95
+ require 'CTioga/utils'
96
+ require 'CTioga/layout'
97
+
98
+
99
+ # The backends
100
+ require 'CTioga/backends'
101
+
102
+ # Support for themes
103
+ require 'CTioga/themes'
104
+ require 'CTioga/axes'
105
+ require 'CTioga/structures'
106
+
107
+ # Legends
108
+ require 'CTioga/legends'
109
+
110
+
111
+ # for interpreting the CTIOGA environment variable
112
+ require 'shellwords'
113
+ require 'MetaBuilder/metabuilder'
114
+
115
+
116
+ module CTioga
117
+
118
+ Version::register_svn_info('$Revision: 966 $', '$Date: 2009-06-02 19:02:50 +0200 (Tue, 02 Jun 2009) $')
119
+
120
+
121
+
122
+ # The regular expression saying that what we see on the command-line
123
+ # means "use default value".
124
+ DEFAULT_RE = /^\s*(auto|default)\s*$/i
125
+
126
+ # The regular expression saying that what we see on the command-line
127
+ # means "disable".
128
+ DISABLE_RE = /^\s*(no(ne)?|off)\s*$/i
129
+
130
+ # The regular expression saying that what we see on the command-line
131
+ # means "true".
132
+ TRUE_RE = /^\s*(true|yes|on)\s*$/i
133
+
134
+ # A small function to help quoting a string for inclusion
135
+ # in a pdfTeX primitive.
136
+ def self.pdftex_quote_string(str)
137
+ return str.gsub(/([%#])|([()])|([{}~_^])|\\/) do
138
+ if $1
139
+ "\\#{$1}"
140
+ elsif $2 # Quoting (), as they can be quite nasty !!
141
+ "\\string\\#{$2}"
142
+ elsif $3
143
+ "\\string#{$3}"
144
+ else # Quoting \
145
+ "\\string\\\\"
146
+ end
147
+ end
148
+ end
149
+
150
+
151
+ # This class is responsible for reading the command-line, via it's parse
152
+ # function, and to actually turn it into nice Tioga commands to make
153
+ # even nicer graphes.
154
+ class PlotMaker
155
+
156
+ # PlotMaker is now handled (at least partially) by MetaBuilder.
157
+ # That should save a lot of code.
158
+
159
+ include MetaBuilder::DescriptionInclude
160
+ extend MetaBuilder::DescriptionExtend
161
+
162
+ describe 'test', "A test class", <<EOD
163
+ A class to test visually the effects of different stuff
164
+ EOD
165
+
166
+ include SciYAG
167
+ include Tioga
168
+
169
+ include Debug
170
+ include Log
171
+ include Utils # For the safe_float function
172
+
173
+ # For dimension conversion and TeX quoting.
174
+ include Tioga::Utils
175
+
176
+ # For the backend handling:
177
+ include CTioga::Backends
178
+
179
+ # Support for Themes:
180
+ include Themes
181
+
182
+ # Axes, edges, labels, tick labels:
183
+ include Axes
184
+
185
+
186
+ # these are basically the attributes which are modified directly
187
+ # on the command-line
188
+ attr_writer :fig_name, :cleanup, :legend
189
+
190
+ # The command-line parser
191
+ attr_reader :parser
192
+
193
+ # The last Curve object on the stack
194
+ attr_reader :last_curve
195
+
196
+ # Whether we are trying to do real-size PDF or not. When set, it
197
+ # is the size of the PDF
198
+ attr_accessor :real_size
199
+
200
+ # The current object
201
+ attr_accessor :current_object
202
+
203
+ # Whether we are making a PNG file
204
+ attr_accessor :png
205
+
206
+ # The PNG's size:
207
+ attr_accessor :png_size
208
+
209
+ # Whether to create separate legends
210
+ attr_accessor :separate_legends
211
+
212
+ # Whether to automatically add a legend to all curves
213
+ attr_accessor :autolegends
214
+
215
+ def initialize
216
+ # The first thing to do is to setup logging, as you really
217
+ # want to be able to communicate with users, don't you ?
218
+ init_logger
219
+
220
+
221
+ @args = [] # Holding the command-line
222
+ @parser = OptionParser.new
223
+
224
+ initialize_themes
225
+
226
+ # Whether the plots should be interpolated.
227
+ @interpolate = false
228
+
229
+ @line_width = nil
230
+
231
+ @fig_name = "Plot"
232
+
233
+ # Initialize the backend structure:
234
+ init_backend_structure
235
+
236
+ init_axes
237
+
238
+ # now, the elements for the structure of the plot:
239
+ @root_object = SubPlot.new
240
+ # We start with a very simple layout
241
+ SimpleLayout.new(@root_object)
242
+
243
+ # the current object is the one to which we'll add plot elements.
244
+ @current_object = @root_object
245
+
246
+ # @legend is holding the text attached to the current item being
247
+ # plotted; it has to be cleared manually after every item processed
248
+ @legend = nil
249
+
250
+ # general purpose variables:
251
+ @cleanup = true # Now, cleaning is done by default !
252
+ @viewer = false
253
+
254
+ # Some options we could set on the command line
255
+ @init_funcalls = []
256
+
257
+ # Whether to provide default legends. On by default
258
+ @autolegends = true
259
+
260
+ @command_line = ""
261
+
262
+ @real_size = "12cmx12cm"
263
+
264
+ # If set, should be the size of the TeX font
265
+
266
+ @tex_fontsize = nil
267
+
268
+ # The frame sides for the setup_real_size function
269
+ @frame_sides = [0.1,0.9,0.9,0.1]
270
+ # Override this, as the layout scheme makes it more or less
271
+ # obsolete
272
+ @frame_sides = [0,1,1,0]
273
+
274
+ # If set, we create separate legend informations:
275
+ @separate_legend = false
276
+
277
+ # The array holding the styles.
278
+ @separate_legend_styles = []
279
+ # The size of the produced PDF in PDF points.
280
+ @separate_legend_width = 12
281
+ @separate_legend_height = 8
282
+
283
+ # Whether to mark the command-line in the PDF file
284
+ @mark = true # On by default, really useful !!!
285
+
286
+ # The LaTeX preamble:
287
+ @preamble = ""
288
+
289
+ # The last curve used:
290
+ @last_curve = nil
291
+
292
+ # Specifications for the --next stuff: an array, the first
293
+ # element is the class to be created for children and the
294
+ # rest are arguments to be added at the beginning.
295
+ @next_specs = [SubPlot, :subplot]
296
+
297
+ # A block to be run on both the old and new object
298
+ # when --next is encountered.
299
+ @next_block = nil
300
+
301
+ # Whether we automatically start a --next stuff on each spec
302
+ # or even on each dataset
303
+ @auto_next = false
304
+
305
+ # We don't make any PNG file:
306
+ @png = false
307
+
308
+ # The standard PNG density...
309
+ @png_oversampling = 2
310
+
311
+ # We don't produce SVG output by default
312
+ @svg = false
313
+
314
+ # The default padding;
315
+ @default_padding = []
316
+ 4.times do
317
+ @default_padding << Dimension.new(0.05)
318
+ end
319
+
320
+ # And we use it :
321
+ use_default_padding
322
+
323
+ prepare_option_parser
324
+ end
325
+
326
+ # Sets the given's object padding to the current default:
327
+ def use_default_padding(obj = nil)
328
+ obj = current_object unless obj
329
+ 4.times do |i|
330
+ obj.layout_preferences.padding[i] = @default_padding[i].dup
331
+ end
332
+ debug "Setting padding for #{identify(obj)} to " +
333
+ obj.layout_preferences.padding.inspect
334
+ end
335
+
336
+
337
+ # Adds the given object to the current's stack and set it
338
+ # as the current object, so that it will receive further
339
+ # children
340
+ def enter_child_object(object)
341
+ @current_object.add_elem(object)
342
+ self.current_object = object
343
+ end
344
+
345
+ # Goes out from a child object to its parent. Returns the child
346
+ # object. Issues a warning if already at top level (in which case
347
+ # it returns the current object)
348
+ def leave_child_object
349
+ prev = current_object
350
+ if current_object.parent
351
+ self.current_object = current_object.parent
352
+ else
353
+ warn "--end while in top level"
354
+ end
355
+ return prev
356
+ end
357
+
358
+
359
+ # Returns the current object's plot style
360
+ def current_plot_style
361
+ return current_object.plot_style
362
+ end
363
+
364
+
365
+
366
+ # This function reads a configuration file and executes it's
367
+ # statements in the scope of a local module, which is then
368
+ # included in this instance, and in the backend's instances.
369
+ def read_config_file(file)
370
+ f = File.open(file)
371
+ info "Reading config file #{file}"
372
+ lines = f.readlines
373
+ lines << "\nend\n"
374
+ lines.unshift "module CTiogaRC\n"
375
+ code = lines.join
376
+ eval code
377
+ extend CTiogaRC
378
+ for b_e in backends.values
379
+ b_e.extend CTiogaRC
380
+ end
381
+ # This is for the compute_formula function.
382
+ Dvector.extend CTiogaRC
383
+ end
384
+
385
+ CONFIG_FILE_NAME = ".ctiogarc"
386
+
387
+ # Looks for a configuration file, either in the current directory
388
+ # or in the HOME directory.
389
+ def lookup_config_file
390
+ if File.readable? CONFIG_FILE_NAME
391
+ return CONFIG_FILE_NAME
392
+ end
393
+
394
+ if ENV.has_key?('HOME')
395
+ home_rc = File.join(ENV['HOME'], CONFIG_FILE_NAME)
396
+ if File.readable?(home_rc)
397
+ return home_rc
398
+ end
399
+ end
400
+ return nil
401
+ end
402
+
403
+ # Sets the frame of the root object. This is absolutely
404
+ # necessary for layout computation. Failure to do so will
405
+ # result in failed plots.
406
+ def set_root_frame(*frames)
407
+ @root_object.root_frame = frames
408
+ end
409
+
410
+ # sets up the FigureMaker object to use for real size. Uses the
411
+ # @real_size instance variable as a source for the size
412
+ def setup_real_size(t)
413
+ # Get the width and height from @real_size
414
+ sizes = @real_size.split("x").collect {|s|
415
+ tex_dimension_to_bp(s)
416
+ }
417
+
418
+ t.def_enter_page_function {
419
+ t.page_setup(*sizes)
420
+ t.set_frame_sides(*@frame_sides)
421
+ set_root_frame(sizes[0] * @frame_sides[0],
422
+ sizes[0] * @frame_sides[1],
423
+ sizes[1] * @frame_sides[2],
424
+ sizes[1] * @frame_sides[3])
425
+ }
426
+
427
+ # Setting label and title scale to 1
428
+ t.title_scale = 1
429
+ t.xlabel_scale = 1
430
+ t.ylabel_scale = 1
431
+ end
432
+
433
+ # Reads a configuration file if one is found.
434
+ def read_config
435
+ if lookup_config_file
436
+ read_config_file(lookup_config_file)
437
+ end
438
+ end
439
+
440
+
441
+ # Push a function call onto the stack of the current element
442
+ def add_elem_funcall(sym, *args)
443
+ @current_object.add_funcall(TiogaFuncall.new(sym, *args))
444
+ end
445
+
446
+ # Push a function call onto the stack of things we should do
447
+ # just after creating the FigureMakerobject
448
+ def add_init_funcall(sym, *args)
449
+ @init_funcalls << TiogaFuncall.new(sym, *args)
450
+ end
451
+
452
+ # Forwards the boundary settings to the current object.
453
+ def set_bounds(which, val)
454
+ method = ("bound_#{which}=").to_sym
455
+ @current_object.send(method, val)
456
+ end
457
+
458
+ def set_range(a,b,str)
459
+ first,last = str.split(/\s*:\s*/)
460
+ first = first.to_f if first
461
+ last = last.to_f if last
462
+ set_bounds(a,first)
463
+ set_bounds(b,last)
464
+ end
465
+
466
+ # Splits a text into four components, expanding if necessary:
467
+ # * if there is only one element, all four become this one
468
+ # * if there are two: w,h, it becomes w,w,h,h
469
+ # * if there are three, the last element is duplicated.
470
+ def expand_sides(txt)
471
+ ary = txt.split(/\s*,\s*/)
472
+ case ary.size
473
+ when 1
474
+ return [ary[0], ary[0], ary[0], ary[0]]
475
+ when 2
476
+ return [ary[0], ary[0], ary[1], ary[1]]
477
+ when 3
478
+ return [ary[0], ary[1], ary[2], ary[2]]
479
+ else
480
+ return ary
481
+ end
482
+ end
483
+
484
+ def margins_from_text(txt)
485
+ ary = expand_sides(txt).map {|s| s.to_f}
486
+ return ary
487
+ end
488
+
489
+ # In the order: left, right, bottom, top, to suit Tioga's
490
+ # default positioning of the text along the axis.
491
+ def set_frame_margins(val)
492
+ @frame_sides = [val[0], 1.0 - val[1], 1.0 - val[3], val[2]]
493
+ end
494
+
495
+ # This function prepares the parser by giving it the arguments and
496
+ # some small help text going along with them. This function will
497
+ # unfortunately get really large with time, but there's nothing
498
+ # to do about it.
499
+ def prepare_option_parser
500
+ theme_prepare_parser(@parser)
501
+
502
+ @parser.separator "\nLaTeX options"
503
+ @parser.on("--use PACKAGE",
504
+ "Adds PACKAGE to the LaTeX preamble") do |w|
505
+ @preamble += "\\usepackage{#{w}}\n"
506
+ end
507
+
508
+ @parser.on("--preamble STRING",
509
+ "Adds STRING to the LaTeX preamble") do |w|
510
+ @preamble += "#{w}\n"
511
+ end
512
+
513
+ @parser.separator "\nGlobal look:"
514
+
515
+ @parser.on("--[no-]background [COLOR]",
516
+ "Sets the background color for plots") do |c|
517
+ # Keep it to false if only false
518
+ c = CTioga.get_tioga_color(c) if c
519
+ current_plot_style.background_color = c
520
+ end
521
+
522
+ @parser.on("--[no-]watermark [TEXT]",
523
+ "Writes a text as a watermark at the back ",
524
+ "of the plot") do |c|
525
+ current_plot_style.watermark_text = c
526
+ end
527
+
528
+ @parser.on("--watermark-color [COLOR]",
529
+ "Chooses the color of the watermark") do |c|
530
+ c = CTioga.get_tioga_color(c) if c
531
+ current_plot_style.watermark_color = c
532
+ end
533
+
534
+ @parser.on("--watermark-scale SCALE",
535
+ "Chooses the color of the watermark") do |c|
536
+ current_plot_style.watermark_scale = Float(c)
537
+ end
538
+
539
+ @parser.on("--aspect-ratio [RATIO]",
540
+ "Sets the aspect ratio (defaults to 1)") do |a|
541
+ a = 1.0 unless a
542
+ a = a.to_f
543
+ if a <= 0.0
544
+ warn "Aspect ratio #{a} not valid, ignored"
545
+ else
546
+ add_elem_funcall(:set_aspect_ratio, a)
547
+ end
548
+ end
549
+ @parser.on("--golden-ratio",
550
+ "Sets the aspect ratio to the golden number") do |a|
551
+ add_elem_funcall(:set_aspect_ratio, 1.61803398874989)
552
+ end
553
+ @parser.on("--xrange RANGE",
554
+ "X plotting range") do |a|
555
+ set_range('left','right', a)
556
+ end
557
+ @parser.on("--yrange RANGE",
558
+ "y plotting range") do |a|
559
+ set_range('bottom','top', a)
560
+ end
561
+ @parser.on("--margin MARGIN",
562
+ "Sets the margin around data", "(left,right,top,bottom",
563
+ "in fractions of the corresponding size)") do |v|
564
+ current_object.plot_margins = margins_from_text(v)
565
+ end
566
+
567
+ @parser.on("--rescale FACTOR",
568
+ "Scales everything by a given factor",
569
+ "(useful for subplots)") do |fact|
570
+ current_object.rescale = safe_float(fact)
571
+ end
572
+
573
+
574
+ # TODO: this should probably move to PlotStyle
575
+ @parser.on("--padding PADDING",
576
+ "Changes the padding for the current object",
577
+ "and subsequent ones") do |pad|
578
+ ary = expand_sides(pad)
579
+ @default_padding = ary.map {|dim| Dimension.new(dim)}
580
+ use_default_padding
581
+ end
582
+
583
+ axes_options(@parser)
584
+
585
+ @parser.separator "\nSubfigures, subplots, regions..."
586
+
587
+ @parser.on("--y2",
588
+ "Switch to an alternative Y axis") do
589
+ begin
590
+ current_object.disable_axis(:y)
591
+ rescue
592
+ # Purely ignore errors
593
+ warn "Something should have happened here "+
594
+ "that did not happen properly"
595
+ end
596
+ plot = SharedAxisPlot.new(:y, current_object)
597
+ # The label will be on the right
598
+ plot.plot_style.ylabel.side = RIGHT
599
+ plot.plot_style.edges.yaxis.loc = RIGHT
600
+ plot.plot_style.edges.set_edges(AXIS_HIDDEN, :left,:top,:bottom)
601
+ current_plot_style.edges.set_edges(AXIS_LINE_ONLY, :right)
602
+ # A simple layout will control the plot
603
+ layout = FixedLayout.new(plot)
604
+ # And this layout should report its info to
605
+ # the current one.
606
+ #
607
+ # Note that we add the child's *layout* to the
608
+ # objects layout, not the child itself.
609
+ #
610
+ # Won't fail if the current plot does not have a layout
611
+ if current_object.layout
612
+ current_object.layout.add_child(layout)
613
+ end
614
+ enter_child_object(plot)
615
+ end
616
+
617
+ @parser.on("--x2",
618
+ "Switch to an alternative X axis") do
619
+ begin
620
+ current_object.disable_axis(:y)
621
+ rescue
622
+ # Purely ignore errors
623
+ warn "Something should have happened here " +
624
+ "that did not happen properly"
625
+ end
626
+ plot = SharedAxisPlot.new(:x, current_object)
627
+ # The label will be on the right
628
+ plot.xlabel.side = TOP
629
+ plot.edges.xaxis.loc = TOP
630
+ plot.edges.set_edges(AXIS_HIDDEN, :left,:right,:bottom)
631
+ current_object.edges.set_edges(AXIS_LINE_ONLY, :top)
632
+ layout = FixedLayout.new(plot)
633
+ # And this layout should report its info to
634
+ # the current one.
635
+ #
636
+ # Note that we add the child's *layout* to the
637
+ # objects layout, not the child itself.
638
+ current_object.layout.add_child(layout)
639
+ enter_child_object(plot)
640
+ end
641
+
642
+ @parser.on("--inset SPEC",
643
+ "Creates an inset with the given",
644
+ "specifications (x,y:w[xh] or x1,y1;x2,y2)" ) do |a|
645
+ plot = SubPlot.new(:subplot, current_object)
646
+ plot.show_legend = true
647
+ enter_child_object(plot)
648
+ # TODO - high priority !
649
+ # implement a way to specify insets with *real* tex dimensions
650
+ # !!!! (such as 3cm,2cm:5cm) !
651
+ #
652
+ # This would *really* make way for real-size graphs
653
+ margins = Utils::inset_margins(a)
654
+ debug "inset margins: #{margins.inspect}"
655
+ current_object.convert_layout(FixedLayout)
656
+ current_object.layout.position = margins
657
+ # By default, we redirect all legends for the inset
658
+ # to the parent.
659
+ current_object.accept_legend = false
660
+ end
661
+
662
+ @parser.on("--zoom-inset SPEC",
663
+ "Creates an inset as with --inset " +
664
+ "and copies all the ",
665
+ "elements in the current plot there") do |a|
666
+ plot = SubPlot.new(:subplot, current_object)
667
+ # We redirect all legends for the inset to the parent.
668
+ plot.accept_legend = false
669
+ # Temporarily disable legends.
670
+ plot.disable_legend = true
671
+ for obj in @current_object.elements
672
+ plot.add_elem(obj.dup)
673
+ end
674
+ for obj in @current_object.funcalls
675
+ plot.add_funcall(obj.dup)
676
+ end
677
+ plot.disable_legend = false
678
+ # Better add it afterwards...
679
+ enter_child_object(plot)
680
+ margins = Utils::inset_margins(a)
681
+ debug "zoom margins: #{margins.inspect}"
682
+ current_object.convert_layout(FixedLayout)
683
+ current_object.layout.position = margins
684
+ end
685
+
686
+ @parser.on("--next-inset SPEC",
687
+ "Equivalent to --end --inset SPEC, except that",
688
+ "various style information are carried from",
689
+ "the previous inset") do |a|
690
+ plot = SubPlot.new(:subplot, current_object)
691
+ plot.show_legend = true
692
+ old = leave_child_object
693
+ enter_child_object(plot)
694
+
695
+ # Force copy of the style:
696
+ plot.plot_style = old.plot_style.deep_copy(plot)
697
+
698
+ margins = Utils::inset_margins(a)
699
+ debug "inset margins: #{margins.inspect}"
700
+ current_object.convert_layout(FixedLayout)
701
+ current_object.layout.position = margins
702
+ # We redirect all legends for the inset to the parent.
703
+ current_object.accept_legend = false
704
+ end
705
+
706
+ @parser.on("--subplot SPEC",
707
+ "Creates a subplot with the given specifications",
708
+ "(x,y:w[xh] or x1,y1;x2,y2)" ) do |a|
709
+ plot = SubPlot.new(:subplot, current_object)
710
+ enter_child_object(plot)
711
+ margins = Utils::inset_margins(a)
712
+
713
+ debug "subplot margins: #{margins.inspect}"
714
+ # In contrast with --inset, we use a layout:
715
+ current_object.layout = SimpleLayout.new(current_object)
716
+ current_object.layout.bounding_box = margins
717
+
718
+ # We do not redirect legends for a subplot
719
+ current_object.accept_legend = true
720
+
721
+ end
722
+
723
+ @parser.on("--next-subplot SPEC",
724
+ "Creates a subplot with the given specifications",
725
+ "(x,y:w[xh] or x1,y1;x2,y2)" ) do |a|
726
+ plot = SubPlot.new(:subplot, current_object)
727
+ old = leave_child_object
728
+ enter_child_object(plot)
729
+ margins = Utils::inset_margins(a)
730
+
731
+ # Force copy of the style:
732
+ plot.plot_style = old.plot_style.deep_copy(plot)
733
+
734
+ debug "subplot margins: #{margins.inspect}"
735
+ # In contrast with --inset, we use a layout:
736
+ current_object.layout = SimpleLayout.new(current_object)
737
+ current_object.layout.bounding_box = margins
738
+
739
+ # We do not redirect legends for a subplot
740
+ current_object.accept_legend = true
741
+
742
+ end
743
+
744
+
745
+
746
+ @parser.on("--disable-legends",
747
+ "Disable the display of legends for the current " +
748
+ "subplot/subfigure") do
749
+ current_object.disable_legend = true
750
+ end
751
+
752
+ @parser.on("--enable-legends",
753
+ "Reverts --disable-legends") do
754
+ current_object.disable_legend = false
755
+ end
756
+
757
+ @parser.on("--[no-]forward-legends",
758
+ "Forwards the legends to the parent object") do |v|
759
+ current_object.accept_legend = ! v
760
+ end
761
+
762
+ # Black magic is starting here !
763
+ @parser.on("--grid SPEC",
764
+ "Creates a grid. SPEC is column|row=nb") do |s|
765
+ # get the spec:
766
+ s =~ /(\w+)=(\d+)/
767
+ which = "#{$1}s=".to_sym
768
+ number = $2.to_i
769
+ if which == :columns= or which == :rows=
770
+ current_object.layout = current_object.layout.
771
+ convert_layout(GridLayout)
772
+
773
+ current_plot_style.hide_axis_and_edges(:x, :y)
774
+
775
+ # We rescale the padding by a factor of 1/number, so it looks
776
+ # reasonable in the end.
777
+ @default_padding.each do |dim|
778
+ dim.scale!(1.0/number)
779
+ end
780
+
781
+ # We use the default padding for the chidren
782
+ use_default_padding
783
+
784
+ current_plot_style.set_xy_labels(nil, nil)
785
+
786
+ current_object.layout.send(which, number)
787
+ plot = SubPlot.new(:subplot, current_object)
788
+ plot.accept_legend = false
789
+ @current_object.layout.add_child(GridItemLayout.new(plot))
790
+ @current_object.add_elem(plot)
791
+ self.current_object = plot
792
+ @next_spec = [SubPlot, :subplot]
793
+ @next_block = proc do |old, new|
794
+ new.accept_legend = false # By default, forward to the parent.
795
+ use_default_padding(new)
796
+ end
797
+ else
798
+ warn "Unrecognized spec #$1"
799
+ end
800
+ end
801
+
802
+ # Black magic is starting here !
803
+ @parser.on("--col",
804
+ "Starts a column of graphes with shared X axis") do
805
+ # We convert the current object to a GridLayout. If
806
+ # there are already plots, that really won't look good.
807
+ # You've been warned !!!
808
+ current_object.layout = current_object.layout.
809
+ convert_layout(GridLayout)
810
+ # current_object.edges.disable
811
+
812
+ current_plot_style.hide_axis_and_edges(:x, :y)
813
+
814
+ current_object.layout_preferences.padding[2] = Dimension.new(0.0)
815
+ current_object.layout_preferences.padding[3] = Dimension.new(0.0)
816
+
817
+ current_object.layout.columns = 1
818
+ plot = SharedAxisPlot.new(:y, current_object)
819
+ @next_spec = [SharedAxisPlot, :y]
820
+ @current_object.layout.add_child(GridItemLayout.new(plot))
821
+ @current_object.add_elem(plot)
822
+
823
+ # We copy the X label and the title
824
+ plot.plot_style.xlabel.label = current_plot_style.xlabel.label
825
+ current_plot_style.xlabel.label = nil
826
+ plot.plot_style.title.label = current_plot_style.title.label
827
+ current_plot_style.title.label = nil
828
+ self.current_object = plot
829
+
830
+ # We cancel vertical padding by default:
831
+ @default_padding[2] = Dimension.new(0.0)
832
+ @default_padding[3] = Dimension.new(0.0)
833
+
834
+ use_default_padding(plot)
835
+ # Forwards the x label to the child, and cancels it here.
836
+ # The code to be run on the old and new tings
837
+ @next_block = proc do |old, new|
838
+ old.plot_style.edges.
839
+ set_axis_and_edges_style(:x, AXIS_WITH_TICKS_ONLY)
840
+ old.plot_style.xlabel.label = nil
841
+
842
+ # Disable title by default
843
+ new.plot_style.title.label = nil
844
+ use_default_padding(new)
845
+ end
846
+ end
847
+
848
+ @parser.on("--next",
849
+ "Start the next object") do
850
+ next_object
851
+ end
852
+
853
+ @parser.on("--[no-]auto-next",
854
+ "Automatically start a --next graph",
855
+ "for every element on the command-line") do |v|
856
+ if v
857
+ @auto_next = :element
858
+ else
859
+ @auto_next = false
860
+ end
861
+ end
862
+
863
+ @parser.on("--auto-next-expanded",
864
+ "Automatically start a --next graph",
865
+ "for every element on the command-line",
866
+ "after data set expansion !") do
867
+ @auto_next = :dataset
868
+ end
869
+
870
+
871
+ # @parser.on("--subframes FRAMES",
872
+ # "Setup the frames relative to the parent",
873
+ # "for the current " +
874
+ # "subplot",
875
+ # "(distance from left,right,top,bottom)") do |v|
876
+ # current_object.frame_margins = margins_from_text(v)
877
+ # end
878
+
879
+ @parser.on("--region",
880
+ "Start a colored region") do
881
+ plot = Region.new(current_object)
882
+ @current_object.add_elem(plot)
883
+ self.current_object = plot
884
+ end
885
+
886
+ @parser.on("--region-color COLOR",
887
+ "The color of the region") do |v|
888
+ c = CTioga.get_tioga_color(v)
889
+ begin
890
+ current_object.region_color = c
891
+ rescue
892
+ error "The current container isn't a --region !"
893
+ end
894
+ end
895
+
896
+ @parser.on("--region-transparency TRANS",
897
+ "The transparency of the region") do |v|
898
+ c = v.to_f
899
+ begin
900
+ current_object.region_transparency = c
901
+ rescue
902
+ error "The current container isn't a --region !"
903
+ end
904
+ end
905
+
906
+ @parser.on("--region-dont-display",
907
+ "No curve until the next --end will actually be ",
908
+ "displayed: they are just used to delimit the ",
909
+ "region used for the fills") do
910
+ begin
911
+ current_object.dont_display = true
912
+ rescue
913
+ error "The current container isn't a --region !"
914
+ end
915
+ end
916
+
917
+ @parser.on("--region-debug",
918
+ "Setup the fills for the curves inside the",
919
+ "region to help understanding what happens") do
920
+ begin
921
+ current_object.region_debug = true
922
+ rescue
923
+ error "The current container isn't a --region !"
924
+ end
925
+ end
926
+
927
+ @parser.on("--region-invert-rule",
928
+ "Inverts the rule for choosing the filled region") do
929
+ begin
930
+ current_object.invert_rule = true
931
+ rescue
932
+ error "The current container isn't a --region !"
933
+ end
934
+ end
935
+
936
+ @parser.on("--region-fill-twice",
937
+ "Fills the region twice, choosing opposite",
938
+ "rules for the boundaries.") do
939
+ begin
940
+ current_object.fill_twice = true
941
+ rescue
942
+ error "The current container isn't a --region !"
943
+ end
944
+ end
945
+
946
+ @parser.on("--end",
947
+ "Ends the last subplot or region") do
948
+ leave_child_object
949
+ end
950
+
951
+
952
+ @parser.separator "\nOptions for real size output:"
953
+ # Real size
954
+ @parser.on("-r", "--[no-]real-size [SIZE]",
955
+ "Tries to produce a PDF file suitable",
956
+ "for inclusion " +
957
+ "with \\includegraphics") do |spec|
958
+ @real_size = spec
959
+ end
960
+ @parser.on("--frame-margins VAL",
961
+ "The proportion of space to leave on",
962
+ "each side for the text display" ) do |val|
963
+ set_frame_margins(margins_from_text(val))
964
+ end
965
+
966
+ Legends::fill_option_parser(@parser, self)
967
+
968
+ @parser.separator "\nTexts:"
969
+
970
+ ## Default TeX fontsize
971
+ @parser.on("--fontsize NB",
972
+ "Default TeX fontsize in points") do |size|
973
+ @tex_fontsize = size
974
+ end
975
+
976
+ @parser.on("--tick-label-scale SCALE",
977
+ "Sets the scale of the text for tick labels") do |l|
978
+ add_elem_funcall(:xaxis_numeric_label_scale=,Float(l))
979
+ add_elem_funcall(:yaxis_numeric_label_scale=,Float(l))
980
+ end
981
+
982
+
983
+ @parser.on("--[no-]separate-legends",
984
+ "If set, PDF files are produced that contain",
985
+ "what the legend pictogram would look like") do |l|
986
+ @separate_legends = l
987
+ if l
988
+ # Switches off autolegends by default
989
+ @autolegends = false
990
+ end
991
+ end
992
+
993
+ @parser.separator "\nGraphic primitives:"
994
+ @parser.on("-d","--draw SPEC",
995
+ "Draw a graphic primitive") do |spec|
996
+ add_elems_to_current(*TiogaPrimitiveMaker.parse_spec(spec, self))
997
+ end
998
+ @parser.on("--draw-help",
999
+ "Display what is known about graphic",
1000
+ "primitives and exit") do
1001
+ puts
1002
+ puts "Currently known graphics primitives are"
1003
+ puts
1004
+ puts TiogaPrimitiveMaker.introspect
1005
+ exit
1006
+ end
1007
+
1008
+ prepare_backend_options(@parser)
1009
+
1010
+ @parser.separator "\nHousekeeping and miscellaneous options:"
1011
+ @parser.on("--xpdf",
1012
+ "Runs xpdf to show the plot obtained") do
1013
+ @viewer = "xpdf -z page" # With the zoom set to full page
1014
+ end
1015
+
1016
+ @parser.on("--open",
1017
+ "Runs open to show the plot obtained") do
1018
+ @viewer = "open"
1019
+ end
1020
+
1021
+ @parser.on("--[no-]viewer [VIEWER]",
1022
+ "Runs VIEWER to show the plot obtained --",
1023
+ "or doesn't show up the viewer at all") do |x|
1024
+ @viewer = x
1025
+ end
1026
+
1027
+ @parser.on("--[no-]cleanup",
1028
+ "Removes all the accessory files produced") do |x|
1029
+ @cleanup = x
1030
+ end
1031
+
1032
+ @parser.on("--tex-cleanup",
1033
+ "Removes all files produced except those",
1034
+ "necessary " +
1035
+ "for inclusion in a TeX document") do
1036
+ @tex_cleanup = true
1037
+ end
1038
+
1039
+ @parser.on("--clean-all",
1040
+ "Removes all files produced -- better use",
1041
+ "along with " +
1042
+ "--xpdf or --open") do
1043
+ @clean_all = true
1044
+ end
1045
+
1046
+ @parser.on("--save-dir DIR",
1047
+ "Sets the directory for output") do |x|
1048
+ add_init_funcall(:save_dir=, x)
1049
+ end
1050
+
1051
+
1052
+ @parser.on("--include FILE",
1053
+ "Imports the file's functions into ctioga's",
1054
+ "namespace so that they can be used",
1055
+ "by backends") do |file|
1056
+ read_config_file(file)
1057
+ end
1058
+
1059
+ @parser.on("-n", "--name NAME",
1060
+ "Base name") do |name|
1061
+ @fig_name = name
1062
+ end
1063
+
1064
+ @parser.on("-o", "--output NAME",
1065
+ "Produces a graph named NAME with the current output") do |name|
1066
+ output_figure(name)
1067
+ end
1068
+
1069
+ @parser.on("-O", "--output-dir DIR",
1070
+ "Sets the output directory") do |dir|
1071
+ @output_directory = dir
1072
+ end
1073
+
1074
+ @parser.on("--echo", "Prints command-line to standard output") do
1075
+ puts "Command-line used:"
1076
+ puts @command_line
1077
+ end
1078
+
1079
+ @parser.on("--display-commandline",
1080
+ "Adds the command line used to make",
1081
+ "to graph on the bottom of the graph") do
1082
+ add_elem_funcall(:show_marker,
1083
+ {
1084
+ "x" => 0.5,
1085
+ "y" => -0.2,
1086
+ "text" => @command_line,
1087
+ "scale" => 0.6,
1088
+ "font" => Tioga::MarkerConstants::Courier,
1089
+ }
1090
+ )
1091
+ end
1092
+
1093
+ @parser.on("--[no-]mark",
1094
+ "Fills the 'creator' field of the " ,
1095
+ "produced PDF file with the command-line",
1096
+ "when on. ") do |v|
1097
+ @mark = v
1098
+ end
1099
+
1100
+ logger_options(@parser)
1101
+ @parser.on("--debug-patterns",
1102
+ "Produces an alignement-checking PDF file") do |v|
1103
+ @debug_patterns = v
1104
+ end
1105
+
1106
+ @parser.on("--eps",
1107
+ "Attempt to produce an EPS file,",
1108
+ "using pdftops, LaTeX and dvips.",
1109
+ "The PDF output is kept in any case") do |v|
1110
+ @eps = v
1111
+ end
1112
+
1113
+ @parser.on("--png SIZE",
1114
+ "Runs ctioga as usual, and runs convert afterwards",
1115
+ "to produce a nice PNG file") do |s|
1116
+ @png = true
1117
+ s =~ /(\d+)x(\d+)/
1118
+ @png_size = [$1,$2]
1119
+ # We setup the real size so that it matches the number of
1120
+ # postscript points:
1121
+ @real_size = "#{$1}bpx#{$2}bp"
1122
+ end
1123
+
1124
+ @parser.on("--png-oversampling NB",
1125
+ "How many more points to produce before downscaling") do |s|
1126
+ @png_oversampling = s.to_f
1127
+ end
1128
+
1129
+ @parser.on("--svg",
1130
+ "Runs ctioga as usual, and runs pdf2svg afterwards",
1131
+ "to produce a nice SVG file") do |s|
1132
+ @svg = true
1133
+ end
1134
+
1135
+
1136
+ @parser.on("--args FILE",
1137
+ "Reads argument from FILE, *one per",
1138
+ "line* and then resumes normal processing") do |v|
1139
+ File.open(v) do |f|
1140
+ unshift_cmdline_args(*f.readlines.collect {|s| s.chomp})
1141
+ end
1142
+ end
1143
+
1144
+ @parser.on_tail("-h", "--help", "Show this message") do
1145
+ puts banner
1146
+ puts
1147
+ puts @parser
1148
+ puts
1149
+ puts "Please note that all the options can be abbreviated"
1150
+ puts "as long as they are still unique"
1151
+ exit
1152
+ end
1153
+
1154
+ @parser.on_tail("--version", "Show version number") do
1155
+ puts "This is ctioga version " + Version::version
1156
+ exit
1157
+ end
1158
+ end
1159
+
1160
+
1161
+ # Start the next object in a complex plot (--grid, --col...)
1162
+ def next_object
1163
+ # We swicth back to the parent object
1164
+ old = leave_child_object
1165
+ a = @next_spec.dup # We need to dup, else shift would make the
1166
+ # next curve not that happy...
1167
+ cls = a.shift
1168
+ # We push the parent object as the last element for new.
1169
+ a << current_object
1170
+ plot = cls.new(*a)
1171
+ @current_object.layout.add_child(GridItemLayout.new(plot))
1172
+ enter_child_object(plot)
1173
+
1174
+ # We copy the style from the old plot to the new one:
1175
+ plot.plot_style = old.plot_style.deep_copy(plot)
1176
+
1177
+ @next_block.call(old, plot) if @next_block
1178
+ end
1179
+
1180
+
1181
+ def unshift_cmdline_args(*args)
1182
+ @args.unshift(*args)
1183
+ end
1184
+
1185
+ # the functions for plot structure...
1186
+ def subplot
1187
+ new_object = SubPlot.new(:subplot,@current_object)
1188
+ @current_object.add_elem(new_object)
1189
+ @current_object = new_object
1190
+ end
1191
+
1192
+ def subfigure
1193
+ new_object = SubPlot.new(:subfigure,@current_object)
1194
+ @current_object.add_elem(new_object)
1195
+ @current_object = new_object
1196
+ end
1197
+
1198
+ def end
1199
+ @current_object = @current_object.parent if @current_object.parent
1200
+ end
1201
+
1202
+ def add_elems_to_current(*elems)
1203
+ for el in elems
1204
+ @current_object.add_elem(el)
1205
+ end
1206
+ end
1207
+
1208
+ # This function is called whenever PlotMaker needs to process one
1209
+ # data set. It's name is given in parameter here.
1210
+ def process_data_set(set)
1211
+ # First, we get the dataset:
1212
+ begin
1213
+ data = xy_data_set(set)
1214
+ rescue Exception => ex
1215
+ error "Problem when processing set #{set} " +
1216
+ "(backend #{backend.class}): #{ex.message}"
1217
+ debug "Error backtrace: #{ex.backtrace.join "\n"}"
1218
+ warn "Simply ignoring set #{set}"
1219
+ return # Simply return.
1220
+ end
1221
+
1222
+ # Scale plots if necessary:
1223
+ data.mul!(:x,x_factor) if x_factor
1224
+ data.mul!(:y,y_factor) if y_factor
1225
+
1226
+ # Add an offset
1227
+ data.add!(:x,x_offset) if x_offset
1228
+ data.add!(:y,y_offset) if y_offset
1229
+
1230
+ # Implement a logarithmic scale
1231
+ data.safe_log10!(:x) if x_log
1232
+ data.safe_log10!(:y) if y_log
1233
+
1234
+ data.strip_nan
1235
+
1236
+ # Then we create a curve object and give it style
1237
+ if histogram
1238
+ curve = Histogram2D.new(get_current_style(set))
1239
+ else
1240
+ curve = Curve2D.new(get_current_style(set))
1241
+ end
1242
+ if @separate_legends
1243
+ @separate_legend_styles << curve.style
1244
+ end
1245
+ curve.set_data(data)
1246
+
1247
+ # Then we add ot to the current object:
1248
+ add_elems_to_current(curve)
1249
+ @last_curve = curve # And we update the last_curve pointer.
1250
+ end
1251
+
1252
+ # Provides the name of an output file, given its suffix and
1253
+ # optionnaly it base name (defaults to @figure_name).
1254
+ def output_filename(suffix, base = @fig_name)
1255
+ if @figmaker.save_dir
1256
+ File.join(@figmaker.save_dir,"#{base}#{suffix}")
1257
+ else
1258
+ "#{base}#{suffix}"
1259
+ end
1260
+ end
1261
+
1262
+ # Parses _args_ as command-line arguments.
1263
+ def parse_command_line_arguments(args)
1264
+
1265
+ # A little trick to allow in-place modification of the
1266
+ # command-line from an option.
1267
+ @args += args
1268
+
1269
+ # Runs the command to set command-line defaults. Ignored silently
1270
+ # in the files given on the command-line.
1271
+
1272
+ quoted_args = args.collect do |s|
1273
+ CTioga.shell_quote_string(s)
1274
+ end.join ' '
1275
+
1276
+ @command_line = "#{File.basename($0)} #{quoted_args}"
1277
+
1278
+ # Read the CTIOGA environment variable
1279
+ if ENV.key? "CTIOGA"
1280
+ ctioga_env = Shellwords.shellwords(ENV["CTIOGA"])
1281
+ @parser.parse(ctioga_env)
1282
+ end
1283
+
1284
+ if respond_to? :ctioga_defaults
1285
+ send :ctioga_defaults
1286
+ end
1287
+
1288
+ first = true
1289
+ done = false
1290
+ while ! done
1291
+ begin
1292
+ @parser.order!(@args) do |spec|
1293
+ # We use Backend#expand to leave room for the backend to
1294
+ # interpret the text as many different sets.
1295
+ sets = expand_spec(spec)
1296
+ info "Expanding #{spec} to #{sets.join(', ')}"
1297
+ # Auto --next:
1298
+ next_object if (@auto_next == :element and !first)
1299
+ for set in sets
1300
+ next_object if (@auto_next == :dataset and !first)
1301
+ process_data_set(set)
1302
+ first = false
1303
+ end
1304
+ end
1305
+ done = true
1306
+ rescue OptionParser::InvalidOption => o
1307
+ # Here, we should try to be clever about options:
1308
+ new_arg = nil
1309
+ option_name = o.args.first.gsub(/^--?/,'')
1310
+ # First, we check whether it corresponds to a shortcut:
1311
+ if Shortcut.has? option_name
1312
+ new_arg = ["--short", option_name]
1313
+ # Or an option of the current backend:
1314
+ elsif backend.description.param_hash.key? option_name
1315
+ new_arg = ["--#{backend.description.name}-#{option_name}"]
1316
+ end
1317
+
1318
+ if new_arg
1319
+ info "Option #{o.args.first} was reinterpreted " +
1320
+ "as #{new_arg.join ' '}"
1321
+ @args.unshift(*new_arg)
1322
+ else
1323
+ debug "Invalid option: #{o.inspect}"
1324
+ debug "Remaining args: #{@args.inspect}"
1325
+ error "Invalid option: #{o.args.first}. Please run " +
1326
+ "ctioga --help to have a list of valid ones"
1327
+ exit 1
1328
+ end
1329
+ rescue Exception => ex
1330
+ # Do not catch normal exit !
1331
+ if ex.is_a? SystemExit
1332
+ raise
1333
+ end
1334
+ fatal "ctioga encountered the following problem: #{ex.to_s}"
1335
+ debug "Error backtrace: #{ex.backtrace.join "\n"}"
1336
+ fatal "Aborting"
1337
+ exit 1
1338
+ end
1339
+ end
1340
+ end
1341
+
1342
+ # Setups up various figuremaker variables
1343
+ def setup_figure_maker(t)
1344
+ # Setting the TeX fontsize if default is not adequate
1345
+ t.tex_fontsize = @tex_fontsize if @tex_fontsize
1346
+
1347
+ # add hyperref preamble to have nice PDF comments:
1348
+ if @mark
1349
+ # We should use the \pdfinfo pdftex primitive, as
1350
+ # this will allow to have less surprises...
1351
+ t.tex_preview_preamble +=
1352
+ "\n\\pdfinfo {\n /Title (" +
1353
+ CTioga.pdftex_quote_string(@command_line) +
1354
+ ")\n" + "/Creator(" +
1355
+ CTioga.pdftex_quote_string("ctioga #{Version::version}") +
1356
+ ")\n}\n"
1357
+ end
1358
+
1359
+ if @debug_patterns
1360
+ debug_patterns(t)
1361
+ end
1362
+
1363
+ # We use Vincent's algorithm for major ticks when available ;-)...
1364
+ begin
1365
+ t.vincent_or_bill = true
1366
+ info "Using Vincent's algorithm for major ticks"
1367
+ rescue
1368
+ info "Using Bill's algorithm for major ticks"
1369
+ end
1370
+
1371
+
1372
+ return unless @root_object.number > 0
1373
+
1374
+ # Asking the theme to do something.
1375
+ theme_bod_hook(t)
1376
+
1377
+ # We put the command line used in the .tex file:
1378
+ t.tex_preview_preamble +=
1379
+ "% Produced by ctioga, with the command-line\n" +
1380
+ "% #{@command_line.gsub(/\n/,"\n% ")}\n"
1381
+
1382
+ if decimal_separator
1383
+ t.tex_preamble += <<'EOD'.gsub(',',decimal_separator) # Neat !
1384
+ \def\frenchsep#1{\frenchsepma#1\endoffrenchsep}
1385
+ \def\fseat#1{\frenchsepma}
1386
+ \def\frenchsepma{\futurelet\next\frenchsepmw}
1387
+ \def\frenchsepmw{\ifx\next\endoffrenchsep\let\endoffrenchsep=\relax%
1388
+ \else\if\next.\ifmmode\mathord,\else,\fi%
1389
+ \else\next\fi\expandafter
1390
+ \fseat\fi}
1391
+ EOD
1392
+ t.yaxis_numeric_label_tex = t.
1393
+ yaxis_numeric_label_tex.gsub('#1','\frenchsep{#1}')
1394
+ t.xaxis_numeric_label_tex = t.
1395
+ xaxis_numeric_label_tex.gsub('#1','\frenchsep{#1}')
1396
+ end
1397
+
1398
+ # Add custom preamble:
1399
+ t.tex_preview_preamble += @preamble
1400
+
1401
+ # If the initialization function was found, we call it
1402
+ if respond_to? :ctioga_init
1403
+ send :ctioga_init, t
1404
+ end
1405
+
1406
+ for funcall in @init_funcalls
1407
+ funcall.do(t)
1408
+ end
1409
+
1410
+ end
1411
+
1412
+ # Runs the code to make the actual figure.
1413
+ def do_figure(t)
1414
+ @root_object.do(t)
1415
+ end
1416
+
1417
+ # Ctioga's banner
1418
+ def banner
1419
+ <<"EOBANNER"
1420
+ This is ctioga version #{Version::version}
1421
+ ctioga is copyright (C) 2006, 2007, 2008 by Vincent Fourmond
1422
+
1423
+ ctioga comes with absolutely NO WARRANTY. This is
1424
+ free software, you are welcome to redistribute it under certain
1425
+ conditions. See the COPYING file in the original tarball for
1426
+ details. You are also welcome to contribute if you like it, see in
1427
+ the manual page where to ask.
1428
+ EOBANNER
1429
+ end
1430
+
1431
+ # Output a figure with the given name
1432
+ def output_figure(fig_name)
1433
+ # now, we plot the stuffs... if there is actually
1434
+ # something to be plotted ;-) !
1435
+
1436
+ @figmaker = t = FigureMaker.new
1437
+ # Cleanup code off by default, as this could cause problems for
1438
+ # legend pictures generation/EPS generation.
1439
+ # The files will get cleaned up anyway.
1440
+ t.autocleanup = false
1441
+
1442
+
1443
+ # Now, we must use real_size stuff !! (default 12cmx12cm)
1444
+ # This is not part of setup_figure_maker, as it can strongly interfere
1445
+ # when ctioga is used to produce a figure.
1446
+ info "Producing PDF file of real size #{@real_size}"
1447
+ setup_real_size(t)
1448
+
1449
+ # And, after that, the generic setup of the FigureMaker object.
1450
+ setup_figure_maker(t)
1451
+
1452
+ info "Generating file '#{fig_name}.pdf'"
1453
+
1454
+ # If fig_name is clearly a path, we split that bit out
1455
+ if File::basename(fig_name) != fig_name
1456
+ dir = File::dirname(fig_name)
1457
+ # If path is relative and output_directory is specified, we make
1458
+ # the path relative to output_dir
1459
+ if @output_directory && dir =~ /^[^\/~]/
1460
+ dir = File::join(@output_directory, dir)
1461
+ end
1462
+ t.save_dir = dir
1463
+ fig_name = File::basename(fig_name)
1464
+ elsif @output_directory
1465
+ t.save_dir = @output_directory
1466
+ end
1467
+
1468
+ t.def_figure(fig_name) {
1469
+ do_figure(t)
1470
+ }
1471
+
1472
+ debug "Contents of the main FigureMaker object: " +
1473
+ figmaker_options(t)
1474
+
1475
+
1476
+ if @fast
1477
+ # It isn't that fast, actually.
1478
+ info "Producing a fast version"
1479
+ # We output only the temporary PDF file
1480
+ class << t
1481
+ undef show_text
1482
+ # We replace show_text by show_marker, to at least get text
1483
+ # Doesn't work for axes and ticks... Pity !
1484
+ def show_text(dict)
1485
+ for key in %w{side shift position pos justification}
1486
+ dict.delete key
1487
+ end
1488
+ show_marker(dict)
1489
+ end
1490
+ end
1491
+ t.create_figure_temp_files(t.figure_index(fig_name))
1492
+ base_name = if t.save_dir
1493
+ File.join(t.save_dir,fig_name)
1494
+ else
1495
+ fig_name
1496
+ end
1497
+ # Remove the _figure.txt files
1498
+ File.rename(base_name + "_figure.pdf", base_name + ".pdf")
1499
+ else
1500
+ t.make_preview_pdf(t.figure_index(fig_name))
1501
+ end
1502
+
1503
+ if @eps
1504
+ info "Attempting to create an EPS file, watch out error messages"
1505
+ # First, we convert the _figure.pdf into eps with pdftops
1506
+ cmd = "pdftops -eps #{output_filename('_figure.pdf', fig_name)}"
1507
+ debug "Running #{cmd}"
1508
+ system cmd
1509
+ f = output_filename('.tex', fig_name)
1510
+ new = output_filename('.new.tex', fig_name)
1511
+ debug "Modifying #{f}"
1512
+ orig = open(f)
1513
+ out = open(new, "w")
1514
+ for l in orig
1515
+ l.gsub!('pdftex,','') # \usepackage[pdftex,...]...
1516
+ l.gsub!('_figure.pdf', '_figure.eps')
1517
+ out.print l
1518
+ end
1519
+ orig.close
1520
+ out.close
1521
+
1522
+ spawn "latex #{new}"
1523
+ spawn "dvips -o #{output_filename('.eps', fig_name)} -t unknown -T " +
1524
+ "#{@real_size.gsub(/x/,',')} #{output_filename('.new.dvi', fig_name)}"
1525
+ end
1526
+
1527
+ if @png
1528
+ # We create the PNG file:
1529
+ spawn "convert -density #{(@png_oversampling * 72).to_i} "+
1530
+ "#{output_filename('.pdf', fig_name)} -resize " +
1531
+ "#{@png_size.join('x')} #{output_filename('.png', fig_name)}"
1532
+ end
1533
+
1534
+ if @svg
1535
+ # We create the SVG file
1536
+ spawn "pdf2svg #{output_filename('.pdf', fig_name)} " +
1537
+ "#{output_filename('.svg', fig_name)}"
1538
+ end
1539
+
1540
+
1541
+ # Generating the separate legends if requested
1542
+ if not @separate_legend_styles.empty?
1543
+ index = 0
1544
+ info "Creating separate lengends"
1545
+ for style in @separate_legend_styles
1546
+ name = sprintf "%s_legend_%02d", fig_name, index
1547
+ index += 1
1548
+ t.def_figure(name) {
1549
+ t.set_device_pagesize(@separate_legend_width * 10,
1550
+ @separate_legend_height * 10)
1551
+ t.set_frame_sides(0,1,1,0)
1552
+ t.update_bbox(0,0)
1553
+ t.update_bbox(1,1)
1554
+ style.output_legend_pictogram(t)
1555
+ }
1556
+ # create_figure takes a number. It is part of Tioga's internals.
1557
+ t.create_figure_temp_files(t.figure_index(name))
1558
+
1559
+
1560
+ # Some post_processing to rename the files appropriately
1561
+ # and delete the unwanted _figure.txt file
1562
+ base_name = if t.save_dir
1563
+ File.join(t.save_dir,name)
1564
+ else
1565
+ name
1566
+ end
1567
+ # Remove the _figure.txt files
1568
+ begin
1569
+ File.rename(base_name + "_figure.pdf", base_name + ".pdf")
1570
+ if @eps # Create also EPS files:
1571
+ spawn "pdftops -eps #{base_name}.pdf"
1572
+ end
1573
+ File.delete(base_name + ".tex")
1574
+ File.delete(base_name + "_figure.txt")
1575
+ rescue
1576
+ warn "Files should have been deleted, but were not found"
1577
+ end
1578
+ end
1579
+ end
1580
+
1581
+ if @cleanup
1582
+ files_to_remove = %w(.tex .out .aux .log _figure.pdf _figure.txt) +
1583
+ %w(.new.tex .new.aux .new.dvi .new.log _figure.eps) # EPS-related files
1584
+ elsif @tex_cleanup
1585
+ files_to_remove = %w(.tex .out .aux .log .pdf)
1586
+ elsif @clean_all
1587
+ files_to_remove = %w(.tex .out .aux .log .pdf _figure.pdf _figure.txt) +
1588
+ %w(.new.tex .new.aux .new.dvi .new.log .eps _figure.eps) # EPS-related files
1589
+
1590
+ end
1591
+ if @viewer
1592
+ # we display the output file:
1593
+ info "Starting the viewer #{@viewer} as requested"
1594
+ pdf_file = if t.save_dir
1595
+ File.join(t.save_dir,fig_name+".pdf")
1596
+ else
1597
+ fig_name+".pdf"
1598
+ end
1599
+ system("#{@viewer} #{pdf_file}")
1600
+ end
1601
+
1602
+ if files_to_remove
1603
+ files_to_remove.collect! {|s|
1604
+ output_filename(s, fig_name)
1605
+ }
1606
+ files_to_remove << "pdflatex.log"
1607
+ info "Cleaning up temporary files"
1608
+ debug "Cleaning #{files_to_remove.join ' '}"
1609
+ files_to_remove.each do |f|
1610
+ begin
1611
+ File.delete(f)
1612
+ rescue
1613
+ debug "File #{f} absent, not deleted"
1614
+ end
1615
+ end
1616
+ end
1617
+
1618
+ end
1619
+
1620
+ # Runs the plots, using the arguments given in the command-line.
1621
+ # Now delegates to some other small functions.
1622
+ def run(args)
1623
+
1624
+ # First, we parse command-line arguments:
1625
+ parse_command_line_arguments(args)
1626
+
1627
+ # Then, we spit the output:
1628
+ output_figure(@fig_name)
1629
+
1630
+ end
1631
+
1632
+ end
1633
+
1634
+ # Takes a string a returns a quoted version that should be able
1635
+ # to go through shell expansion. For now, it won't work with
1636
+ # strings containing ', but that's already a good help.
1637
+ def CTioga.shell_quote_string(str)
1638
+ if str =~ /[\s"*$()\[\]{}';]/
1639
+ if str =~ /'/
1640
+ a = str.gsub(/(["$\\])/) { "\\#$1" }
1641
+ return "\"#{a}\""
1642
+ else
1643
+ return "'#{str}'"
1644
+ end
1645
+ else
1646
+ return str
1647
+ end
1648
+ end
1649
+
1650
+ # This function provides a 'command-line' interface from within Tioga !
1651
+ def CTioga.define_tioga_figure(t, name, *args)
1652
+ t.def_figure(name) {
1653
+ CTioga.show_tioga_plot(t, *args)
1654
+ }
1655
+ end
1656
+
1657
+ # This function provides a 'command-line' interface from within Tioga !
1658
+ def CTioga.show_tioga_plot(t, *args)
1659
+ plot = PlotMaker.new
1660
+ plot.parse_command_line_arguments(args)
1661
+ plot.setup_figure_maker(t)
1662
+
1663
+ width = 72 * t.
1664
+ convert_output_to_inches(t.
1665
+ convert_figure_to_output_dx(t.convert_frame_to_figure_dx(1)))
1666
+ height = 72 * t.
1667
+ convert_output_to_inches(t.
1668
+ convert_figure_to_output_dy(t.convert_frame_to_figure_dy(1)))
1669
+
1670
+ plot.set_root_frame(0, width, height, 0)
1671
+
1672
+ plot.do_figure(t)
1673
+ end
1674
+
1675
+
1676
+ end
1677
+