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