ctioga 1.11.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|