ctioga2 0.13.1 → 0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog +26 -0
  3. data/bin/ct2-make-movie +4 -1
  4. data/bin/ctioga2 +1 -1
  5. data/lib/ctioga2/commands/commands.rb +2 -0
  6. data/lib/ctioga2/commands/doc/doc.rb +1 -1
  7. data/lib/ctioga2/commands/doc/documentation-commands.rb +38 -0
  8. data/lib/ctioga2/commands/doc/html.rb +84 -0
  9. data/lib/ctioga2/commands/general-commands.rb +20 -1
  10. data/lib/ctioga2/commands/general-functions.rb +26 -0
  11. data/lib/ctioga2/commands/general-types.rb +1 -0
  12. data/lib/ctioga2/commands/instruction.rb +61 -0
  13. data/lib/ctioga2/commands/interpreter.rb +12 -2
  14. data/lib/ctioga2/data/datacolumn.rb +38 -0
  15. data/lib/ctioga2/data/dataset.rb +6 -5
  16. data/lib/ctioga2/data/filters.rb +12 -5
  17. data/lib/ctioga2/data/stack.rb +105 -22
  18. data/lib/ctioga2/graphics/elements.rb +1 -1
  19. data/lib/ctioga2/graphics/elements/curve2d.rb +1 -1
  20. data/lib/ctioga2/graphics/elements/element.rb +29 -10
  21. data/lib/ctioga2/graphics/elements/primitive.rb +26 -2
  22. data/lib/ctioga2/graphics/elements/subplot.rb +7 -1
  23. data/lib/ctioga2/graphics/generator.rb +1 -2
  24. data/lib/ctioga2/graphics/legends/area.rb +3 -0
  25. data/lib/ctioga2/graphics/root.rb +18 -2
  26. data/lib/ctioga2/graphics/styles/curve.rb +6 -0
  27. data/lib/ctioga2/graphics/styles/drawable.rb +4 -0
  28. data/lib/ctioga2/graphics/styles/factory.rb +4 -5
  29. data/lib/ctioga2/graphics/styles/plot-types.rb +22 -7
  30. data/lib/ctioga2/graphics/subplot-commands.rb +2 -4
  31. data/lib/ctioga2/graphics/types.rb +17 -0
  32. data/lib/ctioga2/graphics/types/boundaries.rb +10 -0
  33. data/lib/ctioga2/graphics/types/boxes.rb +18 -0
  34. data/lib/ctioga2/graphics/types/dimensions.rb +4 -0
  35. data/lib/ctioga2/graphics/types/grid.rb +98 -4
  36. data/lib/ctioga2/graphics/types/point.rb +9 -0
  37. data/lib/ctioga2/metabuilder/types/lists.rb +1 -1
  38. data/lib/ctioga2/metabuilder/types/styles.rb +5 -3
  39. data/lib/ctioga2/plotmaker.rb +28 -5
  40. data/lib/ctioga2/postprocess.rb +28 -0
  41. data/lib/ctioga2/ruby.rb +7 -0
  42. data/lib/ctioga2/utils.rb +45 -0
  43. data/lib/ctioga2/version.rb +2 -2
  44. metadata +4 -3
@@ -225,7 +225,7 @@ module CTioga2
225
225
  # _x_, _xmin_, _xmax_, _y_, _ymin_, _ymax_, _y1_, _y1min_, _y1max_,
226
226
  # _z_, _zmin_, _zmax_, _y2_, _y2min_, _y2max_, _y3_, _y3min_, _y3max_
227
227
  #
228
- def select!(&block)
228
+ def select!(evaluator)
229
229
  target = []
230
230
  @x.size.times do |i|
231
231
  args = @x.values_at(i, true)
@@ -236,7 +236,7 @@ module CTioga2
236
236
  args.concat(yvect.values_at(i, true))
237
237
  end
238
238
  end
239
- if block.call(*args)
239
+ if evaluator.compute_unsafe(*args)
240
240
  target << i
241
241
  end
242
242
  end
@@ -260,8 +260,8 @@ module CTioga2
260
260
  i += 1
261
261
  end
262
262
  end
263
- block = eval("proc do |#{names.join(',')}|\n#{formula}\nend")
264
- select!(&block)
263
+ evaluator = Ruby.make_evaluator(formula, names)
264
+ select!(evaluator)
265
265
  end
266
266
 
267
267
  # \todo a dup !
@@ -755,13 +755,14 @@ module CTioga2
755
755
 
756
756
  end
757
757
 
758
- protected
759
758
 
760
759
  # Returns all DataColumn objects held by this Dataset
761
760
  def all_columns
