ctioga 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. data/COPYING +340 -0
  2. data/ctioga/bin/ctable +28 -0
  3. data/ctioga/bin/ctioga +37 -0
  4. data/ctioga/doc/ctable.1 +156 -0
  5. data/ctioga/doc/ctioga.1 +2363 -0
  6. data/ctioga/examples/README +46 -0
  7. data/ctioga/examples/ctioga.gnuplot +4 -0
  8. data/ctioga/examples/ctioga_within_tioga.rb +53 -0
  9. data/ctioga/examples/ctiogarc.rb +24 -0
  10. data/ctioga/examples/include_1.rb +15 -0
  11. data/ctioga/examples/noise.dat +100 -0
  12. data/ctioga/examples/noise.rb +13 -0
  13. data/ctioga/examples/trig.csv +100 -0
  14. data/ctioga/examples/trig.dat +100 -0
  15. data/ctioga/examples/trig.rb +14 -0
  16. data/ctioga/examples/trigh.dat +100 -0
  17. data/ctioga/examples/trigh.rb +10 -0
  18. data/ctioga/examples/tutorial +763 -0
  19. data/ctioga/examples/tutorial.sh +269 -0
  20. data/ctioga/tests/README +14 -0
  21. data/ctioga/tests/axes.sh +40 -0
  22. data/ctioga/tests/basic.sh +11 -0
  23. data/ctioga/tests/draw.sh +24 -0
  24. data/ctioga/tests/histograms.sh +14 -0
  25. data/ctioga/tests/insets.sh +41 -0
  26. data/ctioga/tests/layouts.sh +29 -0
  27. data/ctioga/tests/legends.sh +113 -0
  28. data/ctioga/tests/styles.sh +43 -0
  29. data/ctioga/tests/test_style.sh +8 -0
  30. data/ctioga/tests/tests.sh +24 -0
  31. data/ctioga/tests/text_backend.sh +83 -0
  32. data/ctioga/tests/tioga_defaults.rb +18 -0
  33. data/lib/CTioga/axes.rb +904 -0
  34. data/lib/CTioga/backends.rb +88 -0
  35. data/lib/CTioga/boundaries.rb +224 -0
  36. data/lib/CTioga/ctable.rb +134 -0
  37. data/lib/CTioga/curve_style.rb +246 -0
  38. data/lib/CTioga/debug.rb +199 -0
  39. data/lib/CTioga/dimension.rb +133 -0
  40. data/lib/CTioga/elements.rb +17 -0
  41. data/lib/CTioga/elements/base.rb +84 -0
  42. data/lib/CTioga/elements/containers.rb +578 -0
  43. data/lib/CTioga/elements/curves.rb +368 -0
  44. data/lib/CTioga/elements/tioga_primitives.rb +440 -0
  45. data/lib/CTioga/layout.rb +595 -0
  46. data/lib/CTioga/legends.rb +29 -0
  47. data/lib/CTioga/legends/cmdline.rb +187 -0
  48. data/lib/CTioga/legends/item.rb +164 -0
  49. data/lib/CTioga/legends/style.rb +257 -0
  50. data/lib/CTioga/log.rb +73 -0
  51. data/lib/CTioga/movingarrays.rb +131 -0
  52. data/lib/CTioga/partition.rb +271 -0
  53. data/lib/CTioga/plot_style.rb +230 -0
  54. data/lib/CTioga/plotmaker.rb +1677 -0
  55. data/lib/CTioga/shortcuts.rb +69 -0
  56. data/lib/CTioga/structures.rb +82 -0
  57. data/lib/CTioga/styles.rb +140 -0
  58. data/lib/CTioga/themes.rb +581 -0
  59. data/lib/CTioga/themes/classical.rb +82 -0
  60. data/lib/CTioga/themes/demo.rb +63 -0
  61. data/lib/CTioga/themes/fits.rb +91 -0
  62. data/lib/CTioga/themes/mono.rb +33 -0
  63. data/lib/CTioga/tioga.rb +32 -0
  64. data/lib/CTioga/utils.rb +173 -0
  65. data/lib/MetaBuilder/Parameters/dates.rb +38 -0
  66. data/lib/MetaBuilder/Parameters/lists.rb +132 -0
  67. data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
  68. data/lib/MetaBuilder/Parameters/strings.rb +86 -0
  69. data/lib/MetaBuilder/Parameters/styles.rb +75 -0
  70. data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
  71. data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
  72. data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
  73. data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
  74. data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
  75. data/lib/MetaBuilder/descriptions.rb +603 -0
  76. data/lib/MetaBuilder/factory.rb +101 -0
  77. data/lib/MetaBuilder/group.rb +57 -0
  78. data/lib/MetaBuilder/metabuilder.rb +10 -0
  79. data/lib/MetaBuilder/parameter.rb +374 -0
  80. data/lib/MetaBuilder/parameters.rb +11 -0
  81. data/lib/MetaBuilder/qt4.rb +8 -0
  82. data/lib/SciYAG/Backends/backend.rb +379 -0
  83. data/lib/SciYAG/Backends/binner.rb +168 -0
  84. data/lib/SciYAG/Backends/cache.rb +102 -0
  85. data/lib/SciYAG/Backends/dataset.rb +158 -0
  86. data/lib/SciYAG/Backends/descriptions.rb +469 -0
  87. data/lib/SciYAG/Backends/filters.rb +25 -0
  88. data/lib/SciYAG/Backends/filters/average.rb +134 -0
  89. data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
  90. data/lib/SciYAG/Backends/filters/filter.rb +70 -0
  91. data/lib/SciYAG/Backends/filters/norm.rb +39 -0
  92. data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
  93. data/lib/SciYAG/Backends/filters/sort.rb +43 -0
  94. data/lib/SciYAG/Backends/filters/strip.rb +34 -0
  95. data/lib/SciYAG/Backends/filters/trim.rb +64 -0
  96. data/lib/SciYAG/Backends/gnuplot.rb +131 -0
  97. data/lib/SciYAG/Backends/math.rb +108 -0
  98. data/lib/SciYAG/Backends/mdb.rb +462 -0
  99. data/lib/SciYAG/Backends/multitext.rb +96 -0
  100. data/lib/SciYAG/Backends/source.rb +64 -0
  101. data/lib/SciYAG/Backends/text.rb +339 -0
  102. data/lib/SciYAG/backends.rb +16 -0
  103. metadata +191 -0
@@ -0,0 +1,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
+