tioga 1.4

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