tioga 1.4

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 (116) hide show
  1. data/Tioga_README +372 -0
  2. data/lgpl.txt +504 -0
  3. data/split/Dtable/defs.h +33 -0
  4. data/split/Dtable/dtable.c +1928 -0
  5. data/split/Dtable/dtable_intern.h +144 -0
  6. data/split/Dtable/dvector.h +61 -0
  7. data/split/Dtable/extconf.rb +4 -0
  8. data/split/Dtable/include/dtable.h +35 -0
  9. data/split/Dtable/lib/Dtable_extras.rb +90 -0
  10. data/split/Dtable/namespace.h +47 -0
  11. data/split/Dtable/safe_double.h +104 -0
  12. data/split/Dtable/symbols.c +92 -0
  13. data/split/Dtable/symbols.h +52 -0
  14. data/split/Dvector/defs.h +33 -0
  15. data/split/Dvector/dvector.c +5486 -0
  16. data/split/Dvector/dvector_intern.h +142 -0
  17. data/split/Dvector/extconf.rb +4 -0
  18. data/split/Dvector/include/dvector.h +61 -0
  19. data/split/Dvector/lib/Dvector_extras.rb +328 -0
  20. data/split/Dvector/lib/Numeric_extras.rb +134 -0
  21. data/split/Dvector/namespace.h +47 -0
  22. data/split/Dvector/safe_double.h +104 -0
  23. data/split/Dvector/symbols.c +92 -0
  24. data/split/Dvector/symbols.h +52 -0
  25. data/split/Flate/defs.h +33 -0
  26. data/split/Flate/extconf.rb +19 -0
  27. data/split/Flate/flate.c +156 -0
  28. data/split/Flate/flate_intern.h +97 -0
  29. data/split/Flate/include/flate.h +98 -0
  30. data/split/Flate/namespace.h +47 -0
  31. data/split/Flate/safe_double.h +104 -0
  32. data/split/Flate/symbols.c +92 -0
  33. data/split/Flate/symbols.h +52 -0
  34. data/split/Function/defs.h +33 -0
  35. data/split/Function/dvector.h +61 -0
  36. data/split/Function/extconf.rb +4 -0
  37. data/split/Function/function.c +988 -0
  38. data/split/Function/joint_qsort.c +258 -0
  39. data/split/Function/lib/Function_extras.rb +44 -0
  40. data/split/Function/namespace.h +47 -0
  41. data/split/Function/safe_double.h +104 -0
  42. data/split/Function/symbols.c +92 -0
  43. data/split/Function/symbols.h +52 -0
  44. data/split/Tioga/axes.c +774 -0
  45. data/split/Tioga/defs.h +33 -0
  46. data/split/Tioga/dtable.h +35 -0
  47. data/split/Tioga/dvector.h +61 -0
  48. data/split/Tioga/extconf.rb +4 -0
  49. data/split/Tioga/figures.c +672 -0
  50. data/split/Tioga/figures.h +855 -0
  51. data/split/Tioga/flate.h +98 -0
  52. data/split/Tioga/init.c +524 -0
  53. data/split/Tioga/lib/Arcs_and_Circles.rb +64 -0
  54. data/split/Tioga/lib/ColorConstants.rb +274 -0
  55. data/split/Tioga/lib/Colorbars.rb +10 -0
  56. data/split/Tioga/lib/Colormaps.rb +105 -0
  57. data/split/Tioga/lib/Coordinate_Conversions.rb +194 -0
  58. data/split/Tioga/lib/Creating_Paths.rb +94 -0
  59. data/split/Tioga/lib/Doc.rb +91 -0
  60. data/split/Tioga/lib/Executive.rb +515 -0
  61. data/split/Tioga/lib/FigMkr.rb +2224 -0
  62. data/split/Tioga/lib/FigureConstants.rb +125 -0
  63. data/split/Tioga/lib/Figures_and_Plots.rb +268 -0
  64. data/split/Tioga/lib/Images.rb +278 -0
  65. data/split/Tioga/lib/Legends.rb +190 -0
  66. data/split/Tioga/lib/MarkerConstants.rb +122 -0
  67. data/split/Tioga/lib/Markers.rb +129 -0
  68. data/split/Tioga/lib/Page_Frame_Bounds.rb +567 -0
  69. data/split/Tioga/lib/Rectangles.rb +94 -0
  70. data/split/Tioga/lib/Shading.rb +100 -0
  71. data/split/Tioga/lib/Special_Paths.rb +307 -0
  72. data/split/Tioga/lib/Strokes.rb +129 -0
  73. data/split/Tioga/lib/TeX_Text.rb +454 -0
  74. data/split/Tioga/lib/TexPreamble.rb +358 -0
  75. data/split/Tioga/lib/Titles_and_Labels.rb +306 -0
  76. data/split/Tioga/lib/Transparency.rb +89 -0
  77. data/split/Tioga/lib/Using_Paths.rb +164 -0
  78. data/split/Tioga/lib/Utils.rb +74 -0
  79. data/split/Tioga/lib/X_and_Y_Axes.rb +749 -0
  80. data/split/Tioga/lib/irb_tioga.rb +122 -0
  81. data/split/Tioga/lib/tioga.rb +1 -0
  82. data/split/Tioga/lib/tioga_ui.rb +5 -0
  83. data/split/Tioga/lib/tioga_ui_cmds.rb +793 -0
  84. data/split/Tioga/makers.c +989 -0
  85. data/split/Tioga/mk_tioga_sty.rb +53 -0
  86. data/split/Tioga/namespace.h +47 -0
  87. data/split/Tioga/pdf_font_dicts.c +18253 -0
  88. data/split/Tioga/pdfcolor.c +486 -0
  89. data/split/Tioga/pdfcoords.c +505 -0
  90. data/split/Tioga/pdffile.c +342 -0
  91. data/split/Tioga/pdfimage.c +536 -0
  92. data/split/Tioga/pdfpath.c +914 -0
  93. data/split/Tioga/pdfs.h +229 -0
  94. data/split/Tioga/pdftext.c +443 -0
  95. data/split/Tioga/safe_double.h +104 -0
  96. data/split/Tioga/symbols.c +92 -0
  97. data/split/Tioga/symbols.h +52 -0
  98. data/split/Tioga/texout.c +380 -0
  99. data/split/defs.h +33 -0
  100. data/split/extconf.rb +107 -0
  101. data/split/mkmf2.rb +1612 -0
  102. data/split/namespace.h +47 -0
  103. data/split/safe_double.h +104 -0
  104. data/split/scripts/tioga +4 -0
  105. data/split/symbols.c +92 -0
  106. data/split/symbols.h +52 -0
  107. data/tests/dtable_test.data +6 -0
  108. data/tests/dvector_read_test.data +1 -0
  109. data/tests/dvector_test.data +101 -0
  110. data/tests/tc_Dtable.rb +221 -0
  111. data/tests/tc_Dvector.rb +791 -0
  112. data/tests/tc_FMkr.rb +162 -0
  113. data/tests/tc_Flate.rb +45 -0
  114. data/tests/tc_Function.rb +111 -0
  115. data/tests/ts_Tioga.rb +38 -0
  116. metadata +163 -0