762
761
  return [@x, *@ys]
763
762
  end
764
763
 
764
+ protected
765
+
765
766
  # Returns all Dvectors of the columns one by one.
766
767
  def all_vectors
767
768
  return all_columns.map {|x| x.vectors}.flatten(1)
@@ -42,13 +42,16 @@ dataset pushed unto the data stack: they can be viewed as filters.",
42
42
  Sorts the last dataset pushed unto the stack according to X values. Can be
43
43
  used as a filter.
44
44
 
45
+ This command sorts in-place.
46
+
45
47
  See also {command: sort}.
46
48
  EOH
47
49
 
48
50
  SortFilter =
49
51
  Cmd.new("sort", nil, "--sort",
50
52
  [], {}) do |plotmaker, opts|
51
- plotmaker.data_stack.add_to_dataset_hook('sort-last()')
53
+ plotmaker.data_stack.
54
+ add_to_dataset_hook(Commands::Instruction.new('sort-last', [], {}))
52
55
  end
53
56
 
54
57
  SortFilter.describe("Systematically sort subsequent datasets",
@@ -80,7 +83,8 @@ EOH
80
83
  [CmdArg.new('integer')], {}) do |plotmaker, number, opts|
81
84
  ## @todo There should be a way to add commands in a type-safe
82
85
  ## way, without having to convert to string first.
83
- plotmaker.data_stack.add_to_dataset_hook("trim-last(#{number})")
86
+ plotmaker.data_stack.
87
+ add_to_dataset_hook(Commands::Instruction.new('trim-last', [number], {}))
84
88
  end
85
89
 
