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.
- data/COPYING +340 -0
- data/ctioga/bin/ctable +28 -0
- data/ctioga/bin/ctioga +37 -0
- data/ctioga/doc/ctable.1 +156 -0
- data/ctioga/doc/ctioga.1 +2363 -0
- data/ctioga/examples/README +46 -0
- data/ctioga/examples/ctioga.gnuplot +4 -0
- data/ctioga/examples/ctioga_within_tioga.rb +53 -0
- data/ctioga/examples/ctiogarc.rb +24 -0
- data/ctioga/examples/include_1.rb +15 -0
- data/ctioga/examples/noise.dat +100 -0
- data/ctioga/examples/noise.rb +13 -0
- data/ctioga/examples/trig.csv +100 -0
- data/ctioga/examples/trig.dat +100 -0
- data/ctioga/examples/trig.rb +14 -0
- data/ctioga/examples/trigh.dat +100 -0
- data/ctioga/examples/trigh.rb +10 -0
- data/ctioga/examples/tutorial +763 -0
- data/ctioga/examples/tutorial.sh +269 -0
- data/ctioga/tests/README +14 -0
- data/ctioga/tests/axes.sh +40 -0
- data/ctioga/tests/basic.sh +11 -0
- data/ctioga/tests/draw.sh +24 -0
- data/ctioga/tests/histograms.sh +14 -0
- data/ctioga/tests/insets.sh +41 -0
- data/ctioga/tests/layouts.sh +29 -0
- data/ctioga/tests/legends.sh +113 -0
- data/ctioga/tests/styles.sh +43 -0
- data/ctioga/tests/test_style.sh +8 -0
- data/ctioga/tests/tests.sh +24 -0
- data/ctioga/tests/text_backend.sh +83 -0
- data/ctioga/tests/tioga_defaults.rb +18 -0
- data/lib/CTioga/axes.rb +904 -0
- data/lib/CTioga/backends.rb +88 -0
- data/lib/CTioga/boundaries.rb +224 -0
- data/lib/CTioga/ctable.rb +134 -0
- data/lib/CTioga/curve_style.rb +246 -0
- data/lib/CTioga/debug.rb +199 -0
- data/lib/CTioga/dimension.rb +133 -0
- data/lib/CTioga/elements.rb +17 -0
- data/lib/CTioga/elements/base.rb +84 -0
- data/lib/CTioga/elements/containers.rb +578 -0
- data/lib/CTioga/elements/curves.rb +368 -0
- data/lib/CTioga/elements/tioga_primitives.rb +440 -0
- data/lib/CTioga/layout.rb +595 -0
- data/lib/CTioga/legends.rb +29 -0
- data/lib/CTioga/legends/cmdline.rb +187 -0
- data/lib/CTioga/legends/item.rb +164 -0
- data/lib/CTioga/legends/style.rb +257 -0
- data/lib/CTioga/log.rb +73 -0
- data/lib/CTioga/movingarrays.rb +131 -0
- data/lib/CTioga/partition.rb +271 -0
- data/lib/CTioga/plot_style.rb +230 -0
- data/lib/CTioga/plotmaker.rb +1677 -0
- data/lib/CTioga/shortcuts.rb +69 -0
- data/lib/CTioga/structures.rb +82 -0
- data/lib/CTioga/styles.rb +140 -0
- data/lib/CTioga/themes.rb +581 -0
- data/lib/CTioga/themes/classical.rb +82 -0
- data/lib/CTioga/themes/demo.rb +63 -0
- data/lib/CTioga/themes/fits.rb +91 -0
- data/lib/CTioga/themes/mono.rb +33 -0
- data/lib/CTioga/tioga.rb +32 -0
- data/lib/CTioga/utils.rb +173 -0
- data/lib/MetaBuilder/Parameters/dates.rb +38 -0
- data/lib/MetaBuilder/Parameters/lists.rb +132 -0
- data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
- data/lib/MetaBuilder/Parameters/strings.rb +86 -0
- data/lib/MetaBuilder/Parameters/styles.rb +75 -0
- data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
- data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
- data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
- data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
- data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
- data/lib/MetaBuilder/descriptions.rb +603 -0
- data/lib/MetaBuilder/factory.rb +101 -0
- data/lib/MetaBuilder/group.rb +57 -0
- data/lib/MetaBuilder/metabuilder.rb +10 -0
- data/lib/MetaBuilder/parameter.rb +374 -0
- data/lib/MetaBuilder/parameters.rb +11 -0
- data/lib/MetaBuilder/qt4.rb +8 -0
- data/lib/SciYAG/Backends/backend.rb +379 -0
- data/lib/SciYAG/Backends/binner.rb +168 -0
- data/lib/SciYAG/Backends/cache.rb +102 -0
- data/lib/SciYAG/Backends/dataset.rb +158 -0
- data/lib/SciYAG/Backends/descriptions.rb +469 -0
- data/lib/SciYAG/Backends/filters.rb +25 -0
- data/lib/SciYAG/Backends/filters/average.rb +134 -0
- data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
- data/lib/SciYAG/Backends/filters/filter.rb +70 -0
- data/lib/SciYAG/Backends/filters/norm.rb +39 -0
- data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
- data/lib/SciYAG/Backends/filters/sort.rb +43 -0
- data/lib/SciYAG/Backends/filters/strip.rb +34 -0
- data/lib/SciYAG/Backends/filters/trim.rb +64 -0
- data/lib/SciYAG/Backends/gnuplot.rb +131 -0
- data/lib/SciYAG/Backends/math.rb +108 -0
- data/lib/SciYAG/Backends/mdb.rb +462 -0
- data/lib/SciYAG/Backends/multitext.rb +96 -0
- data/lib/SciYAG/Backends/source.rb +64 -0
- data/lib/SciYAG/Backends/text.rb +339 -0
- data/lib/SciYAG/backends.rb +16 -0
- 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
|
+
|