@@ -0,0 +1,2224 @@
1
+ # FigMkr.rb
2
+
3
+ =begin
4
+ Copyright (C) 2005, 2006 Bill Paxton
5
+
6
+ This file is part of Tioga.
7
+
8
+ Tioga is free software; you can redistribute it and/or modify
9
+ it under the terms of the GNU General Library Public License as published
10
+ by the Free Software Foundation; either version 2 of the License, or
11
+ (at your option) any later version.
12
+
13
+ Tioga is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU Library General Public License for more details.
17
+
18
+ You should have received a copy of the GNU Library General Public License
19
+ along with Tioga; if not, write to the Free Software
20
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
+ =end
22
+
23
+ require 'Tioga/FigureConstants.rb'
24
+
25
+ module Tioga
26
+ class FigureMaker
27
+
28
+ include FigureConstants
29
+
30
+ @@default_figure_maker = nil
31
+ @@which_pdflatex = nil
32
+ @@initialized = false # set true by the C code when first make a figure
33
+
34
+
35
+ # The tag used for cvs export
36
+ CVS_TAG = "rel_1_4" # now manually cheating...
37
+
38
+ # Version now uses the CVS_TAG to create the version number. CVS_TAG should
39
+ # look like 'rel_1_1_0' for the 1.1.0 release.
40
+ def FigureMaker.version
41
+ CVS_TAG =~ /\D+(.*?)\s*\$?$/
42
+ version = $1.tr("-_", "..")
43
+ if version.length > 0
44
+ return version
45
+ else
46
+ return "SNV $Revision: 313 $" # Can't do better than that.
47
+ end
48
+ end
49
+
50
+ def FigureMaker.default
51
+ @@default_figure_maker = FigureMaker.new if @@default_figure_maker == nil
52
+ @@default_figure_maker
53
+ end
54
+
55
+ def FigureMaker.default=(fm)
56
+ @@default_figure_maker = fm
57
+ end
58
+
59
+ def FigureMaker.pdflatex
60
+ @@which_pdflatex = 'pdflatex' if @@which_pdflatex == nil
61
+ @@which_pdflatex
62
+ end
63
+
64
+ def FigureMaker.pdflatex=(s)
65
+ @@which_pdflatex = s
66
+ end
67
+
68
+
69
+ def FigureMaker.make_name_lookup_hash(ary)
70
+ dict = Hash.new
71
+ ary.each { |name| dict[name] = true }
72
+ return dict
73
+ end
74
+
75
+ attr_accessor :style_filename
76
+
77
+ attr_accessor :legend_info
78
+
79
+ attr_reader :num_figures
80
+
81
+ attr_reader :figure_names
82
+
83
+ attr_reader :figure_pdfs
84
+
85
+ attr_reader :run_dir
86
+
87
+ attr_accessor :save_dir
88
+
89
+ attr_accessor :quiet_mode
90
+
91
+ attr_accessor :legend_defaults
92
+
93
+ attr_accessor :marker_defaults
94
+
95
+ attr_accessor :tex_preamble
96
+
97
+ attr_accessor :xaxis_numeric_label_tex
98
+ attr_accessor :yaxis_numeric_label_tex
99
+
100
+ attr_accessor :tex_fontsize
101
+ attr_accessor :tex_fontfamily
102
+ attr_accessor :tex_fontseries
103
+ attr_accessor :tex_fontshape
104
+
105
+ attr_accessor :default_page_width
106
+ attr_accessor :default_page_height
107
+ attr_accessor :default_frame_left
108
+ attr_accessor :default_frame_right
109
+ attr_accessor :default_frame_top
110
+ attr_accessor :default_frame_bottom
111
+
112
+ attr_accessor :num_error_lines
113
+
114
+ # Whether or not to create +save_dir+ if it doesn't exist
115
+ attr_accessor :create_save_dir
116
+
117
+ # Whether or not do do automatic cleanup of the files
118
+ attr_accessor :autocleanup
119
+
120
+ # Whether or not do do multithreading for parallel pdflatex calls
121
+ attr_accessor :multithreads_okay_for_tioga
122
+
123
+
124
+
125
+ # old preview attributes -- to be removed later
126
+
127
+ attr_accessor :model_number
128
+ attr_accessor :add_model_number
129
+ attr_accessor :need_to_reload_data
130
+ attr_accessor :auto_refresh_filename
131
+
132
+ #attr_accessor :tex_preview_documentclass
133
+ #attr_accessor :tex_preview_pagestyle
134
+ #attr_accessor :tex_preview_tiogafigure_command
135
+ attr_accessor :tex_xoffset
136
+ attr_accessor :tex_yoffset
137
+
138
+ #attr_accessor :tex_preview_paper_width
139
+ #attr_accessor :tex_preview_paper_height
140
+ #attr_accessor :tex_preview_hoffset
141
+ #attr_accessor :tex_preview_voffset
142
+ #attr_accessor :tex_preview_figure_width
143
+ #attr_accessor :tex_preview_figure_height
144
+ #attr_accessor :tex_preview_minwhitespace
145
+ #attr_accessor :tex_preview_fullpage
146
+
147
+
148
+ def reset_figures # set the state to default values
149
+ @figure_commands = []
150
+ @num_figures = 0
151
+ @create_save_dir = true # creates +save_dir+ by default
152
+
153
+ @name = nil
154
+ @auto_refresh_filename = nil
155
+ @figure_names = [ ]
156
+ @figure_pdfs = [ ]
157
+ @legend_info = [ ]
158
+ @run_dir = Dir.getwd
159
+ @save_dir = nil
160
+ @quiet_mode = false
161
+ @model_number = -1
162
+ @need_to_reload_data = true
163
+ @add_model_number = false
164
+ @tex_preview_documentclass = 'article'
165
+ @tex_preamble = '% start of preamble.
166
+ \usepackage[dvipsnames,usenames]{color} % need this for text colors
167
+ '
168
+ # \usepackage[pdftex]{geometry} % need this for setting page size for preview
169
+ # This has been commented out as it's place lie in the texout.c, for
170
+ # it's parameters to be set properly...
171
+ @tex_preview_pagestyle = 'empty'
172
+ @default_page_width = 72*5 # in big-points (1/72 inch)
173
+ @default_page_height = 72*5 # in big-points (1/72 inch)
174
+
175
+ @default_frame_left = 0.15 # as fraction of width from left edge
176
+ @default_frame_right = 0.85 # as fraction of width from left edge
177
+ @default_frame_top = 0.85 # as fraction of width from bottom edge
178
+ @default_frame_bottom = 0.15 # as fraction of width from bottom edge
179
+
180
+ @xaxis_numeric_label_tex = '$#1$'
181
+ @yaxis_numeric_label_tex = '$#1$'
182
+
183
+ @tex_preview_fullpage = true
184
+ @tex_preview_minwhitespace = nil # use default
185
+
186
+ @tex_preview_tiogafigure_command = 'tiogafigurescaledtofit'
187
+ @tex_preview_figure_width = '\paperwidth - 2in'
188
+ @tex_preview_figure_height = '\paperheight - 2in'
189
+
190
+ @num_error_lines = 10
191
+
192
+ @tex_xoffset = 0
193
+ @tex_yoffset = 0
194
+
195
+ @tex_preview_hoffset = '1in'
196
+ @tex_preview_voffset = '1in'
197
+
198
+ @tex_fontsize = '10.0'
199
+ @tex_fontfamily = 'rmdefault'
200
+ @tex_fontseries = 'mddefault'
201
+ @tex_fontshape = 'updefault'
202
+
203
+ @legend_defaults = {
204
+ 'legend_top_margin' => 0.03,
205
+ 'legend_bottom_margin' => 0.03,
206
+ 'legend_left_margin' => 0.83,
207
+ 'legend_right_margin' => 0.0,
208
+ 'plot_top_margin' => 0.0,
209
+ 'plot_bottom_margin' => 0.0,
210
+ 'plot_left_margin' => 0.0,
211
+ 'plot_right_margin' => 0.18,
212
+ 'plot_scale' => 1,
213
+ 'legend_scale' => 1 }
214
+
215
+ @marker_defaults = {
216
+ 'fill_color' => Black,
217
+ 'stroke_color' => Black,
218
+ 'scale' => 1,
219
+ 'angle' => 0,
220
+ 'justification' => CENTERED,
221
+ 'alignment' => ALIGNED_AT_MIDHEIGHT,
222
+ 'horizontal_scale' => 1.0,
223
+ 'vertical_scale' => 1.0,
224
+ 'italic_angle' => 0.0,
225
+ 'ascent_angle' => 0.0 }
226
+
227
+ @eval_command = nil
228
+
229
+ @enter_show_plot_function = nil
230
+ @exit_show_plot_function = nil
231
+
232
+ @enter_subfigure_function = nil
233
+ @exit_subfigure_function = nil
234
+
235
+ @enter_subplot_function = nil
236
+ @exit_subplot_function = nil
237
+
238
+ @enter_context_function = nil
239
+ @exit_context_function = nil
240
+
241
+ @enter_page_function = lambda { default_enter_page_function }
242
+ @exit_page_function = nil
243
+
244
+ @tex_preview_paper_width = '297mm'
245
+ @tex_preview_paper_height = '210mm'
246
+
247
+ @plot_box_command = lambda { show_plot_box }
248
+
249
+ # Automatic cleanup of by default
250
+ @autocleanup = true
251
+
252
+ # multithreads by default
253
+ @multithreads_okay_for_tioga = true
254
+
255
+ end
256
+
257
+ def initialize
258
+ reset_figures
259
+ end
260
+
261
+
262
+ def reset_state
263
+ reset_figures
264
+ end
265
+
266
+
267
+ def tex_xaxis_numeric_label
268
+ self.xaxis_numeric_label_tex
269
+ end
270
+
271
+ def tex_xaxis_numeric_label=(str)
272
+ self.xaxis_numeric_label_tex = str
273
+ end
274
+
275
+
276
+ def tex_yaxis_numeric_label
277
+ self.yaxis_numeric_label_tex
278
+ end
279
+
280
+ def tex_yaxis_numeric_label=(str)
281
+ self.yaxis_numeric_label_tex = str
282
+ end
283
+
284
+
285
+ def default_line_scale=(s)
286
+ self.rescale_lines(s)
287
+ end
288
+
289
+ def default_enter_page_function
290
+ page_setup(self.default_page_width,self.default_page_height)
291
+ set_frame_sides(self.default_frame_left,self.default_frame_right,
292
+ self.default_frame_top,self.default_frame_bottom)
293
+ end
294
+
295
+ def set_default_font_size(size, update_preview_size_string = true)
296
+ private_set_default_font_size(size)
297
+ return unless update_preview_size_string == true
298
+ @tex_preview_fontsize = sprintf("%0.2fbp", size)
299
+ end
300
+
301
+ def page_setup(width,height) # in big-points (1/72 inch)
302
+ set_device_pagesize(width*10-1, height*10-1)
303
+ @tex_preview_figure_width = width.to_s + 'bp'
304
+ @tex_preview_figure_height = height.to_s + 'bp'
305
+ @tex_preview_paper_height = "#{height}bp"
306
+ @tex_preview_paper_width = "#{width}bp"
307
+ @tex_preview_tiogafigure_command = 'tiogafigureshow'
308
+ @tex_preview_fullpage = false
309
+ @tex_xoffset = 0
310
+ @tex_yoffset = 0
311
+ @tex_preview_hoffset = '0in'
312
+ @tex_preview_voffset = '0in'
313
+ set_frame_sides(0,1,1,0)
314
+ set_bounds(
315
+ 'left_boundary' => 0, 'right_boundary' => 1,
316
+ 'top_boundary' => 1, 'bottom_boundary' => 0)
317
+ self.update_bbox(0,0)
318
+ self.update_bbox(0,1)
319
+ self.update_bbox(1,0)
320
+ self.update_bbox(1,1)
321
+ end
322
+
323
+ def tex_preview_preamble # for backward compatibility
324
+ self.tex_preamble
325
+ end
326
+
327
+ def tex_preview_preamble=(str)
328
+ self.tex_preamble = str
329
+ end
330
+
331
+ def figure_name(num)
332
+ @figure_names[num]
333
+ end
334
+
335
+ def figure_pdf(name)
336
+ if name.kind_of?(Integer)
337
+ num = name
338
+ name = @figure_names[num]
339
+ else
340
+ num = @figure_names.index(name)
341
+ end
342
+ return nil if num == nil
343
+ @figure_pdfs[num]
344
+ end
345
+
346
+ def line_color
347
+ self.stroke_color
348
+ end
349
+
350
+ def line_color=(color)
351
+ self.stroke_color=(color)
352
+ end
353
+
354
+ def stroke_width
355
+ self.line_width
356
+ end
357
+
358
+ def stroke_width=(width)
359
+ self.line_width=(width)
360
+ end
361
+
362
+
363
+ def opacity_for_stroke
364
+ self.stroke_opacity
365
+ end
366
+
367
+ def opacity_for_stroke=(frac)
368
+ self.stroke_opacity=(frac)
369
+ end
370
+
371
+ def opacity_for_fill
372
+ self.fill_opacity
373
+ end
374
+
375
+ def opacity_for_fill=(frac)
376
+ self.fill_opacity=(frac)
377
+ end
378
+
379
+
380
+ def transparency_for_stroke
381
+ 1.0 - self.stroke_opacity
382
+ end
383
+
384
+ def transparency_for_stroke=(frac)
385
+ self.stroke_opacity=(1.0 - frac)
386
+ end
387
+
388
+ def transparency_for_fill
389
+ 1.0 - self.fill_opacity
390
+ end
391
+
392
+ def transparency_for_fill=(frac)
393
+ self.fill_opacity=(1.0 - frac)
394
+ end
395
+
396
+
397
+ def fill_transparency
398
+ self.transparency_for_fill
399
+ end
400
+
401
+ def fill_transparency=(frac)
402
+ self.transparency_for_fill = frac
403
+ end
404
+
405
+ def stroke_transparency
406
+ self.transparency_for_stroke
407
+ end
408
+
409
+ def stroke_transparency=(frac)
410
+ self.transparency_for_stroke = frac
411
+ end
412
+
413
+
414
+ def title_visible=(bool)
415
+ if bool != false
416
+ raise "Sorry: can only set 'title_visible' to false."
417
+ end
418
+ self.no_title
419
+ end
420
+
421
+ def xlabel_visible=(bool)
422
+ if bool != false
423
+ raise "Sorry: can only set 'xlabel_visible' to false."
424
+ end
425
+ self.no_xlabel
426
+ end
427
+
428
+ def ylabel_visible=(bool)
429
+ if bool != false
430
+ raise "Sorry: can only set 'ylabel_visible' to false."
431
+ end
432
+ self.no_ylabel
433
+ end
434
+
435
+ def xaxis_visible=(bool)
436
+ if bool != false
437
+ raise "Sorry: can only set 'xaxis_visible' to false."
438
+ end
439
+ self.no_xaxis
440
+ end
441
+
442
+ def yaxis_visible=(bool)
443
+ if bool != false
444
+ raise "Sorry: can only set 'yaxis_visible' to false."
445
+ end
446
+ self.no_yaxis
447
+ end
448
+
449
+ def left_edge_visible=(bool)
450
+ if bool != false
451
+ raise "Sorry: can only set 'left_edge_visible' to false."
452
+ end
453
+ self.no_left_edge
454
+ end
455
+
456
+ def right_edge_visible=(bool)
457
+ if bool != false
458
+ raise "Sorry: can only set 'right_edge_visible' to false."
459
+ end
460
+ self.no_right_edge
461
+ end
462
+
463
+ def top_edge_visible=(bool)
464
+ if bool != false
465
+ raise "Sorry: can only set 'top_edge_visible' to false."
466
+ end
467
+ self.no_top_edge
468
+ end
469
+
470
+ def bottom_edge_visible=(bool)
471
+ if bool != false
472
+ raise "Sorry: can only set 'bottom_edge_visible' to false."
473
+ end
474
+ self.no_bottom_edge
475
+ end
476
+
477
+ Required = '!required!'.to_sym
478
+
479
+ # A helper function to prepare a default hash for a function, and to check
480
+ # for extraneous or missing arguments. It takes two arguments:
481
+ # * _dict_, the user-provided dictionnary to check and complete
482
+ # * _specs_, a hash representing the valid arguments and their default
483
+ # value.
484
+ # Symbols in the _specs_ hash have special meanings:
485
+ # * a normal symbol means: call self.symbol to get the default value. If
486
+ # this call fails with a MethodMissing error, the symbol is returned.
487
+ # * a '->stuff'.to_sym symbol means: copy the value from the value
488
+ # associated with the key 'stuff'.
489
+ # * a '!required!' symbol means that if the key is missing from
490
+ # the original, raise an exception. (use the Required consant for that)
491
+ #
492
+ # You should find a few examples in the constants of this class...
493
+ #
494
+ # This function modifies _dict_ in place !
495
+ def prepare_argument_hash(spec, dict, strict = true)
496
+ for key in dict.keys
497
+ if (not spec.key?(key)) and strict
498
+ warn "Unkown key #{key} for #{caller.first}, information removed"
499
+ dict.delete(key)
500
+ end
501
+ end
502
+ for key in spec.keys
503
+ dict[key] = argument_value(spec, dict, key)
504
+ end
505
+ return dict
506
+ end
507
+
508
+ # Returns the value associated with the given key. Default value is
509
+ # returned in case the key is missing from the _dict_ hash. See
510
+ def argument_value(spec, dict, key)
511
+ return dict[key] if dict.key?(key)
512
+ raise "Unknown key asked for" unless spec.key?(key)
513
+ default = spec[key]
514
+ return default unless default.is_a?(::Symbol)
515
+ if default.to_s =~ /^->(.*)/ # Special symbol:
516
+ # return the value of the pointed key
517
+ return argument_value(spec, dict, $1)
518
+ elsif default == Required
519
+ raise "Required key #{key} is missing from #{dict.inspect}"
520
+ end
521
+ begin
522
+ return self.send(default)
523
+ rescue NoMethodError
524
+ return default
525
+ end
526
+ end
527
+
528
+ @@keys_for_save_legend_info = FigureMaker.make_name_lookup_hash(['text', 'dy',
529
+ 'line_color', 'stroke_color', 'line_width', 'stroke_width',
530
+ 'line_cap', 'line_type', 'marker', 'marker_color', 'marker_scale', 'marker_dict' ])
531
+
532
+ Save_legend_info_args = {
533
+ 'text' => nil,
534
+ 'dy' => :legend_text_dy,
535
+ 'line_color' => :line_color,
536
+ 'line_width' => :line_width,
537
+ 'line_cap' => :line_cap,
538
+ 'line_type' => :line_type,
539
+ 'stroke_color' => nil,
540
+ 'stroke_width' => nil,
541
+ 'marker' => nil,
542
+ 'marker_dict' => nil, # use again #prepare_argument_hash ?
543
+ 'marker_color' => nil,
544
+ 'marker_scale' => nil,
545
+ }
546
+
547
+ def save_legend_info(arg)
548
+ if arg.kind_of?String
549
+ dict = { 'text' => arg }
550
+ else
551
+ dict = arg
552
+ end
553
+ prepare_argument_hash(Save_legend_info_args, dict)
554
+ if dict['marker_dict'] == nil
555
+ dict['marker'] = false if dict['marker'] == nil
556
+ dict['marker_color'] = self.line_color if dict['marker_color'] == nil
557
+ dict['marker_scale'] = 0.5 if dict['marker_scale'] == nil
558
+ end
559
+ @legend_info << dict
560
+ end
561
+
562
+ def show_legend
563
+ char_dx = self.default_text_height_dx
564
+ char_dy = self.default_text_height_dy
565
+ line_ht_x = char_dx * self.legend_scale
566
+ line_ht_y = char_dy * self.legend_scale
567
+ x = self.legend_text_xstart*line_ht_x
568
+ ltw = self.legend_text_width
569
+ if ltw < 0
570
+ if @pr_margin == 0
571
+ ltw = 1
572
+ else
573
+ ltw = 7
574
+ end
575
+ end
576
+ xright = x + ltw*line_ht_x
577
+ y = 1.0 - self.legend_text_ystart*line_ht_y
578
+ update_bbox(xright, y)
579
+ dy = -self.legend_text_dy*line_ht_y
580
+ line_x0 = self.legend_line_x0*line_ht_x
581
+ line_x1 = self.legend_line_x1*line_ht_x
582
+ line_dy = self.legend_line_dy*line_ht_y
583
+ self.label_left_margin = self.label_right_margin = self.label_top_margin = self.label_bottom_margin = -1e99
584
+ @legend_info.each do |dict|
585
+ text = dict['text']
586
+ if text != nil
587
+ show_text('text' => text,
588
+ 'x' => x, 'y' => y, 'scale' => self.legend_scale,
589
+ 'justification' => self.legend_justification,
590
+ 'alignment' => self.legend_alignment)
591
+ end
592
+ line_width = dict['line_width']
593
+ if line_width >= 0
594
+ self.line_color = dict['line_color']
595
+ self.line_width = dict['line_width']
596
+ self.line_cap = dict['line_cap']
597
+ self.line_type = dict['line_type']
598
+ stroke_line(line_x0, y+line_dy, line_x1, y+line_dy)
599
+ end
600
+ # place any marker right in the middle of the line
601
+ if dict['marker_dict'] != nil
602
+ marker_dict = dict['marker_dict']
603
+ marker_dict['x'] = 0.5*(line_x0 + line_x1)
604
+ marker_dict['y'] = y+line_dy
605
+ show_marker(marker_dict)
606
+ elsif dict['marker']
607
+ show_marker( 'x' => 0.5*(line_x0 + line_x1),
608
+ 'y' => (y+line_dy),
609
+ 'marker' => dict['marker'],
610
+ 'color' => dict['marker_color'],
611
+ 'scale' => dict['marker_scale'])
612
+ end
613
+ dy = dict['dy']; dy = 1 if dy == nil
614
+ y -= line_ht_y * dy
615
+ end
616
+ end
617
+
618
+ def legend_height
619
+ height = 0.0
620
+ @legend_info.each { |dict| height += dict['dy'] }
621
+ return height
622
+ end
623
+
624
+ def reset_legend_info
625
+ @legend_info = [ ]
626
+ end
627
+
628
+ def save_legend_separator(dy)
629
+ save_legend_info('dy' => dy, 'line_width' => -1)
630
+ end
631
+
632
+ def do_box_labels(title, xlabel, ylabel)
633
+ if title != nil
634
+ show_title(title); no_title
635
+ end
636
+ if xlabel != nil
637
+ show_xlabel(xlabel); no_xlabel
638
+ end
639
+ if ylabel != nil
640
+ show_ylabel(ylabel); no_ylabel
641
+ end
642
+ end
643
+
644
+ def set_aspect_ratio_relative_to_frame(width_to_height)
645
+ if width_to_height >= 1
646
+ margin = (1 - 1.0/width_to_height) * 0.5
647
+ set_subframe('top_margin' => margin, 'bottom_margin' => margin)
648
+ else
649
+ margin = (1 - width_to_height) * 0.5
650
+ set_subframe('left_margin' => margin, 'right_margin' => margin)
651
+ end
652
+ end
653
+
654
+ def set_physical_aspect_ratio(width_to_height)
655
+ wd = convert_frame_to_page_dx(1)
656
+ ht = convert_frame_to_page_dy(1)
657
+ set_aspect_ratio_relative_to_frame(width_to_height * ht / wd)
658
+ end
659
+
660
+ def set_aspect_ratio(width_to_height)
661
+ set_physical_aspect_ratio(width_to_height)
662
+ end
663
+
664
+ def set_landscape
665
+ set_aspect_ratio(11.0/8.5)
666
+ end
667
+
668
+ def landscape
669
+ set_landscape
670
+ end
671
+
672
+ def set_portrait
673
+ set_aspect_ratio(8.5/11.0)
674
+ end
675
+
676
+ def portrait
677
+ set_portrait
678
+ end
679
+
680
+ @@keys_for_set_subframe = FigureMaker.make_name_lookup_hash(['margins',
681
+ 'left_margin', 'right_margin', 'top_margin', 'bottom_margin',
682
+ 'left', 'right', 'plot_left_margin', 'top', 'bottom' ])
683
+ def set_subframe(dict=nil)
684
+ return if dict == nil
685
+ if dict.kind_of?Array and dict.size == 4
686
+ left = dict[0]
687
+ right = dict[1]
688
+ top = dict[2]
689
+ bottom = dict[3]
690
+ else
691
+ check_dict(dict, @@keys_for_set_subframe, 'set_subframe')
692
+ if (margins = dict['margins']) != nil and margins.kind_of?Array and margins.size == 4
693
+ left = margins[0]
694
+ right = margins[1]
695
+ top = margins[2]
696
+ bottom = margins[3]
697
+ else
698
+ left = alt_names(dict, 'left_margin', 'left')
699
+ right = alt_names(dict, 'right_margin', 'right')
700
+ top = alt_names(dict, 'top_margin', 'top')
701
+ bottom = alt_names(dict, 'bottom_margin', 'bottom')
702
+ end
703
+ end
704
+ left = 0 if left == nil
705
+ right = 0 if right == nil
706
+ top = 0 if top == nil
707
+ bottom = 0 if bottom == nil
708
+ private_set_subframe(left, right, top, bottom)
709
+ end
710
+
711
+ @@keys_for_set_bounds = FigureMaker.make_name_lookup_hash([
712
+ 'bounds_left', 'bounds_right', 'bounds_top', 'bounds_bottom',
713
+ 'left_boundary', 'right_boundary', 'top_boundary', 'bottom_boundary', 'boundaries' ])
714
+ def set_bounds(dict=nil)
715
+ return if dict == nil
716
+ if dict.kind_of?Array and dict.size == 4
717
+ left = dict[0]
718
+ right = dict[1]
719
+ top = dict[2]
720
+ bottom = dict[3]
721
+ else
722
+ check_dict(dict, @@keys_for_set_bounds, 'set_bounds')
723
+ if (boundaries = dict['boundaries']) != nil and boundaries.kind_of?Array and boundaries.size == 4
724
+ left = boundaries[0]
725
+ right = boundaries[1]
726
+ top = boundaries[2]
727
+ bottom = boundaries[3]
728
+ else
729
+ left = complain_if_missing_numeric_arg(dict,'bounds_left','left_boundary','set_bounds')
730
+ right = complain_if_missing_numeric_arg(dict,'bounds_right','right_boundary','set_bounds')
731
+ top = complain_if_missing_numeric_arg(dict,'bounds_top','top_boundary','set_bounds')
732
+ bottom = complain_if_missing_numeric_arg(dict,'bounds_bottom','bottom_boundary','set_bounds')
733
+ end
734
+ end
735
+ private_set_bounds(left, right, top, bottom)
736
+ end
737
+
738
+
739
+ def trace_cmd_no_arg(entry_function, exit_function, &cmd)
740
+
741
+ unless entry_function == nil
742
+ begin
743
+ entry_function.call
744
+ rescue Exception => er
745
+ report_error(er, nil)
746
+ end
747
+ end
748
+
749
+ result = cmd.call
750
+
751
+ unless exit_function == nil
752
+ begin
753
+ exit_function.call
754
+ rescue Exception => er
755
+ report_error(er, nil)
756
+ end
757
+ end
758
+
759
+ return result
760
+
761
+ end
762
+
763
+
764
+ def trace_cmd_one_arg(entry_function, exit_function, arg, &cmd)
765
+
766
+ unless entry_function == nil
767
+ begin
768
+ entry_function.call(arg)
769
+ rescue Exception => er
770
+ report_error(er, nil)
771
+ end
772
+ end
773
+
774
+ result = cmd.call
775
+
776
+ unless exit_function == nil
777
+ begin
778
+ exit_function.call(arg)
779
+ rescue Exception => er
780
+ report_error(er, nil)
781
+ end
782
+ end
783
+
784
+ return result
785
+
786
+ end
787
+
788
+
789
+ def show_plot(bounds=nil,&cmd)
790
+ trace_cmd_one_arg(@enter_show_plot_function, @exit_show_plot_function, bounds) {
791
+ set_bounds(bounds)
792
+ context { clip_to_frame; cmd.call }
793
+ show_plot_box
794
+ }
795
+ end
796
+
797
+ def subfigure(margins=nil,&cmd)
798
+ trace_cmd_one_arg(@enter_subfigure_function, @exit_subfigure_function, margins) {
799
+ context { doing_subfigure; set_subframe(margins); cmd.call }
800
+ }
801
+ end
802
+
803
+ def root_plot
804
+ return ! self.in_subplot
805
+ end
806
+
807
+ def in_subfigure
808
+ return ! self.root_figure
809
+ end
810
+
811
+ def subplot(margins=nil,&cmd)
812
+ trace_cmd_one_arg(@enter_subplot_function, @exit_subplot_function, margins) {
813
+ reset_legend_info
814
+ context { doing_subplot; set_subframe(margins); cmd.call }
815
+ }
816
+ end
817
+
818
+
819
+ @@keys_for_column_margins = FigureMaker.make_name_lookup_hash([
820
+ 'left_margin', 'right_margin', 'column_margin', 'column',
821
+ 'first_column', 'last_column', 'num_columns' ])
822
+ def column_margins(dict)
823
+ check_dict(dict, @@keys_for_column_margins, 'column_margins')
824
+ left_margin = get_if_given_else_default(dict, 'left_margin', 0)
825
+ right_margin = get_if_given_else_default(dict, 'right_margin', 0)
826
+ num_columns = dict['num_columns']
827
+ first_column = dict['first_column']
828
+ last_column = dict['last_column']
829
+ column_margin = dict['column_margin']
830
+ column = dict['column']
831
+ if column != nil
832
+ first_column = last_column = column
833
+ end
834
+ first_column = 1 if first_column == nil
835
+ last_column = first_column if last_column == nil
836
+ first_column -= 1; last_column -= 1 # switch to 0 for left column instead of 1
837
+ num_columns = last_column + 1 if num_columns == nil
838
+ column_margin = 0 if column_margin == nil
839
+ columns_after = num_columns - last_column - 1
840
+ columns_before = first_column
841
+ column_width = (1.0 - (left_margin + right_margin + column_margin * (num_columns-1))) / num_columns
842
+ left_margin = left_margin + columns_before * (column_width + column_margin)
843
+ right_margin = right_margin + columns_after * (column_width + column_margin)
844
+ return { 'left_margin' => left_margin, 'right_margin' => right_margin }
845
+ end
846
+
847
+ @@keys_for_row_margins = FigureMaker.make_name_lookup_hash([
848
+ 'top_margin', 'bottom_margin', 'row_margin', 'row',
849
+ 'first_row', 'last_row', 'num_rows' ])
850
+ def row_margins(dict)
851
+ check_dict(dict, @@keys_for_row_margins, 'row_margins')
852
+ top_margin = get_if_given_else_default(dict, 'top_margin', 0)
853
+ bottom_margin = get_if_given_else_default(dict, 'bottom_margin', 0)
854
+ num_rows = dict['num_rows']
855
+ first_row = dict['first_row']
856
+ last_row = dict['last_row']
857
+ row_margin = dict['row_margin']
858
+ row = dict['row']
859
+ if row != nil
860
+ first_row = last_row = row
861
+ end
862
+ first_row = 1 if first_row == nil
863
+ last_row = first_row if last_row == nil
864
+ first_row -= 1; last_row -= 1 # switch to 0 for top row instead of 1
865
+ num_rows = last_row + 1 if num_rows == nil
866
+ row_margin = 0 if row_margin == nil
867
+ rows_below = num_rows - last_row - 1
868
+ rows_above = first_row
869
+ row_width = (1.0 - (top_margin + bottom_margin + row_margin * (num_rows-1))) / num_rows
870
+ top_margin = top_margin + rows_above * (row_width + row_margin)
871
+ bottom_margin = bottom_margin + rows_below * (row_width + row_margin)
872
+ return { 'top_margin' => top_margin, 'bottom_margin' => bottom_margin }
873
+ end
874
+
875
+
876
+ def context(&cmd)
877
+ trace_cmd_no_arg(@enter_context_function, @exit_context_function) {
878
+ private_context(cmd) }
879
+ end
880
+
881
+
882
+ def rescale(factor)
883
+ rescale_text(factor)
884
+ rescale_lines(factor)
885
+ end
886
+
887
+ def show_plot_box
888
+ show_xaxis
889
+ show_yaxis
890
+ show_edges
891
+ show_title
892
+ show_xlabel
893
+ show_ylabel
894
+ end
895
+
896
+ def show_xaxis
897
+ show_axis(self.xaxis_loc) if self.xaxis_visible
898
+ end
899
+
900
+ def show_yaxis
901
+ show_axis(self.yaxis_loc) if self.yaxis_visible
902
+ end
903
+
904
+ def show_edges
905
+ show_top_edge
906
+ show_bottom_edge
907
+ show_left_edge
908
+ show_right_edge
909
+ end
910
+
911
+ def show_top_edge
912
+ show_edge(TOP) if self.top_edge_visible && self.xaxis_loc != TOP
913
+ end
914
+
915
+ def show_bottom_edge
916
+ show_edge(BOTTOM) if self.bottom_edge_visible && self.xaxis_loc != BOTTOM
917
+ end
918
+
919
+ def show_left_edge
920
+ show_edge(LEFT) if self.left_edge_visible && self.yaxis_loc != LEFT
921
+ end
922
+
923
+ def show_right_edge
924
+ show_edge(RIGHT) if self.right_edge_visible && self.yaxis_loc != RIGHT
925
+ end
926
+
927
+ def show_title(text = nil)
928
+ text = self.title if text == nil
929
+ if (self.title_visible && text != nil)
930
+ show_text('text' => text,
931
+ 'side' => self.title_side, 'position' => self.title_position,
932
+ 'scale' => self.title_scale, 'shift' => self.title_shift,
933
+ 'angle' => self.title_angle, 'alignment' => self.title_alignment,
934
+ 'justification' => self.title_justification,
935
+ 'color' => self.title_color)
936
+ end
937
+ end
938
+
939
+ def show_xlabel(text = nil)
940
+ text = self.xlabel if text == nil
941
+ if (self.xlabel_visible && text != nil)
942
+ angle = self.xlabel_angle
943
+ side = self.xlabel_side
944
+ shift = self.xlabel_shift
945
+ shift -= 0.5 if (side == TOP && angle == 0)
946
+ show_text('text' => text,
947
+ 'side' => side, 'position' => self.xlabel_position,
948
+ 'scale' => self.xlabel_scale, 'shift' => shift,
949
+ 'angle' => angle, 'alignment' => self.xlabel_alignment,
950
+ 'justification' => self.xlabel_justification,
951
+ 'color' => self.xlabel_color)
952
+ end
953
+ end
954
+
955
+ def show_ylabel(text = nil)
956
+ text = self.ylabel if text == nil
957
+ if (self.ylabel_visible && text != nil)
958
+ angle = self.ylabel_angle
959
+ side = self.ylabel_side
960
+ shift = self.ylabel_shift
961
+ shift += 0.5 if (side == RIGHT && angle == 0)
962
+ show_text('text' => text,
963
+ 'side' => side, 'position' => self.ylabel_position,
964
+ 'scale' => self.ylabel_scale, 'shift' => shift,
965
+ 'angle' => angle, 'alignment' => self.ylabel_alignment,
966
+ 'justification' => self.ylabel_justification,
967
+ 'color' => self.ylabel_color)
968
+ end
969
+ end
970
+
971
+ @@keys_for_show_plot_with_legend = FigureMaker.make_name_lookup_hash([
972
+ 'legend_top_margin', 'legend_bottom_margin', 'legend_left_margin', 'legend_right_margin',
973
+ 'plot_top_margin', 'plot_bottom_margin', 'plot_left_margin', 'plot_right_margin',
974
+ 'plot_scale', 'legend_scale' ])
975
+ def show_plot_with_legend(dict=nil, &cmd)
976
+ check_dict(dict, @@keys_for_show_plot_with_legend, 'show_plot_with_legend') if dict != nil
977
+ legend_top_margin = get_if_given_else_use_default_dict(dict, 'legend_top_margin', @legend_defaults)
978
+ legend_bottom_margin = get_if_given_else_use_default_dict(dict, 'legend_bottom_margin', @legend_defaults)
979
+ legend_left_margin = get_if_given_else_use_default_dict(dict, 'legend_left_margin', @legend_defaults)
980
+ legend_right_margin = get_if_given_else_use_default_dict(dict, 'legend_right_margin', @legend_defaults)
981
+ legend_scale = get_if_given_else_use_default_dict(dict, 'legend_scale', @legend_defaults)
982
+ plot_top_margin = get_if_given_else_use_default_dict(dict, 'plot_top_margin', @legend_defaults)
983
+ plot_bottom_margin = get_if_given_else_use_default_dict(dict, 'plot_bottom_margin', @legend_defaults)
984
+ plot_left_margin = get_if_given_else_use_default_dict(dict, 'plot_left_margin', @legend_defaults)
985
+ plot_right_margin = get_if_given_else_use_default_dict(dict, 'plot_right_margin', @legend_defaults)
986
+ plot_scale = get_if_given_else_use_default_dict(dict, 'plot_scale', @legend_defaults)
987
+ reset_legend_info
988
+ rescale(plot_scale)
989
+ subplot([plot_left_margin, plot_right_margin, plot_top_margin, plot_bottom_margin]) { cmd.call }
990
+ set_subframe([legend_left_margin, legend_right_margin, legend_top_margin, legend_bottom_margin])
991
+ rescale(legend_scale) # note that legend_scale is an addition to the plot_scale, not a replacement
992
+ @pr_margin = plot_right_margin
993
+ show_legend
994
+ end
995
+
996
+ def append_points_with_gaps_to_path(xs, ys, gaps, close_subpaths = false)
997
+ private_append_points_with_gaps_to_path(xs, ys, gaps, close_subpaths)
998
+ end
999
+
1000
+ def show_polyline(xs, ys, color = nil, legend = nil, type = nil, gaps = nil, close_subpaths = nil)
1001
+ context do
1002
+ self.line_type = type if type != nil
1003
+ self.stroke_color = color if color != nil
1004
+ append_points_with_gaps_to_path(xs, ys, gaps, close_subpaths)
1005
+ stroke
1006
+ save_legend_info(legend) if legend != nil
1007
+ end
1008
+ end
1009
+
1010
+ def stroke_polyline(xs, ys, color = nil, legend = nil, type = nil, gaps = nil, close_subpaths = nil)
1011
+ show_polyline(xs, ys, color, legend, type, gaps, close_subpaths)
1012
+ end
1013
+
1014
+ def show_contour(xs, ys, gaps = nil, color = nil, type = nil, legend = nil)
1015
+ show_polyline(xs, ys, color, legend, type, gaps, false)
1016
+ end
1017
+
1018
+ @@keys_for_make_contour = FigureMaker.make_name_lookup_hash([
1019
+ 'dest_xs', 'dest_ys', 'z_level', 'z', 'level', 'xs', 'ys', 'gaps', 'zs', 'data', 'legit', 'method'])
1020
+ def make_contour(dict)
1021
+ check_dict(dict, @@keys_for_make_contour, 'make_contour')
1022
+ z_level = dict['z_level']
1023
+ if z_level == nil
1024
+ z_level = complain_if_missing_numeric_arg(dict, 'z', 'level', 'make_contour')
1025
+ end
1026
+ dest_xs = get_dvec(dict, 'dest_xs', 'make_contour')
1027
+ dest_ys = get_dvec(dict, 'dest_ys', 'make_contour')
1028
+ xs = get_dvec(dict, 'xs', 'make_contour')
1029
+ ys = get_dvec(dict, 'ys', 'make_contour')
1030
+ gaps = dict['gaps']
1031
+ if (!(gaps.kind_of? Array))
1032
+ raise "Sorry: 'gaps' for 'make_contour' must be an Array"
1033
+ end
1034
+ zs = alt_names(dict, 'zs', 'data')
1035
+ if (!(zs.kind_of? Dtable))
1036
+ raise "Sorry: 'zs' for 'make_contour' must be a Dtable"
1037
+ end
1038
+ dest_xs.clear; dest_ys.clear; gaps.clear
1039
+
1040
+ legit = dict['legit']
1041
+ if legit == nil
1042
+ legit = Dtable.new(xs.length,ys.length).set(1.0)
1043
+ elsif (!(legit.kind_of? Dtable))
1044
+ raise "Sorry: 'legit' for 'make_contour' must be a Dtable -- nonzero means legitimate value in corresponding entry in zs"
1045
+ end
1046
+
1047
+ method = dict['method']
1048
+ use_conrec = (method == 'conrec' or method == 'CONREC')? 1 : 0
1049
+ private_make_contour(dest_xs, dest_ys, gaps, xs, ys, zs, z_level, legit, use_conrec)
1050
+
1051
+ end
1052
+
1053
+ @@keys_for_make_steps = FigureMaker.make_name_lookup_hash([
1054
+ 'xfirst', 'x_first', 'yfirst', 'y_first', 'xlast', 'x_last', 'ylast', 'y_last',
1055
+ 'xs', 'ys', 'dest_xs', 'dest_ys'])
1056
+ def make_steps(dict)
1057
+ check_dict(dict, @@keys_for_make_steps, 'make_steps')
1058
+ xfirst = complain_if_missing_numeric_arg(dict, 'xfirst', 'x_first', 'make_steps')
1059
+ yfirst = complain_if_missing_numeric_arg(dict, 'yfirst', 'y_first', 'make_steps')
1060
+ xlast = complain_if_missing_numeric_arg(dict, 'xlast', 'x_last', 'make_steps')
1061
+ ylast = complain_if_missing_numeric_arg(dict, 'ylast', 'y_last', 'make_steps')
1062
+ dest_xs = get_dvec(dict, 'dest_xs', 'make_steps')
1063
+ dest_ys = get_dvec(dict, 'dest_ys', 'make_steps')
1064
+ xs = get_dvec(dict, 'xs', 'make_steps')
1065
+ ys = get_dvec(dict, 'ys', 'make_steps')
1066
+ private_make_steps(dest_xs, dest_ys, xs, ys, xfirst, yfirst, xlast, ylast)
1067
+ end
1068
+
1069
+ @@keys_for_make_curves = FigureMaker.make_name_lookup_hash([
1070
+ 'start_slope', 'end_slope', 'xs', 'ys', 'sample_xs', 'result_ys'])
1071
+ def make_spline_interpolated_points(dict)
1072
+ check_dict(dict, @@keys_for_make_curves, 'make_spline_interpolated_points')
1073
+ start_slope = dict['start_slope']
1074
+ end_slope = dict['end_slope']
1075
+ sample_xs = get_dvec(dict, 'sample_xs', 'make_spline_interpolated_points')
1076
+ result_ys = get_dvec(dict, 'result_ys', 'make_spline_interpolated_points')
1077
+ xs = get_dvec(dict, 'xs', 'make_spline_interpolated_points')
1078
+ ys = get_dvec(dict, 'ys', 'make_spline_interpolated_points')
1079
+ private_make_spline_interpolated_points(sample_xs, result_ys, xs, ys, start_slope, end_slope)
1080
+ end
1081
+
1082
+ @@keys_for_make_interpolant = FigureMaker.make_name_lookup_hash([
1083
+ 'start_slope', 'end_slope', 'xs', 'ys'])
1084
+ def make_interpolant(dict)
1085
+ check_dict(dict, @@keys_for_make_interpolant, 'make_smoothed')
1086
+ start_slope = dict['start_slope']
1087
+ end_slope = dict['end_slope']
1088
+ xs = get_dvec(dict, 'xs', 'make_interpolant')
1089
+ ys = get_dvec(dict, 'ys', 'make_interpolant')
1090
+ return Dvector.create_spline_interpolant(xs, ys,
1091
+ (start_slope != nil), start_slope, (end_slope != nil), end_slope)
1092
+ end
1093
+
1094
+ def append_interpolant_to_path(interp)
1095
+ x_s = interp[0]; y_s = interp[1]; a_s = interp[2]; b_s = interp[3]; c_s = interp[4]
1096
+ ctrl = Dvector.new
1097
+ move_to_point(x_s[0], y_s[0]);
1098
+ (x_s.size - 1).times do |i|
1099
+ x0 = x_s[i]; delta_x = x_s[i+1] - x0
1100
+ ctrl.make_bezier_control_points_for_cubic_in_x(x0, y_s[i], delta_x, a_s[i], b_s[i], c_s[i])
1101
+ append_curve_to_path(ctrl[0], ctrl[1], ctrl[2], ctrl[3], ctrl[4], ctrl[5])
1102
+ end
1103
+ end
1104
+
1105
+ @@keys_for_show_error_bars = FigureMaker.make_name_lookup_hash(['x', 'y', 'dx', 'dy',
1106
+ 'dx_plus', 'dx_minus', 'dy_plus', 'dy_minus', 'color', 'end_cap', 'line_width'])
1107
+ def show_error_bars(dict)
1108
+ check_dict(dict, @@keys_for_show_error_bars, 'show_error_bars')
1109
+ x = dict['x']
1110
+ y = dict['y']
1111
+ if (x == nil || y == nil)
1112
+ raise "Sorry: Must give both 'x' and 'y' for show_error_bar."
1113
+ end
1114
+ dx = dict['dx']
1115
+ dx_plus = get_if_given_else_default(dict, 'dx_plus', dx)
1116
+ dx_minus = get_if_given_else_default(dict, 'dx_minus', dx)
1117
+ dy = dict['dy']
1118
+ dy_plus = get_if_given_else_default(dict, 'dy_plus', dy)
1119
+ dy_minus = get_if_given_else_default(dict, 'dy_minus', dy)
1120
+ if (dx_plus == nil || dx_minus == nil || dy_plus == nil || dy_minus == nil)
1121
+ raise "Sorry: Must give both 'dx' and 'dy' error ranges for show_error_bar."
1122
+ end
1123
+ end_cap = get_if_given_else_default(dict, 'end_cap', 0.15) # end_cap length in default text heights
1124
+ x_end_cap = end_cap * self.default_text_height_dy
1125
+ y_end_cap = end_cap * self.default_text_height_dx
1126
+ line_width = get_if_given_else_default(dict, 'line_width', 1)
1127
+ prev_line_width = self.line_width
1128
+ self.line_width = line_width if prev_line_width != line_width
1129
+ color = get_if_given_else_default(dict, 'color', Black)
1130
+ prev_color = self.stroke_color
1131
+ self.stroke_color = color if prev_color != color
1132
+ # vertical error (dy)
1133
+ if (dy_plus != 0 || dy_minus != 0)
1134
+ stroke_line(x, y+dy_plus, x, y-dy_minus)
1135
+ stroke_line(x-y_end_cap, y+dy_plus, x+y_end_cap, y+dy_plus)
1136
+ stroke_line(x-y_end_cap, y-dy_minus, x+y_end_cap, y-dy_minus)
1137
+ end
1138
+ # horizontal error (dx)
1139
+ if (dx_plus != 0 || dx_minus != 0)
1140
+ stroke_line(x+dx_plus, y, x-dx_minus, y)
1141
+ stroke_line(x+dx_plus, y-x_end_cap, x+dx_plus, y+x_end_cap)
1142
+ stroke_line(x-dx_minus, y-x_end_cap, x-dx_minus, y+x_end_cap)
1143
+ end
1144
+ self.line_width = prev_line_width if prev_line_width != line_width
1145
+ self.stroke_color = prev_color if prev_color != color
1146
+ end
1147
+
1148
+ @@keys_for_show_arrow = FigureMaker.make_name_lookup_hash([
1149
+ 'x_head', 'y_head', 'x_tail', 'y_tail', 'head', 'tail',
1150
+ 'color', 'head_color', 'tail_color', 'line_color',
1151
+ 'line_style',
1152
+ 'head_marker', 'tail_marker', 'line_width',
1153
+ 'head_angle', 'head_just', 'tail_just', 'tail_angle',
1154
+ 'head_scale', 'tail_scale'])
1155
+
1156
+ # TODO: add linestyle capacities to arrows.
1157
+ def show_arrow(dict)
1158
+ check_dict(dict, @@keys_for_show_arrow, 'show_arrow')
1159
+ if check_pair(head = dict['head'], 'head', 'show_arrow')
1160
+ x_head = head[0]
1161
+ y_head = head[1]
1162
+ else
1163
+ x_head = dict['x_head']
1164
+ y_head = dict['y_head']
1165
+ end
1166
+ if (x_head == nil || y_head == nil)
1167
+ raise "Sorry: Must give both 'x_head' and 'y_head' for show_arrow."
1168
+ end
1169
+ if check_pair(tail = dict['tail'], 'tail', 'show_arrow')
1170
+ x_tail = tail[0]
1171
+ y_tail = tail[1]
1172
+ else
1173
+ x_tail = dict['x_tail']
1174
+ y_tail = dict['y_tail']
1175
+ end
1176
+ if (x_tail == nil || y_tail == nil)
1177
+ raise "Sorry: Must give both 'x_tail' and 'y_tail' for show_arrow."
1178
+ end
1179
+ color = get_if_given_else_default(dict, 'color', Black)
1180
+ head_color = get_if_given_else_default(dict, 'head_color', color)
1181
+ tail_color = get_if_given_else_default(dict, 'tail_color', color)
1182
+ head_angle = get_if_given_else_default(dict, 'head_angle', 0)
1183
+ tail_angle = get_if_given_else_default(dict, 'tail_angle', -180)
1184
+ line_color = get_if_given_else_default(dict, 'line_color', color)
1185
+ head_marker = get_if_given_else_default(dict, 'head_marker', Arrowhead)
1186
+ tail_marker = get_if_given_else_default(dict, 'tail_marker', BarThin)
1187
+ line_width = get_if_given_else_default(dict, 'line_width', 1)
1188
+ head_scale = dict['head_scale']
1189
+ head_scale = @marker_defaults['scale'] if head_scale == nil
1190
+ tail_scale = dict['tail_scale']
1191
+
1192
+ # Automatic detection of marker justification
1193
+ dict['tail_just'] ||= if tail_marker == Arrowhead || tail_marker == ArrowheadOpen
1194
+ RIGHT_JUSTIFIED
1195
+ else
1196
+ CENTERED
1197
+ end
1198
+
1199
+ dict['head_just'] ||= if head_marker == Arrowhead || head_marker == ArrowheadOpen
1200
+ RIGHT_JUSTIFIED
1201
+ else
1202
+ CENTERED
1203
+ end
1204
+
1205
+ prev_line_cap = self.line_cap
1206
+ self.line_cap = LINE_CAP_BUTT if prev_line_cap != LINE_CAP_BUTT
1207
+ prev_line_width = self.line_width
1208
+ self.line_width = line_width if prev_line_width != line_width
1209
+ prev_stroke_color = self.stroke_color
1210
+ self.stroke_color = line_color if line_color != prev_stroke_color
1211
+ prev_line_type = self.line_type
1212
+ if dict['line_style']
1213
+ self.line_type = dict['line_style']
1214
+ end
1215
+ dx = x_head - x_tail
1216
+ dy = y_head - y_tail
1217
+ pg_dx = convert_figure_to_output_dx(dx)
1218
+ pg_dy = convert_figure_to_output_dy(dy)
1219
+ len = (pg_dx*pg_dx + pg_dy*pg_dy).sqrt
1220
+ chsz = convert_figure_to_output_dx(default_text_height_dx) * head_scale
1221
+ chsz = -chsz if chsz < 0
1222
+
1223
+ tail_frac = if dict['tail_just'] == RIGHT_JUSTIFIED
1224
+ 0.5 * chsz/len
1225
+ else
1226
+ 0.0
1227
+ end
1228
+ head_frac = if dict['head_just'] == RIGHT_JUSTIFIED
1229
+ (len - 0.5 * chsz)/len
1230
+ else
1231
+ 1.0
1232
+ end
1233
+ if head_frac < tail_frac # Pretty uncommon, I guess
1234
+ head_frac = tail_frac = 0.5 # We put everything in the middle, then
1235
+ end
1236
+
1237
+ # Don't actually stroke the line if line_width is null. Can be used
1238
+ # to draw only the markers.
1239
+ stroke_line(x_tail + tail_frac*dx, y_tail + tail_frac*dy,
1240
+ x_tail + head_frac*dx, y_tail + head_frac*dy) if line_width > 0
1241
+ angle = convert_to_degrees(dx, dy)
1242
+ if head_marker != 'None'
1243
+ show_marker('marker' => head_marker, 'point' => [x_head, y_head], 'color' => head_color,
1244
+ 'justification' => dict['head_just'], 'angle'=> (angle + head_angle), 'scale' => head_scale)
1245
+ end
1246
+ if tail_marker != 'None'
1247
+ if tail_marker == Arrowhead || tail_marker == ArrowheadOpen
1248
+ just = RIGHT_JUSTIFIED
1249
+ else
1250
+ just = CENTERED
1251
+ end
1252
+ show_marker('marker' => tail_marker, 'point' => [x_tail, y_tail], 'color' => tail_color,
1253
+ 'angle'=> (angle + tail_angle), 'scale' => tail_scale, 'justification' => dict['tail_just'])
1254
+ end
1255
+ self.line_cap = prev_line_cap if prev_line_cap != LINE_CAP_BUTT
1256
+ self.line_width = prev_line_width if prev_line_width != line_width
1257
+ self.stroke_color = prev_stroke_color if prev_stroke_color != line_color
1258
+ self.line_type = prev_line_type
1259
+ end
1260
+
1261
+ @@keys_for_axial_shading = FigureMaker.make_name_lookup_hash(['extend_start', 'extend_end',
1262
+ 'x_start', 'y_start', 'start', 'start_point', 'x_end', 'y_end', 'end', 'end_point', 'colormap', 'color_map'])
1263
+ def axial_shading(dict)
1264
+ check_dict(dict, @@keys_for_axial_shading, 'axial_shading')
1265
+ x_start = y_start = nil
1266
+ start = alt_names(dict, 'start', 'start_point')
1267
+ if check_pair(start, 'start', 'axial_shading')
1268
+ x_start = start[0]; y_start = start[1];
1269
+ end
1270
+ x_start = dict['x_start'] if x_start == nil
1271
+ y_start = dict['y_start'] if y_start == nil
1272
+ if (x_start == nil || y_start == nil)
1273
+ raise "Sorry: Must give both 'x_start' and 'y_start' for axial_shading."
1274
+ end
1275
+ x_end = y_end = nil
1276
+ endpt = alt_names(dict, 'end', 'end_point')
1277
+ if check_pair(endpt, 'end', 'axial_shading')
1278
+ x_end = endpt[0]; y_end= endpt[1];
1279
+ end
1280
+ x_end = dict['x_end'] if x_end == nil
1281
+ y_end = dict['y_end'] if y_end == nil
1282
+ if (x_end == nil || y_end == nil)
1283
+ raise "Sorry: Must give both 'x_end' and 'y_end' for axial_shading."
1284
+ end
1285
+ colormap = alt_names(dict, 'colormap', 'color_map')
1286
+ extend_start = dict['extend_start']
1287
+ extend_end = dict['extend_end']
1288
+ private_axial_shading(x_start, y_start, x_end, y_end, colormap, extend_start, extend_end)
1289
+ end
1290
+
1291
+ @@keys_for_radial_shading = FigureMaker.make_name_lookup_hash(['extend_start', 'extend_end',
1292
+ 'x_start', 'y_start', 'radius_start', 'start_radius', 'start', 'start_circle',
1293
+ 'x_end', 'y_end', 'radius_start', 'end_radius', 'end', 'end_circle',
1294
+ 'colormap', 'color_map', 'x_hat', 'y_hat', 'xhat', 'yhat'])
1295
+ def radial_shading(dict)
1296
+ check_dict(dict, @@keys_for_radial_shading, 'radial_shading')
1297
+ start_circle = alt_names(dict, 'start_circle', 'start')
1298
+ end_circle = alt_names(dict, 'end_circle', 'end')
1299
+ colormap = alt_names(dict, 'colormap', 'color_map')
1300
+ x_hat = alt_names(dict, 'x_hat', 'xhat')
1301
+ x_hat = [1, 0] if x_hat == nil
1302
+ y_hat = alt_names(dict, 'y_hat', 'yhat')
1303
+ y_hat = [0, 1] if y_hat == nil
1304
+ origin = dict['origin']
1305
+ origin = [0, 0] if origin == nil
1306
+ extend_start = dict['extend_start']
1307
+ extend_end = dict['extend_end']
1308
+ private_radial_shading(start_circle[0], start_circle[1], start_circle[2],
1309
+ end_circle[0], end_circle[1], end_circle[2], colormap,
1310
+ x_hat[0], y_hat[0], x_hat[1], y_hat[1],
1311
+ extend_start, extend_end)
1312
+ end
1313
+
1314
+ @@keys_for_show_image = FigureMaker.make_name_lookup_hash([
1315
+ 'll', 'lr', 'ul', 'w', 'width', 'height', 'h',
1316
+ 'opacity_mask', 'stencil_mask',
1317
+ 'jpg', 'JPG', 'jpeg', 'JPEG', 'interpolate', 'data', 'value_mask',
1318
+ 'color_space', 'color_map', 'colormap'])
1319
+ def show_image(dict)
1320
+ internal_show_image(dict, false)
1321
+ end
1322
+
1323
+ @@keys_for_create_colormap = FigureMaker.make_name_lookup_hash(['length', 'points', 'Rs', 'Gs', 'Bs', 'Hs', 'Ls', 'Ss'])
1324
+ def create_colormap(dict)
1325
+ check_dict(dict, @@keys_for_create_colormap, 'create_colormap')
1326
+ length = dict['length']
1327
+ length = 256 if length == nil
1328
+ if !(length >= 2 && length <= 256)
1329
+ raise "Sorry: dictionary for create color map must have 'length' between 2 and 256"
1330
+ end
1331
+ points = dict['points']
1332
+ if points == nil
1333
+ raise "Sorry: dictionary for create color map must have 'points' set to a vector of increasing locations from 0.0 to 1.0"
1334
+ end
1335
+ c1s = dict['Rs']
1336
+ c2s = dict['Gs']
1337
+ c3s = dict['Bs']
1338
+ if c1s != nil && c2s != nil && c3s != nil
1339
+ rgb_flag = true
1340
+ else
1341
+ c1s = dict['Hs']
1342
+ c2s = dict['Ls']
1343
+ c3s = dict['Ss']
1344
+ if c1s == nil || c2s == nil || c3s == nil
1345
+ raise "Sorry: dictionary for create color map must have 'Rs', 'Gs', and 'Bs', or 'Hs', 'Ls', and 'Ss'."
1346
+ end
1347
+ rgb_flag = false
1348
+ end
1349
+ private_create_colormap(rgb_flag, length, points, c1s, c2s, c3s)
1350
+ end
1351
+
1352
+ def intense_colormap
1353
+ if @intense_colormap == nil
1354
+ @intense_colormap = create_colormap(
1355
+ 'points' => [0.0, 0.44, 0.50, 0.50, 0.56, 1.0],
1356
+ 'Hs' => [240, 240, 240, 0, 0, 0],
1357
+ 'Ls' => [0.5, 0.90, 0.99, 0.99, 0.90, 0.5],
1358
+ 'Ss' => [1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
1359
+ )
1360
+ end
1361
+ @intense_colormap
1362
+ end
1363
+
1364
+ def mellow_colormap
1365
+ if @mellow_colormap == nil
1366
+ @mellow_colormap = create_colormap(
1367
+ 'points' => [0.0, 0.44, 0.50, 0.50, 0.56, 1.0],
1368
+ 'Hs' => [240, 240, 240, 0, 0, 0],
1369
+ 'Ls' => [0.5, 0.90, 0.99, 0.99, 0.90, 0.5],
1370
+ 'Ss' => [0.5, 1.0, 1.0, 1.0, 1.0, 0.5]
1371
+ )
1372
+ end
1373
+ @mellow_colormap
1374
+ end
1375
+
1376
+ def rainbow_colormap
1377
+ if @rainbow_colormap == nil
1378
+ @rainbow_colormap = create_colormap(
1379
+ 'points' => [0.00, 0.27, 0.46, 0.73, 1.00], # distorted to reduce size of green
1380
+ # modified for debugging purposes...
1381
+ 'Hs' => (Dvector[0.0, 1.0, 2.0, 3.0, 4.0]*90.0 - 20.0)*(340.0/360.0) + 10.0,
1382
+ 'Ls' => [ 0.5, 0.5, 0.5, 0.5, 0.5],
1383
+ 'Ss' => [ 1.0, 1.0, 1.0, 1.0, 1.0]
1384
+ )
1385
+ end
1386
+ @rainbow_colormap
1387
+ end
1388
+
1389
+ @@keys_for_create_gradient_colormap = FigureMaker.make_name_lookup_hash([
1390
+ 'hue', 'starting_H', 'ending_H',
1391
+ 'lightness', 'starting_L', 'ending_L', 'saturation', 'starting_S', 'ending_S'])
1392
+ def create_gradient_colormap(dict)
1393
+ check_dict(dict, @@keys_for_create_gradient_colormap, 'create_gradient_colormap')
1394
+ hue = get_if_given_else_default(dict, 'hue', 0)
1395
+ starting_H = get_if_given_else_default(dict, 'starting_H', hue)
1396
+ ending_H = get_if_given_else_default(dict, 'ending_H', hue)
1397
+ lightness = get_if_given_else_default(dict, 'lightness', 0.5)
1398
+ starting_L = get_if_given_else_default(dict, 'starting_L', lightness)
1399
+ ending_L = get_if_given_else_default(dict, 'ending_L', lightness)
1400
+ saturation = get_if_given_else_default(dict, 'saturation', 0.5)
1401
+ starting_S = get_if_given_else_default(dict, 'starting_S', saturation)
1402
+ ending_S = get_if_given_else_default(dict, 'ending_S', saturation)
1403
+ create_colormap(
1404
+ 'points' => [0.00, 1.00],
1405
+ 'Hs' => [starting_H, ending_H],
1406
+ 'Ls' => [starting_L, ending_L],
1407
+ 'Ss' => [starting_S, ending_S]
1408
+ )
1409
+ end
1410
+
1411
+ @@keys_for_create_image_data = FigureMaker.make_name_lookup_hash([
1412
+ 'first_row', 'last_row', 'first_column', 'last_column',
1413
+ 'min_value', 'max_value', 'max_code', 'if_below_range', 'if_above_range', 'masking'])
1414
+ def create_image_data(data, dict)
1415
+ check_dict(dict, @@keys_for_create_image_data, 'create_image_data')
1416
+ first_row = dict['first_row']
1417
+ last_row = dict['last_row']
1418
+ first_column = dict['first_column']
1419
+ last_column = dict['last_column']
1420
+ min_value = dict['min_value']
1421
+ max_value = dict['max_value']
1422
+ max_code = dict['max_code']
1423
+ if_below_range = dict['if_below_range']
1424
+ if_above_range = dict['if_above_range']
1425
+ first_row = 0 if first_row == nil
1426
+ last_row = -1 if last_row == nil
1427
+ first_column = 0 if first_column == nil
1428
+ last_column = -1 if last_column == nil
1429
+ min_value = data.min if min_value == nil
1430
+ max_value = data.max if max_value == nil
1431
+ max_code = 255 if max_code == nil
1432
+ if_below_range = 0 if if_below_range == nil
1433
+ if_above_range = max_code if if_above_range == nil
1434
+ if dict['masking'] == true
1435
+ max_code = 254; if_below_range = if_above_range = 255
1436
+ end
1437
+ return private_create_image_data(data, first_row, last_row, first_column, last_column,
1438
+ min_value, max_value, max_code, if_below_range, if_above_range);
1439
+ end
1440
+
1441
+ @@keys_for_create_monochrome_image_data = FigureMaker.make_name_lookup_hash([
1442
+ 'first_row', 'last_row', 'first_column', 'last_column', 'boundary', 'reverse'])
1443
+ def create_monochrome_image_data(data, dict)
1444
+ check_dict(dict, @@keys_for_create_monochrome_image_data, 'create_monochrome_image_data')
1445
+ first_row = dict['first_row']
1446
+ last_row = dict['last_row']
1447
+ first_column = dict['first_column']
1448
+ last_column = dict['last_column']
1449
+ boundary = dict['boundary']
1450
+ reverse = dict['reverse']
1451
+ first_row = 0 if first_row == nil
1452
+ last_row = -1 if last_row == nil
1453
+ first_column = 0 if first_column == nil
1454
+ last_column = -1 if last_column == nil
1455
+ boundary = 0 if boundary == nil
1456
+ reverse = false if reverse == nil
1457
+ return private_create_monochrome_image_data(data, first_row, last_row, first_column, last_column,
1458
+ boundary, reverse);
1459
+ end
1460
+
1461
+ @@keys_for_show_marker = FigureMaker.make_name_lookup_hash([
1462
+ 'marker', 'x', 'y', 'at', 'point', 'Xs', 'Ys', 'xs', 'ys', 'mode', 'rendering_mode',
1463
+ 'angle', 'scale', 'font', 'string', 'text', 'color', 'fill_color', 'stroke_color', 'stroke_width',
1464
+ 'horizontal_scale', 'vertical_scale', 'italic_angle', 'ascent_angle', 'alignment', 'justification'])
1465
+ def show_marker(dict)
1466
+ check_dict(dict, @@keys_for_show_marker, 'show_marker')
1467
+ marker = dict['marker']
1468
+ x = y = nil
1469
+ at = alt_names(dict, 'at', 'point')
1470
+ if check_pair(at, 'at', 'show_text')
1471
+ x = at[0]; y = at[1];
1472
+ end
1473
+ x = dict['x'] if x == nil
1474
+ y = dict['y'] if y == nil
1475
+ xs = alt_names(dict, 'Xs', 'xs')
1476
+ ys = alt_names(dict, 'Ys', 'ys')
1477
+ if ((xs != nil && ys == nil) || (ys != nil && xs == nil))
1478
+ raise "Sorry: Must supply both xs and ys for show_marker"
1479
+ end
1480
+ if (xs != nil && ys != nil && xs.size != ys.size)
1481
+ raise "Sorry: Must equal length xs and ys for show_marker"
1482
+ end
1483
+ color = dict['color']
1484
+ if color == nil
1485
+ fill_color = get_if_given_else_use_default_dict(dict, 'fill_color', @marker_defaults)
1486
+ stroke_color = get_if_given_else_use_default_dict(dict, 'stroke_color', @marker_defaults)
1487
+ else
1488
+ fill_color = stroke_color = color
1489
+ end
1490
+ font = dict['font']
1491
+ mode = alt_names(dict, 'mode', 'rendering_mode')
1492
+ string = alt_names(dict, 'string', 'text')
1493
+ if (marker == nil && string == nil)
1494
+ raise "Sorry: Must give either 'marker' or 'string' for show_marker"
1495
+ end
1496
+ if (marker != nil && string != nil)
1497
+ raise "Sorry: Must give either 'marker' or 'string' for show_marker, but not both"
1498
+ end
1499
+ if (marker == nil)
1500
+ glyph = stroke_width = nil
1501
+ else
1502
+ if !(marker.kind_of?Array) && marker.size >= 2 && marker.size <= 3
1503
+ raise "Sorry: 'marker' for show_marker must be array of [font_number, char_code] or [font_number, char_code, rendering_mode]"
1504
+ end
1505
+ font = marker[0]
1506
+ glyph = marker[1]
1507
+ if marker.size == 3
1508
+ mode = STROKE if mode == nil
1509
+ stroke_width = marker[2]
1510
+ else
1511
+ mode = FILL if mode == nil
1512
+ stroke_width = nil
1513
+ end
1514
+ end
1515
+ mode = FILL if mode == nil
1516
+ font = Times_Roman if (font == nil && string != nil)
1517
+ if (font != nil && !(font.kind_of? Integer))
1518
+ raise "Sorry: 'font' for show_marker must be an integer font number (see FigureConstants list)"
1519
+ end
1520
+ stroke_width = get_if_given_else_default(dict, 'stroke_width', stroke_width)
1521
+ angle = get_if_given_else_use_default_dict(dict, 'angle', @marker_defaults)
1522
+ scale = get_if_given_else_use_default_dict(dict, 'scale', @marker_defaults)
1523
+ just = get_if_given_else_use_default_dict(dict, 'justification', @marker_defaults)
1524
+ align = get_if_given_else_use_default_dict(dict, 'alignment', @marker_defaults)
1525
+ h_scale = get_if_given_else_use_default_dict(dict, 'horizontal_scale', @marker_defaults)
1526
+ v_scale = get_if_given_else_use_default_dict(dict, 'vertical_scale', @marker_defaults)
1527
+ it_angle = get_if_given_else_use_default_dict(dict, 'italic_angle', @marker_defaults)
1528
+ ascent_angle = get_if_given_else_use_default_dict(dict, 'ascent_angle', @marker_defaults)
1529
+ glyph = 0 if glyph == nil
1530
+ int_args = glyph*100000 + font*1000 + mode*100 + align*10 + just
1531
+ # Ruby limits us to 15 args, so pack some small integers together
1532
+ private_show_marker(int_args, stroke_width, string, x, y, xs, ys,
1533
+ h_scale, v_scale, scale, it_angle, ascent_angle, angle, fill_color, stroke_color)
1534
+ end
1535
+
1536
+ def show_label(dict)
1537
+ at = alt_names(dict, 'at', 'point')
1538
+ if check_pair(at, 'at', 'show_text')
1539
+ xloc = at[0]
1540
+ yloc = at[1]
1541
+ else
1542
+ xloc = dict['x']
1543
+ yloc = dict['y']
1544
+ end
1545
+ if (xloc == nil || yloc == nil)
1546
+ raise "Sorry: Must supply 'at', 'point', or 'x' and 'y' for show_label"
1547
+ end
1548
+ return if !check_label_clip(xloc, yloc)
1549
+ show_text(dict)
1550
+ end
1551
+
1552
+ @@keys_for_show_text = FigureMaker.make_name_lookup_hash([
1553
+ 'text', 'side', 'loc', 'position', 'pos', 'x', 'y',
1554
+ 'shift', 'scale', 'color', 'angle', 'alignment', 'justification', 'at', 'point'])
1555
+ def show_text(dict)
1556
+ check_dict(dict, @@keys_for_show_text, 'show_text')
1557
+ text = dict['text']
1558
+ if text == nil
1559
+ raise "Sorry: Must supply 'text' entry in dictionary for show_text"
1560
+ end
1561
+ scale = get_if_given_else_default(dict, 'scale', 1)
1562
+ color = dict['color'] # color is [r,g,b] array. this adds \textcolor[rgb]{r,g,b}{...}
1563
+ if color != nil
1564
+ if !color.kind_of?Array
1565
+ raise "Sorry: 'color' must be array of [r,g,b] intensities for show_text (#{color})"
1566
+ end
1567
+ r = color[0]; g = color[1]; b = color[2];
1568
+ if (!(r.kind_of? Numeric) || !(g.kind_of? Numeric) || !(b.kind_of? Numeric) )
1569
+ raise "Sorry: 'color' must be array of [r,g,b] intensities for show_text"
1570
+ end
1571
+ text = sprintf("\\textcolor[rgb]{%0.2f,%0.2f,%0.2f}{%s}", r, g, b, text)
1572
+ end
1573
+ just = get_if_given_else_default(dict, 'justification', self.justification)
1574
+ align = get_if_given_else_default(dict, 'alignment', self.alignment)
1575
+ angle = get_if_given_else_default(dict, 'angle', 0)
1576
+ loc = alt_names(dict, 'loc', 'side')
1577
+ if (loc == nil)
1578
+ at = alt_names(dict, 'at', 'point')
1579
+ if check_pair(at, 'at', 'show_text')
1580
+ xloc = at[0]
1581
+ yloc = at[1]
1582
+ else
1583
+ xloc = dict['x']
1584
+ yloc = dict['y']
1585
+ end
1586
+ if (xloc == nil || yloc == nil)
1587
+ raise "Sorry: Must supply a location for show_text"
1588
+ end
1589
+ show_rotated_label(text, xloc, yloc, scale, angle, just, align)
1590
+ return
1591
+ end
1592
+ position = alt_names(dict, 'position', 'pos')
1593
+ position = 0.5 if position == nil
1594
+ shift = dict['shift']
1595
+ if loc == LEFT
1596
+ shift = self.text_shift_on_left if shift == nil
1597
+ elsif loc == RIGHT
1598
+ shift = self.text_shift_on_right if shift == nil
1599
+ elsif loc == TOP
1600
+ shift = self.text_shift_on_top if shift == nil
1601
+ elsif loc == BOTTOM
1602
+ shift = self.text_shift_on_bottom if shift == nil
1603
+ else
1604
+ if (loc == AT_X_ORIGIN)
1605
+ shift = self.text_shift_from_x_origin if shift == nil
1606
+ xloc = shift*self.char_height_dx
1607
+ yloc = convert_frame_to_figure_y(position)
1608
+ return
1609
+ elsif (loc == AT_Y_ORIGIN)
1610
+ shift = self.text_shift_from_y_origin if shift == nil
1611
+ yloc = shift*self.char_height_dy
1612
+ xloc = convert_frame_to_figure_x(position)
1613
+ else
1614
+ raise "Sorry: 'loc' must be LEFT, RIGHT, TOP, BOTTOM, AT_X_ORIGIN, or AT_Y_ORIGIN for show_text"
1615
+ end
1616
+ show_rotated_label(text, xloc, yloc, scale, angle, just, align)
1617
+ return
1618
+ end
1619
+ show_rotated_text(text, loc, shift, position, scale, angle, just, align)
1620
+ end
1621
+
1622
+
1623
+ def reset_eval_function
1624
+ @eval_command = nil
1625
+ end
1626
+
1627
+ def def_eval_function(&cmd)
1628
+ if cmd == nil
1629
+ raise "Sorry: must provide a command block for def_eval"
1630
+ end
1631
+ @eval_command = cmd
1632
+ end
1633
+
1634
+
1635
+
1636
+ def def_enter_page_function(&cmd)
1637
+ if cmd == nil
1638
+ raise "Sorry: must provide a command block for def_enter_page_function"
1639
+ end
1640
+ @enter_page_function = cmd
1641
+ end
1642
+
1643
+ def reset_enter_page_function
1644
+ @enter_page_function = nil
1645
+ end
1646
+
1647
+ def def_exit_page_function(&cmd)
1648
+ if cmd == nil
1649
+ raise "Sorry: must provide a command block for def_exit_page_function"
1650
+ end
1651
+ @exit_page_function = cmd
1652
+ end
1653
+
1654
+ def reset_exit_page_function
1655
+ @exit_page_function = nil
1656
+ end
1657
+
1658
+
1659
+
1660
+ def def_enter_show_plot_function(&cmd)
1661
+ if cmd == nil
1662
+ raise "Sorry: must provide a command block for def_enter_show_plot_function"
1663
+ end
1664
+ @enter_show_plot_function = cmd
1665
+ end
1666
+
1667
+ def reset_enter_show_plot_function
1668
+ @enter_show_plot_function = nil
1669
+ end
1670
+
1671
+ def def_exit_show_plot_function(&cmd)
1672
+ if cmd == nil
1673
+ raise "Sorry: must provide a command block for def_exit_show_plot_function"
1674
+ end
1675
+ @exit_show_plot_function = cmd
1676
+ end
1677
+
1678
+ def reset_exit_show_plot_function
1679
+ @exit_show_plot_function = nil
1680
+ end
1681
+
1682
+
1683
+
1684
+ def def_enter_subfigure_function(&cmd)
1685
+ if cmd == nil
1686
+ raise "Sorry: must provide a command block for def_enter_subfigure_function"
1687
+ end
1688
+ @enter_subfigure_function = cmd
1689
+ end
1690
+
1691
+ def reset_enter_subfigure_function
1692
+ @enter_subfigure_function = nil
1693
+ end
1694
+
1695
+ def def_exit_subfigure_function(&cmd)
1696
+ if cmd == nil
1697
+ raise "Sorry: must provide a command block for def_exit_subfigure_function"
1698
+ end
1699
+ @exit_subfigure_function = cmd
1700
+ end
1701
+
1702
+ def reset_exit_subfigure_function
1703
+ @exit_subfigure_function = nil
1704
+ end
1705
+
1706
+
1707
+ def def_enter_subplot_function(&cmd)
1708
+ if cmd == nil
1709
+ raise "Sorry: must provide a command block for def_enter_subplot_function"
1710
+ end
1711
+ @enter_subplot_function = cmd
1712
+ end
1713
+
1714
+ def reset_enter_subplot_function
1715
+ @enter_subplot_function = nil
1716
+ end
1717
+
1718
+ def def_exit_subplot_function(&cmd)
1719
+ if cmd == nil
1720
+ raise "Sorry: must provide a command block for def_exit_subplot_function"
1721
+ end
1722
+ @exit_subplot_function = cmd
1723
+ end
1724
+
1725
+ def reset_exit_subplot_function
1726
+ @exit_subplot_function = nil
1727
+ end
1728
+
1729
+
1730
+ def def_enter_context_function(&cmd)
1731
+ if cmd == nil
1732
+ raise "Sorry: must provide a command block for def_enter_context_function"
1733
+ end
1734
+ @enter_context_function = cmd
1735
+ end
1736
+
1737
+ def reset_enter_context_function
1738
+ @enter_subplot_context = nil
1739
+ end
1740
+
1741
+ def def_exit_context_function(&cmd)
1742
+ if cmd == nil
1743
+ raise "Sorry: must provide a command block for def_exit_context_function"
1744
+ end
1745
+ @exit_context_function = cmd
1746
+ end
1747
+
1748
+ def reset_exit_context_function
1749
+ @exit_subplot_context = nil
1750
+ end
1751
+
1752
+
1753
+ def eval_function(string)
1754
+ result = string
1755
+ if @eval_command == nil
1756
+ begin
1757
+ result = eval(string)
1758
+ rescue Exception => er
1759
+ report_error(er, nil)
1760
+ end
1761
+ else
1762
+ begin
1763
+ result = @eval_command.call(string)
1764
+ rescue Exception => er
1765
+ report_error(er, nil)
1766
+ end
1767
+ end
1768
+ return result
1769
+ end
1770
+
1771
+ def def_figure(name, &cmd)
1772
+ name = name.split(' ').join('_') # replace blanks by underscores since names must be okay for shell
1773
+ if cmd == nil
1774
+ raise "Sorry: must provide a command block for def_figure"
1775
+ end
1776
+ if (num = @figure_names.index(name)) == nil
1777
+ @figure_names << name
1778
+ num = @figure_names.index(name)
1779
+ @num_figures = @figure_names.length
1780
+ end
1781
+ @figure_commands[num] = cmd
1782
+ return cmd
1783
+ end
1784
+
1785
+ def figure_index(name)
1786
+ return @figure_names.index(name)
1787
+ end
1788
+
1789
+ def make_figure(num)
1790
+ make_pdf(num)
1791
+ end
1792
+
1793
+
1794
+ def make_preview_pdf(num) # old name
1795
+ make_pdf(num)
1796
+ end
1797
+
1798
+
1799
+ def make_pdf(num) # returns pdf name if successful, false if failed.
1800
+ num = get_num_for_pdf(num)
1801
+ result = start_making_pdf(num)
1802
+ return unless result
1803
+ return @figure_pdfs[num] = finish_making_pdf(@figure_names[num])
1804
+ end
1805
+
1806
+
1807
+ def require_pdf(num)
1808
+ num = get_num_for_pdf(num)
1809
+ make_pdf(num) if @figure_pdfs[num] == nil
1810
+ return @figure_pdfs[num]
1811
+ end
1812
+
1813
+
1814
+ def require_all(fignums=nil, report=false, always_make=false)
1815
+ fignums = Array.new(@figure_names.length) {|i| i} if fignums == nil
1816
+ nums = []
1817
+ fignums.each do |num|
1818
+ if always_make || (@figure_pdfs[num] == nil)
1819
+ result = start_making_pdf(num)
1820
+ if result
1821
+ report_number_and_name(num,@figure_names[num]) if report
1822
+ nums << num
1823
+ else
1824
+ puts 'ERROR: Failed to make pdf for ' + @figure_names[num]
1825
+ end
1826
+ end
1827
+ end
1828
+ finish_making_pdfs(nums,report)
1829
+ return true
1830
+ end
1831
+
1832
+
1833
+ def make_all(fignums=nil, report=false)
1834
+ require_all(fignums, report, true)
1835
+ end
1836
+
1837
+
1838
+ def make_portfolio_pdf(name, fignums=nil, report=false)
1839
+ make_portfolio(name,fignums,report)
1840
+ end
1841
+
1842
+
1843
+ def make_portfolio(name, fignums=nil, report=false)
1844
+ require_all(fignums,report)
1845
+ private_make_portfolio(name, fignums, @figure_names)
1846
+ finish_making_pdf(name)
1847
+ end
1848
+
1849
+
1850
+
1851
+
1852
+
1853
+
1854
+ private
1855
+
1856
+
1857
+ def report_number_and_name(num,name)
1858
+ if num < 10
1859
+ puts ' ' + num.to_s + ' ' + name
1860
+ elsif num < 100
1861
+ puts ' ' + num.to_s + ' ' + name
1862
+ else
1863
+ puts num.to_s + ' ' + name
1864
+ end
1865
+ end
1866
+
1867
+
1868
+ def get_num_for_pdf(num)
1869
+ num = @figure_names.index(num) unless num.kind_of?(Integer)
1870
+ ensure_safe_save_dir
1871
+ num = num.to_i
1872
+ num_figures = @figure_names.size
1873
+ num += num_figures if num < 0
1874
+ if ((num < 0) or (num >= num_figures))
1875
+ puts "Sorry: number must be between 0 and #{num_figures-1}"
1876
+ num = nil
1877
+ end
1878
+ return num
1879
+ end
1880
+
1881
+
1882
+ def start_making_pdf(num) # returns true if successful, false if failed.
1883
+ num = get_num_for_pdf(num)
1884
+ if num == nil
1885
+ result = false
1886
+ else
1887
+ name = @figure_names[num]
1888
+ begin
1889
+ result = create_figure_temp_files(num)
1890
+ name = get_save_filename(name)
1891
+ rescue Exception => er
1892
+ report_error(er, "ERROR: make failed for #{name}")
1893
+ result = false
1894
+ end
1895
+ end
1896
+ return result
1897
+ end
1898
+
1899
+
1900
+ def create_figure_temp_files(name) # returns true if successful, false if failed.
1901
+ if name.kind_of?(Integer)
1902
+ num = name
1903
+ name = @figure_names[num]
1904
+ else
1905
+ num = @figure_names.index(name)
1906
+ end
1907
+ return false if num == nil
1908
+ cmd = @figure_commands[num]
1909
+ return false unless cmd.kind_of?(Proc)
1910
+ begin
1911
+ reset_legend_info
1912
+ result = private_make(name, cmd)
1913
+ return result
1914
+ rescue Exception => er
1915
+ report_error(er, "ERROR while executing command: #{cmd}")
1916
+ end
1917
+ return false
1918
+ end
1919
+
1920
+
1921
+ def finish_making_pdfs(fignums,report)
1922
+ if @multithreads_okay_for_tioga # run separate threads for the pdflatex processing
1923
+ threads = []
1924
+ fignums.each do |num|
1925
+ threads << Thread.new(num) { |i| finish_1_pdf(i,report) }
1926
+ end
1927
+ threads.each {|thr| thr.join}
1928
+ else
1929
+ fignums.each {|num| finish_1_pdf(num,report)}
1930
+ end
1931
+ return true
1932
+ end
1933
+
1934
+
1935
+ def finish_1_pdf(num,report)
1936
+ pdfname = finish_making_pdf(@figure_names[num])
1937
+ if pdfname != false
1938
+ @figure_pdfs[num] = pdfname
1939
+ report_number_and_name(num,pdfname) if report
1940
+ else
1941
+ puts 'ERROR: pdflatex failed to make pdf for ' + @figure_names[num]
1942
+ end
1943
+ end
1944
+
1945
+
1946
+ def finish_making_pdf(name) # returns pdfname if succeeds, false if fails.
1947
+ pdflatex = FigureMaker.pdflatex
1948
+ quiet = @quiet_mode
1949
+ run_directory = @run_dir
1950
+ if (@save_dir == nil)
1951
+ syscmd = "#{pdflatex} -interaction nonstopmode #{name}.tex > pdflatex.log 2>&1"
1952
+ else
1953
+ syscmd = "cd #{@save_dir}; #{pdflatex} -interaction nonstopmode #{name}.tex > pdflatex.log 2>&1"
1954
+ end
1955
+ result = system(syscmd)
1956
+ if !result
1957
+ if (@save_dir == nil)
1958
+ logname = "pdflatex.log"
1959
+ else
1960
+ logname = "#{@save_dir}/pdflatex.log"
1961
+ end
1962
+ puts "ERROR: #{pdflatex} failed."
1963
+ file = File.open(logname)
1964
+ if file == nil
1965
+ puts "cannot open #{logname}"
1966
+ else
1967
+ reporting = false; linecount = 0
1968
+ file.each_line do |line|
1969
+ firstchar = line[0..0]
1970
+ comparison = (firstchar <=> '!')
1971
+ reporting = true if comparison == 0
1972
+ if reporting
1973
+ puts line
1974
+ linecount = linecount + 1
1975
+ break if linecount == @num_error_lines
1976
+ end
1977
+ end
1978
+ file.close
1979
+ end
1980
+ end
1981
+ if result
1982
+ pdfname = name
1983
+ logname = "pdflatex.log"
1984
+ files = %w(.tex .out .aux .log _figure.pdf _figure.txt).map do |suffix|
1985
+ pdfname + suffix
1986
+ end
1987
+ files << logname
1988
+ files << "color_names.aux"
1989
+ if @save_dir # prepend directory specification
1990
+ files.map! do |f|
1991
+ "#{@save_dir}/#{f}"
1992
+ end
1993
+ end
1994
+ begin
1995
+ if @autocleanup
1996
+ files.each do |f|
1997
+ begin
1998
+ File.delete(f)
1999
+ rescue
2000
+ end
2001
+ end
2002
+ end
2003
+ end
2004
+ pdfname = "#{@save_dir}/#{pdfname}" if @save_dir != nil
2005
+ pdfname = "#{run_directory}/#{pdfname}" if run_directory != nil && pdfname[0..0] != '/'
2006
+ pdfname += ".pdf"
2007
+ return pdfname
2008
+ end
2009
+ return false
2010
+ end
2011
+
2012
+ def internal_show_image(dict, is_mask)
2013
+ check_dict(dict, @@keys_for_show_image, 'show_image')
2014
+ ll = dict['ll']; lr = dict['lr']; ul = dict['ul']
2015
+ w = alt_names(dict, 'w', 'width')
2016
+ h = alt_names(dict, 'h', 'height')
2017
+ opacity_mask = alt_names(dict, 'opacity_mask', 'stencil_mask')
2018
+ if opacity_mask != nil
2019
+ mask_xo_num = internal_show_image(opacity_mask, true)
2020
+ else
2021
+ mask_xo_num = 0
2022
+ end
2023
+ filename = alt_names(dict, 'jpg', 'JPG')
2024
+ filename = dict['jpeg'] if filename == nil
2025
+ filename = dict['JPEG'] if filename == nil
2026
+ if filename != nil
2027
+ return private_show_jpg(filename, w, h, [ll[0], ll[1], lr[0], lr[1], ul[0], ul[1]], mask_xo_num)
2028
+ end
2029
+ interpolate = get_if_given_else_default(dict, 'interpolate', true)
2030
+ data = dict['data']
2031
+ value_mask = dict['value_mask']
2032
+ color_space = alt_names(dict, 'color_space', 'colormap')
2033
+ color_space = dict['color_map'] if color_space == nil
2034
+ if color_space == nil
2035
+ raise "Sorry: must specify 'color_space' for the image"
2036
+ end
2037
+ if color_space == 'MONO' || color_space == 'mono'
2038
+ raise "Sorry: monochrome image must not itself have a mask" unless mask_xo_num == 0
2039
+ reversed = get_if_given_else_default(dict, 'reversed', false)
2040
+ xo_obj = private_show_monochrome_image(ll[0], ll[1], lr[0], lr[1], ul[0], ul[1],
2041
+ interpolate, reversed, w, h, data, ((is_mask)? -1 : 0))
2042
+ return is_mask ? xo_obj : self
2043
+ end
2044
+ if color_space == 'GRAY' || color_space == 'gray' || color_space == 'GREY' || color_space == 'grey'
2045
+ if is_mask
2046
+ raise("Sorry: mask must not itself have a mask") unless mask_xo_num == 0
2047
+ mask_xo_num = -1
2048
+ end
2049
+ xo_obj = private_show_grayscale_image(ll[0], ll[1], lr[0], lr[1], ul[0], ul[1],
2050
+ interpolate, w, h, data, mask_xo_num)
2051
+ return is_mask ? xo_obj : self
2052
+ end
2053
+ if is_mask
2054
+ raise "Sorry: mask image must have 'color_space' set to 'gray' or 'mono'"
2055
+ end
2056
+ if color_space == 'RGB' || color_space == 'rgb'
2057
+ private_show_rgb_image(ll[0], ll[1], lr[0], lr[1], ul[0], ul[1], interpolate, w, h, data, mask_xo_num)
2058
+ return self
2059
+ end
2060
+ if color_space == 'CMYK' || color_space == 'cmyk'
2061
+ private_show_cmyk_image(ll[0], ll[1], lr[0], lr[1], ul[0], ul[1], interpolate, w, h, data, mask_xo_num)
2062
+ return self
2063
+ end
2064
+ if value_mask == nil
2065
+ value_mask_min = value_mask_max = 256
2066
+ elsif value_mask.kind_of?Integer
2067
+ value_mask_min = value_mask_max = value_mask
2068
+ else
2069
+ value_mask_min = value_mask[0];
2070
+ value_mask_max = value_mask[1];
2071
+ end
2072
+ private_show_image(
2073
+ ll[0], ll[1], lr[0], lr[1], ul[0], ul[1], interpolate,
2074
+ w, h, data, value_mask_min, value_mask_max, color_space[0], color_space[1], mask_xo_num)
2075
+ return self
2076
+ end
2077
+
2078
+ def report_error(er, msg)
2079
+ if msg != nil
2080
+ puts msg
2081
+ puts ""
2082
+ end
2083
+ puts " " + "#{er.message}" + " [version: " + FigureMaker.version + "]"
2084
+ line_count = 0
2085
+ show_count = 0
2086
+ past_callers_routines = false
2087
+ in_callers_routines = false
2088
+ er.backtrace.each do |line|
2089
+ if (line.include?('Tioga/FigMkr.rb')) || (line.include?('Tioga/tioga_ui.rb'))
2090
+ if in_callers_routines
2091
+ past_callers_routines = true
2092
+ in_callers_routines = false
2093
+ end
2094
+ else
2095
+ in_callers_routines = true
2096
+ end
2097
+ if (show_count < @num_error_lines) and in_callers_routines
2098
+ puts " " + line
2099
+ show_count = show_count + 1
2100
+ end
2101
+ line_count = line_count + 1
2102
+ end
2103
+ end
2104
+
2105
+
2106
+ def make_page(cmd) # the C implementation uses this to call the page function for the figure
2107
+ entry_function = @enter_page_function
2108
+ exit_function = @exit_page_function
2109
+ unless entry_function == nil
2110
+ begin
2111
+ entry_result = entry_function.call
2112
+ rescue Exception => er
2113
+ report_error(er, nil)
2114
+ end
2115
+ end
2116
+ result = do_cmd(cmd)
2117
+ unless result == false or exit_function == nil
2118
+ begin
2119
+ exit_result = exit_function.call
2120
+ rescue Exception => er
2121
+ report_error(er, nil)
2122
+ end
2123
+ end
2124
+ return result
2125
+ end
2126
+
2127
+
2128
+ def do_cmd(cmd) # the C implementation uses this to call Ruby commands
2129
+ begin
2130
+ cmd.call
2131
+ return true
2132
+ rescue Exception => er
2133
+ report_error(er, nil)
2134
+ end
2135
+ return false
2136
+ end
2137
+
2138
+ def check_dict(dict,names,str)
2139
+ dict.each_key do |name|
2140
+ if names[name] == nil
2141
+ raise "Sorry: Invalid dictionary key for #{str} (#{name})."
2142
+ end
2143
+ end
2144
+ end
2145
+
2146
+ def set_if_given(name, dict)
2147
+ val = dict[name]
2148
+ return if val == nil
2149
+ eval "self." + name + " = val"
2150
+ end
2151
+
2152
+ def alt_names(dict, name1, name2)
2153
+ val = dict[name1]
2154
+ val = dict[name2] if val == nil
2155
+ return val
2156
+ end
2157
+
2158
+ def get_if_given_else_use_default_dict(dict, name, default_dict)
2159
+ if dict != nil
2160
+ val = dict[name]
2161
+ return val if val != nil
2162
+ end
2163
+ val = default_dict[name]
2164
+ if val == nil
2165
+ raise "Sorry: failed to find value for '#{name}' in the defaults dictionary."
2166
+ end
2167
+ return val
2168
+ end
2169
+
2170
+ def get_if_given_else_default(dict, name, default)
2171
+ return default if dict == nil
2172
+ val = dict[name]
2173
+ return val if val != nil
2174
+ return default
2175
+ end
2176
+
2177
+ def complain_if_missing_numeric_arg(dict, name, alt_name, who_called)
2178
+ val = dict[name]
2179
+ val = dict[alt_name] if val == nil
2180
+ if val == nil
2181
+ raise "Sorry: Must supply '#{name}' in call to '#{who_called}'"
2182
+ end
2183
+ if !(val.kind_of?Numeric)
2184
+ raise "Sorry: Must supply numeric value for '#{name}' in call to '#{who_called}'"
2185
+ end
2186
+ return val
2187
+ end
2188
+
2189
+ def check_pair(ary, name, who_called)
2190
+ return false if ary == nil
2191
+ if !(ary.kind_of?(Array) || ary.kind_of?(Dvector)) and ary.size == 2
2192
+ raise "Sorry: '#{name}' must be array [x,y] for #{who_called}."
2193
+ end
2194
+ return true
2195
+ end
2196
+
2197
+ def get_dvec(dict, name, who_called)
2198
+ val = dict[name]
2199
+ if val == nil || !(val.kind_of? Dvector)
2200
+ raise "Sorry: '#{name}' must be a Dvector for '#{who_called}'"
2201
+ end
2202
+ return val
2203
+ end
2204
+
2205
+ # We make sure that save_dir exists and is a directory, creating it
2206
+ # if necessary.
2207
+ def ensure_safe_save_dir
2208
+ if @save_dir
2209
+ if File.exists?(@save_dir)
2210
+ raise "save_dir (#{@save_dir}) exists and is not a directory" unless File.directory?(@save_dir)
2211
+ else
2212
+ # we create the directory if possible
2213
+ if @create_save_dir
2214
+ Dir.mkdir @save_dir
2215
+ else
2216
+ raise "save_dir (#{@save_dir}) doesn't exist " +
2217
+ " and I was told not to create it"
2218
+ end
2219
+ end
2220
+ end
2221
+ end
2222
+
2223
+ end # class FigureMaker
2224
+ end # module Tioga