86
90
  TrimFilter.describe("Systematically trim subsequent datasets",
@@ -113,7 +117,8 @@ EOH
113
117
  CherryPickFilter =
114
118
  Cmd.new("cherry-pick", nil, "--cherry-pick",
115
119
  [CmdArg.new('text')], {}) do |plotmaker, formula|
116
- plotmaker.data_stack.add_to_dataset_hook("cherry-pick-last '#{formula}'")
120
+ plotmaker.data_stack.
121
+ add_to_dataset_hook(Commands::Instruction.new('cherry-pick-last', [formula], {}))
117
122
  end
118
123
 
119
124
  CherryPickFilter.describe("Systematicallly remove data for which the formula is false",
@@ -162,7 +167,8 @@ EOH
162
167
  AverageDupFilter =
163
168
  Cmd.new("avg-dup", nil, "--avg-dup",
164
169
  [], {}) do |plotmaker, formula|
165
- plotmaker.data_stack.add_to_dataset_hook("avg-dup-last()")
170
+ plotmaker.data_stack.
171
+ add_to_dataset_hook(Commands::Instruction.new('avg-dup-last', [], {}))
166
172
  end
167
173
 
168
174
  AverageDupFilter.describe("Systematicallly average successive elements with identical X values",
@@ -189,7 +195,8 @@ EOH
189
195
  SmoothFilter =
190
196
  Cmd.new("smooth", nil, "--smooth",
191
197
  [CmdArg.new('integer')], {}) do |plotmaker, nb|
192
- plotmaker.data_stack.add_to_dataset_hook("smooth-last #{nb}")
198
+ plotmaker.data_stack.
199
+ add_to_dataset_hook(Commands::Instruction.new('smooth-last', [nb], {}))
193
200
  end
194
201
 
195
202
  SmoothFilter.describe("Systematicallly smooth data",
@@ -52,13 +52,7 @@ module CTioga2
52
52
  # A hook executed every time a dataset is pushed unto the stack
53
53
  # using #add_dataset.
54
54
  #
55
- # \todo this string is parsed for each call to
56
- # #add_dataset. Perhaps it would be good to provide a way to
57
- # record a Command call, without parsing it from scratch ???
58
- #
59
- # Although, with variables, that could be interesting to reparse
60
- # everytime, since any change in the variables would be taken
61
- # into account.
55
+ # This is a list of Instruction
62
56
  attr_accessor :dataset_hook
63
57
 
64
58
  # Creates a new DataStack object.
@@ -70,6 +64,8 @@ module CTioga2
70
64
  # Defaults to the 'text' backend
71
65
  @backend_factory = Data::Backends::BackendFactory.new('text')
72
66
 
67
+ @dataset_hook = []
68
+
73
69
  # Probably a bit out of place...
74
70
  csv =
75
71
  Cmd.new('csv', nil, '--csv', []) do |plotmaker|
@@ -88,7 +84,7 @@ EOH
88
84
  # Performs expansion on the given _set_ with the current
89
85
  # backend, retrieves corresponding Dataset objects, pushes them
90
86
  # onto the stack and returns them.
91
- def get_datasets(set, options = {})
87
+ def get_datasets(set, options = {}, add = true)
92
88
  backend = @backend_factory.specified_backend(options)
93
89
  sets = backend.expand_sets(set)
94
90
  datasets = []
@@ -100,7 +96,9 @@ EOH
100
96
  debug { "#{e.backtrace.join("\n")}" }
101
97
  end
102
98
  end
103
- add_datasets(datasets, options)
99
+ if add
100
+ add_datasets(datasets, options)
101
+ end
104
102
  return datasets
105
103
  end
106
104
 
@@ -152,7 +150,16 @@ EOH
152
150
  end
153
151
  end
154
152
  else
155
- if @named_datasets.key? spec
153
+ if spec =~ /^\s*#(.*)/
154
+ # graph idea -> get dataset from the plot element
155
+ eln = $1
156
+ obj = Elements::TiogaElement.find_object(eln)
157
+ if !obj.respond_to(:dataset)
158
+ raise "Object '##{eln}' does not name a plot"
159
+ end
160
+ ds = obj.dataset
161
+ index = @stack.index(ds)
162
+ elsif @named_datasets.key? spec
156
163
  name = spec
157
164
  ds = @named_datasets[spec]
158
165
  i = 0
@@ -189,13 +196,14 @@ EOH
189
196
  def store_dataset(dataset, ignore_hooks = false)
190
197
  @stack << dataset
191
198
  if @dataset_hook && (! ignore_hooks)
192
- # \todo error handling
193
- begin
194
- PlotMaker.plotmaker.interpreter.run_commands(@dataset_hook)
195
- rescue Exception => e
196
- error { "There was a problem running the dataset hook '#{@dataset_hook}', disabling it" }
197
- @dataset_hook = nil
198
- info { "-> '#{format_exception e}'" }
199
+ for ins in @dataset_hook
200
+ begin
201
+ ins.run(PlotMaker.plotmaker)
202
+ rescue Exception => e
203
+ error { "There was a problem running the dataset hook '#{ins.to_s}', disabling it" }
204
+ @dataset_hook.delete(ins)
205
+ info { "-> '#{format_exception e}'" }
206
+ end
199
207
  end
200
208
  end
201
209
  end
@@ -222,9 +230,9 @@ EOH
222
230
  # Appends a set of commands to the dataset hook
223
231
  def add_to_dataset_hook(commands)
224
232
  if @dataset_hook
225
- @dataset_hook << "\n#{commands}"
233
+ @dataset_hook += [commands].flatten
226
234
  else
227
- @dataset_hook = commands
235
+ @dataset_hook = [commands].flatten
228
236
  end
229
237
  end
230
238
 
@@ -314,13 +322,34 @@ EOH
314
322
  "Commands for manipulation of the data stack",
315
323
  100)
316
324
 
317
- LoadDatasetOptions = {
318
- 'name' => CmdArg.new('text'),
325
+ AppendDatasetOptions = {
319
326
  'as' => CmdArg.new('text'),
320
327
  'where' => CmdArg.new('text'),
321
328
  'ignore_hooks' => CmdArg.new('boolean')
322
329
  }
323
330
 
331
+ AppendDataCommand =
332
+ Cmd.new("append", nil, "--append",
333
+ [ CmdArg.new('dataset'), ],
334
+ AppendDatasetOptions) do |plotmaker, set, opts|
335
+ datasets = plotmaker.data_stack.get_datasets(set, opts, false)
336
+ # Now, we append them to the last dataset
337
+ plotmaker.data_stack.concatenate_datasets(datasets)
338
+ end
339
+
340
+ AppendDataCommand.describe("Appends the datasets to the last in the stack",
341
+ <<EOH, DataStackGroup)
342
+ Use the current backend to load the given dataset(s) and append to the
343
+ last dataset on the stack (without creating a new dataset). Roughly
344
+ the equivalent of first running {command: load} and then
345
+ {command: join-datasets}.
346
+ EOH
347
+
348
+ LoadDatasetOptions = AppendDatasetOptions.dup.merge(
349
+ {
350
+ 'name' => CmdArg.new('text')
351
+ })
352
+
324
353
  LoadDataCommand =
325
354
  Cmd.new("load", '-L', "--load",
326
355
  [ CmdArg.new('dataset'), ],
@@ -338,9 +367,10 @@ similar construct), each dataset gets named with %d replace with the
338
367
  number of the dataset within the expansion (starting at 0). This name
339
368
  can be used to further use the dataset without remembering its
340
369
  number. See the type {type: stored-dataset} for more information.
341
-
342
370
  EOH
343
371
 
372
+
373
+
344
374
  ContourOptions = LoadDatasetOptions.dup.update({
345
375
  'which' => CmdArg.new('stored-dataset'),
346
376
  })
@@ -439,6 +469,57 @@ EOH
439
469
  ApplyLastCommand.describe("Applies a formula to the last dataset",
440
470
  <<EOH, DataStackGroup)
441
471
  Applies a formula to the last dataset (or the named one)
472
+ EOH
473
+
474
+ BinLastCommand =
475
+ Cmd.new("bin", nil, "--bin",
476
+ [],
477
+ {
478
+ 'number' => CmdArg.new('integer'),
479
+ 'column' => CmdArg.new('integer'),
480
+ 'delta' => CmdArg.new('float'),
481
+ 'min' => CmdArg.new('float'),
482
+ 'max' => CmdArg.new('float'),
483
+ 'normalize' => CmdArg.new('boolean'),
484
+ 'which' => CmdArg.new('stored-dataset'),
485
+ 'name' => CmdArg.new('text')
486
+ }) do |plotmaker, opts|
487
+ stack = plotmaker.data_stack
488
+ ds = plotmaker.data_stack.specified_dataset(opts)
489
+
490
+ cn = opts['column'] || 1
491
+ col = ds.all_columns[cn]
492
+
493
+
494
+ if opts.key? 'number'
495
+ min = opts['min'] || col.min
496
+ max = opts['max'] || col.max
497
+ number = opts['number']
498
+ elsif opts.key? 'delta'
499
+ delta = opts['delta']
500
+ if opts.key? 'min'
501
+ min = opts['min']
502
+ max = min+((col.max-min)/delta).ceil*delta
503
+ elsif opts.key? 'max'
504
+ max = opts['max']
505
+ min = max-((max-col.min)/delta).floor*delta
506
+ else
507
+ min = (col.min/delta).floor * delta
508
+ max = (col.max/delta).ceil * delta
509
+ end
510
+ number = ((max-min)/delta).to_i
511
+ else
512
+ raise "Must specify either the option 'number' or the option 'delta'"
513
+ end
514
+
515
+ newds = Dataset.new("bin", col.bin(min, max, number, opts['normalize']))
516
+ plotmaker.data_stack.add_datasets([newds], opts)
517
+ end
518
+
519
+ BinLastCommand.describe("Bins the last dataset",
520
+ <<EOH, DataStackGroup)
521
+ This command bins the contents of the Y column of the last dataset on the
522
+ stack, and pushes the results as a new dataset.
442
523
  EOH
443
524
 
444
525
  ShowStackCommand =
@@ -537,6 +618,7 @@ EOH
537
618
  SetDatasetHookCommand =
538
619
  Cmd.new("dataset-hook", nil, "--dataset-hook",
539
620
  [CmdArg.new('commands')], {}) do |plotmaker, commands, opts|
621
+ raise 'This command is disabled as of now'
540
622
  plotmaker.data_stack.dataset_hook = commands
541
623
  end
542
624
 
@@ -562,6 +644,7 @@ EOH
562
644
  AddDatasetHookCommand =
563
645
  Cmd.new("dataset-hook-add", nil, "--dataset-hook-add",
564
646
  [CmdArg.new('commands')], {}) do |plotmaker, commands, opts|
647
+ raise 'This command is disabled as of now'
565
648
  plotmaker.data_stack.add_to_dataset_hook(commands)
566
649
  end
567
650
 
@@ -71,7 +71,7 @@ EOH
71
71
  Sets the range of the #{x.to_s.upcase} coordinates.
72
72
 
73
73
  *Important note:* when the axis is in log range (using
74
- {command: #{x.to_s.upcase}log}), the numbers you give are not the or
74
+ {command: #{x.to_s}log}), the numbers you give are not the or
75
75
  {command: ylog} values, but their @log10@, so that to
76
76
  display #{x.to_s.upcase} values from @1e-2@ to @1e3@, use:
77
77
 
@@ -205,7 +205,7 @@ module CTioga2
205
205
 
206
206
  ## Actually draws the curve
207
207
  def real_do(t)
208
- debug { "Plotting curve #{inspect}" }
208
+ debug { "Plotting curve #{to_yaml}" }
209
209
  t.context do
210
210
  ## \todo allow customization of the order of drawing,
211
211
  ## using a simple user-specificable array of path,
@@ -167,6 +167,7 @@ module CTioga2
167
167
 
168
168
  def self.register_object(obj)
169
169
  @registered_objects ||= {}
170
+ @objects_by_class ||= {}
170
171
  if i = obj.object_id
171
172
  if @registered_objects.key? i
172
173
  warn { "Second object with ID #{i}, ignoring the name" }
@@ -174,6 +175,10 @@ module CTioga2
174
175
  @registered_objects[i] = obj
175
176
  end
176
177
  end
178
+ for cls in (obj.object_classes || [])
179
+ @objects_by_class[cls] ||= []
180
+ @objects_by_class[cls] << obj
181
+ end
177
182
  end
178
183
 
179
184
  def self.find_object(obj_id)
@@ -184,12 +189,27 @@ module CTioga2
184
189
  raise "No such object: '#{obj_id}'"
185
190
  end
186
191
  end
187
-
192
+
193
+ def self.find_objects(id_list)
194
+ # First split on commas:
195
+ ids = id_list.split(/\s*,\s*/)
196
+ objs = []
197
+ @objects_by_class ||= {}
198
+ for oi in ids
199
+ if oi =~ /^\.(.*)/
200
+ objs += (@objects_by_class[$1] || [])
201
+ elsif oi =~ /^\#?(.*)/
202
+ objs << self.find_object($1)
203
+ end
204
+ end
205
+ return objs
206
+ end
207
+
188
208
 
189
209
  def setup_style(obj_parent, opts)
190
210
  @cached_options = opts
191
211
  @object_id = opts["id"] || nil
192
- @object_classes = opts["class"] || []
212
+ @object_classes = opts.key?("class") ? [opts["class"]].flatten : []
193
213
  @object_parent = obj_parent
194
214
 
195
215
  TiogaElement.register_object(self)
@@ -232,10 +252,10 @@ module CTioga2
232
252
  # redefine _do_ too if you need another debugging output.
233
253
  def do(f)
234
254
  if @hidden
235
- debug { "not plotting hidden #{self.inspect}" }
255
+ debug { "not plotting hidden #{self.to_yaml}" }
236
256
  return
237
257
  else
238
- debug { "plotting #{self.inspect}" }
258
+ debug { "plotting #{self.to_yaml}" }
239
259
  end
240
260
  @gp_cache = {}
241
261
  real_do(f)
@@ -313,12 +333,11 @@ appropriate command).
313
333
  EOD
314
334
 
315
335
  ObjectsType =
316
- CmdType.new('objects', {:type => :array,
317
- :subtype => {:type => :function_based,
318
- :class => Elements::TiogaElement,
319
- :func_name => :find_object}
320
- }, <<EOD)
321
- A list of comma-separated {type: object}s.
336
+ CmdType.new('objects', {:type => :function_based,
337
+ :class => Elements::TiogaElement,
338
+ :func_name => :find_objects}, <<EOD)
339
+ A list of comma-separated {type: object}s, or a class specification
340
+ starting with a .
322
341
  EOD
323
342
 
324
343
  end
@@ -161,6 +161,29 @@ module CTioga2
161
161
  return primitive_class
162
162
  end
163
163
 
164
+
165
+
166
+ primitive("legend-pictogram", "legend-pictogram",
167
+ ["point", "object"], {
168
+ 'width' => 'dimension'
169
+ },
170
+ "Draws the legend pictogram for the given curve"
171
+ ) do |t, point, obj, opts|
172
+ al = Types::AlignedPoint::from_point(point)
173
+ cs = obj.curve_style
174
+
175
+ dx = opts['width'] || Types::Dimension.new(:dy, 2.5)
176
+ # dy = opts['height'] || Types::Dimension.new(:dy, 1)
177
+ dy = dx*0.4 # I'm not sure it really matters
178
+ pbb = Types::PointBasedBox.new(al, dx, dy)
179
+ pbb.within_frames(t) do
180
+ cs.draw_legend_pictogram(t)
181
+ end
182
+ end
183
+
184
+
185
+
186
+
164
187
  # This creates a primitive base on a style object, given a
165
188
  # _style_class_, the base _style_name_ for the underlying
166
189
  # styling system, options to remove and options to add.
@@ -265,8 +288,9 @@ EOD
265
288
  [ 'point', 'dimension' ],
266
289
  Styles::OrientedLineStyle,
267
290
  'oriented-line'
268
- ) do |t, org, dim, style, options|
269
- style.draw_oriented_arrow(t, *org.to_figure_xy(t), dim)
291
+ ) do |t, org, dim, style, options|
292
+
293
+ style.draw_oriented_arrow(t, *(org.to_figure_xy(t) + [dim]))
270
294
  end
271
295
 
272
296