ctioga 1.11.1

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