diagrammatron 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f4b7974989ef344d2eaaae0c54f82567b8ed9edd9c7fb5d2dfc3e405895efa7
4
- data.tar.gz: efdd52da27d648f9da7fed309ba668eae6237eeddd51a703c47c46c45c394435
3
+ metadata.gz: 8c51dc3c8502bd1626d2a78bccd7e772e60fb25a26e6327879f42144849db598
4
+ data.tar.gz: 78548d0ad0af86031afecb81bf4fedca42da58420d0de3a0d5063f07cb68538e
5
5
  SHA512:
6
- metadata.gz: e235a20fb0fb4013e4f0006731743830f84500519d2ab4262b04caa9bae0962bd7da5127cd30e44c9e6bf891b735a02f10fe193149047fa6b54ec0bd6185f2e5
7
- data.tar.gz: e6c4c3db889c83d23390ee874379215747fb4336fbb3f1f2f543226ab72788fe948a6394e500ea89ed4d4a013126a324b10fa5b49bf73be9233cd94e9781a024
6
+ metadata.gz: 03d48ecd6c7e586007dcb470655fff6814e21d32f1921dcb828b35602bea208e48f58d9ba2ea5cdf5f16fb8f633a242a9d43a599c21fbf61d5ffaa631282c827
7
+ data.tar.gz: 5ed263d065a49d69a031fa140ac0159d58301da26f4b51807573e5b8eefbeca6238301f6953cca5c8b44423a83fec18903a7e9d960a5b3fcf1d4702f1b671233
@@ -6,54 +6,32 @@
6
6
 
7
7
  require_relative '../lib/common'
8
8
  require 'optparse'
9
- require 'yaml'
10
9
  require 'set'
11
- require 'pathname'
12
10
 
13
11
 
14
12
  def work_copy(src, quiet)
15
13
  work = { edges: {}, nodes: [] }
16
- # Expected nodes, edges. Other pass-through.
17
14
  label2idx = {}
18
15
  errors = false
19
16
  edge_nodes = Set.new
20
- edges = src.fetch('edges', [])
21
- unedges = []
17
+ edges = src['edges']
22
18
  selfedges = []
23
19
  edges.each_index do |k|
24
20
  edge = edges[k]
25
- labels = edge.fetch('between', [])
26
- if labels.nil? || labels.empty?
27
- unedges.push(k)
28
- elsif labels.size == 2
29
- if labels.first == labels.last
30
- selfedges.push(k)
31
- else
32
- edge_nodes.add labels.first
33
- edge_nodes.add labels.last
34
- work[:edges][k] = { idx: k, between: [ labels[0], labels[1] ] }
35
- end
21
+ labels = edge['between']
22
+ if labels.first == labels.last
23
+ selfedges.push(k)
36
24
  else
37
- aargh "Edge #{k + 1} does not have two labels in 'between'"
38
- errors = true
25
+ edge_nodes.add labels.first
26
+ edge_nodes.add labels.last
27
+ work[:edges][k] = { idx: k, between: [ labels[0], labels[1] ] }
39
28
  end
40
29
  end
41
30
  labeled_nodes = Set.new
42
- unlabeled = []
43
- nodes = src.fetch('nodes', [])
31
+ nodes = src['nodes']
44
32
  subsets = {}
45
33
  nodes.each_index do |k|
46
34
  node = nodes[k]
47
- unless node.key? 'sid'
48
- aargh "Node without sid: #{node.fetch('label', k + 1)}"
49
- errors = true
50
- next
51
- end
52
- unless node.key?('xo') && node.key?('yo')
53
- aargh "Node without xo or yo: #{node.fetch('label', k + 1)}"
54
- errors = true
55
- next
56
- end
57
35
  sid = node['sid']
58
36
  subsets[sid] = [] unless subsets.key? sid
59
37
  subsets[sid].push(k)
@@ -63,10 +41,6 @@ def work_copy(src, quiet)
63
41
  xo: node['xo'] * 2, # Make room for edge coordinates.
64
42
  yo: node['yo'] * 2
65
43
  })
66
- unless node.key? 'label'
67
- unlabeled.push k
68
- next
69
- end
70
44
  label = node['label']
71
45
  if label2idx.key?(label) && edge_nodes.member?(label)
72
46
  aargh "Edge-referred label used twice: #{label}"
@@ -80,12 +54,14 @@ def work_copy(src, quiet)
80
54
  aargh "Edges refer to missing node labels: #{missing.to_a.join(' ')}"
81
55
  errors = true
82
56
  end
57
+ unless selfedges.empty?
58
+ info = selfedges.map { |k| "#{edges[k]['between'].first} (#{k})" }
59
+ aargh "Edges from node to itself: #{info.join(' ')}"
60
+ errors = true
61
+ end
83
62
  return nil if errors
84
63
  unused = labeled_nodes - edge_nodes
85
- [ [ unused.to_a, 'unconnected labeled nodes' ],
86
- [ unlabeled, 'unlabeled nodes' ],
87
- [ selfedges, 'edges from node to itself' ],
88
- [ unedges, 'edges without end-points' ]
64
+ [ [ unused.to_a, 'unconnected labeled nodes' ]
89
65
  ].each do |x|
90
66
  next if quiet || x.first.empty?
91
67
  aargh("Note, #{x.last}: #{x.first.join(' ')}")
@@ -125,7 +101,7 @@ Segment = Struct.new(:vertical, :cc, :range, :edge_index, :at_node, :segment_ind
125
101
  node_subset.each do |n|
126
102
  node = work[:nodes][n]
127
103
  next unless cc == node[ck]
128
- (0..1).each do |k|
104
+ 2.times do |k|
129
105
  return true if range[i0] < node[rk] && node[rk] < range[i1]
130
106
  next if at_node[k]
131
107
  return true if range[k] == node[rk]
@@ -631,7 +607,7 @@ def place_edges(work)
631
607
  all.push nil
632
608
  all.concat(layered_order(gright, glur).reverse)
633
609
  # Give each rational offset using layer index + 1 and layer count + 2.
634
- denominator = 2 + all.count { |x| x.nil? }
610
+ denominator = 2 + all.count(&:nil?)
635
611
  layer = 1
636
612
  all.each do |sg|
637
613
  if sg.nil?
@@ -737,6 +713,8 @@ end
737
713
  def main
738
714
  input = nil
739
715
  output = nil
716
+ input_schema = 'edges'
717
+ output_schema = 'place'
740
718
  quiet = false
741
719
  parser = OptionParser.new do |opts|
742
720
  opts.summary_indent = ' '
@@ -757,13 +735,21 @@ def main
757
735
  $stdout.puts %(#{opts}
758
736
 
759
737
  Input YAML file is expected to be the output of diagrammatron-nodes.
738
+
739
+ Input YAML file schema is returned by:
740
+ diagrammatron-schema #{input_schema}
741
+
742
+ Output YAML file schema is returned by:
743
+ diagrammatron-schema #{output_schema}
744
+
745
+ There can be other fields present but they are ignored and retained.
760
746
  )
761
747
  exit 0
762
748
  end
763
749
  end
764
750
  parser.parse! ARGV
765
751
 
766
- doc = load_source(input)
752
+ doc = load_verified(input, input_schema)
767
753
  return 2 if doc.nil?
768
754
 
769
755
  begin
@@ -775,7 +761,7 @@ Input YAML file is expected to be the output of diagrammatron-nodes.
775
761
 
776
762
  place_edges(work)
777
763
  prepare_output(doc, work)
778
- dump_result(output, YAML.dump(doc, line_width: 1_000_000), 4)
764
+ save_verified(output, doc, 4, output_schema)
779
765
  end
780
766
 
781
767
  exit(main) if (defined? $unit_test).nil?
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # Copyright © 2021, 2022 Ismo Kärkkäinen
4
+ # Copyright © 2021-2023 Ismo Kärkkäinen
5
5
  # Licensed under Universal Permissive License. See LICENSE.txt.
6
6
 
7
7
  require_relative '../lib/common'
8
8
  require 'optparse'
9
- require 'pathname'
10
9
 
11
10
 
12
11
  def template(name = nil)
@@ -28,8 +27,8 @@ def main
28
27
  end
29
28
  opts.on('-h', '--help', 'Print this help and exit.') do
30
29
  $stdout.puts %(#{opts}
31
- This is used to easily access template files included in the gem without need
32
- to clone the original repository.
30
+ This is used to easily access template files included in the gem without
31
+ the need to clone the original repository.
33
32
 
34
33
  Without arguments, lists all templates, template root, and content files
35
34
  included in the gem.
@@ -41,7 +40,7 @@ Given a name of a included file, saves it to --output.
41
40
  end
42
41
  parser.parse! ARGV
43
42
 
44
- if ARGV.size.zero?
43
+ if ARGV.empty?
45
44
  # List all files in templates directory.
46
45
  Dir.entries(template).sort.each do |name|
47
46
  next if name.start_with? '.'
@@ -6,9 +6,7 @@
6
6
 
7
7
  require_relative '../lib/common'
8
8
  require 'optparse'
9
- require 'yaml'
10
9
  require 'set'
11
- require 'pathname'
12
10
 
13
11
 
14
12
  def vertical(work)
@@ -139,7 +137,7 @@ def shifts(count)
139
137
  side = (count / 2.0).ceil
140
138
  side = ((side / 4) + ((side % 4).positive? ? 1 : 0)) * 4
141
139
  xs = Array.new(side) { |index| Integer((index - side / 2).round) }
142
- (0..3).each { |k| xs.push(-xs[k]) }
140
+ 4.times { |k| xs.push(-xs[k]) }
143
141
  ys = Array.new(xs)
144
142
  xs.rotate!(side / 2 - 1)
145
143
  ys.rotate!(side - 1) # First half-way to offset with xs, then like xs.
@@ -281,36 +279,23 @@ def work_copy(src, quiet)
281
279
  errors = false
282
280
  edge_nodes = Set.new
283
281
  edges = src.fetch('edges', [])
284
- unedges = []
285
282
  selfedges = []
286
283
  edges.each_index do |k|
287
284
  edge = edges[k]
288
- labels = edge.fetch('between', [])
289
- if labels.nil? || labels.empty?
290
- unedges.push(k)
291
- elsif labels.size == 2
292
- if labels.first == labels.last
293
- selfedges.push(k)
294
- else
295
- edge_nodes.add labels.first
296
- edge_nodes.add labels.last
297
- work[:edges].push({ idx: k, between: [ labels[0], labels[1] ] })
298
- end
285
+ labels = edge['between']
286
+ if labels.first == labels.last
287
+ selfedges.push(k)
299
288
  else
300
- aargh "Edge #{k + 1} does not have two labels in 'between'"
301
- errors = true
289
+ edge_nodes.add labels.first
290
+ edge_nodes.add labels.last
291
+ work[:edges].push({ idx: k, between: [ labels[0], labels[1] ] })
302
292
  end
303
293
  end
304
294
  labeled_nodes = Set.new
305
- unlabeled = []
306
295
  nodes = src.fetch('nodes', [])
307
296
  nodes.each_index do |k|
308
297
  work[:nodes].push({ idx: k })
309
298
  node = nodes[k]
310
- unless node.key? 'label'
311
- unlabeled.push k
312
- next
313
- end
314
299
  label = node['label']
315
300
  if label2idx.key?(label) && edge_nodes.member?(label)
316
301
  aargh "Edge-referred label used twice: #{label}"
@@ -327,9 +312,7 @@ def work_copy(src, quiet)
327
312
  return nil if errors
328
313
  unused = labeled_nodes - edge_nodes
329
314
  [ [ unused.to_a, 'unconnected labeled nodes' ],
330
- [ unlabeled, 'unlabeled nodes' ],
331
- [ selfedges, 'edges from node to itself' ],
332
- [ unedges, 'edges without end-points' ]
315
+ [ selfedges, 'edges from node to itself' ]
333
316
  ].each do |x|
334
317
  next if quiet || x.first.empty?
335
318
  aargh("Note, #{x.last}: #{x.first.join(' ')}")
@@ -351,6 +334,8 @@ def prepare_output(doc, work)
351
334
  end
352
335
 
353
336
  def main
337
+ input_schema = 'nodes'
338
+ output_schema = 'edges'
354
339
  input = nil
355
340
  output = nil
356
341
  algo = 'pathlength'
@@ -378,22 +363,15 @@ def main
378
363
  $stdout.puts %(
379
364
  Algorithm names are: #{$algorithms.keys.sort.join(' ')}
380
365
 
381
- Input YAML file is expected to be like:
382
- ---
383
- nodes:
384
- - label: something
385
- - label: another
386
- - ignored: "Since no label. Still placed."
387
- - label: "Unused and ok. Still placed."
388
- edges:
389
- - between: [ something, another ]
390
- - between: [ something, something ] # Ignored.
391
- - between: [ ] # Ignored.
392
- - ignored: "Since no between."
393
- ...
366
+ Input YAML file schema is returned by:
367
+ diagrammatron-schema #{input_schema}
368
+
394
369
  There can be other fields present but they are ignored. The nodes will
395
370
  receive values xo and yo that indicate horizontal and vertical coordinates.
396
371
 
372
+ Output YAML file schema is returned by:
373
+ diagrammatron-schema #{output_schema}
374
+
397
375
  Output is the input file with 'xo', 'yo' and 'sid' added to each node.
398
376
  The 'xo' and 'yo' indicate which unique x- and y-coordinate the value is.
399
377
  The 'sid' indicates the sub-diagram consisting of connected nodes.
@@ -408,7 +386,7 @@ The 'sid' indicates the sub-diagram consisting of connected nodes.
408
386
  end
409
387
  algo = $algorithms[algo]
410
388
 
411
- doc = load_source(input)
389
+ doc = load_verified(input, input_schema)
412
390
  return 2 if doc.nil?
413
391
 
414
392
  begin
@@ -420,7 +398,7 @@ The 'sid' indicates the sub-diagram consisting of connected nodes.
420
398
 
421
399
  algo.call(work)
422
400
  prepare_output(doc, work)
423
- dump_result(output, YAML.dump(doc, line_width: 1_000_000), 4)
401
+ save_verified(output, doc, 4, output_schema)
424
402
  end
425
403
 
426
404
  exit(main) if (defined? $unit_test).nil?
@@ -1,18 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # Copyright © 2021, 2022 Ismo Kärkkäinen
4
+ # Copyright © 2021-2023 Ismo Kärkkäinen
5
5
  # Licensed under Universal Permissive License. See LICENSE.txt.
6
6
 
7
7
  require_relative '../lib/common'
8
8
  require 'optparse'
9
- require 'yaml'
10
9
  require 'set'
11
- require 'pathname'
12
10
 
13
- def info(msg, return_value = nil)
11
+
12
+ def info(msg)
14
13
  $stderr.puts(msg) unless $QUIET
15
- return_value
16
14
  end
17
15
 
18
16
  BoundingBox = Struct.new(:xmin, :ymin, :xmax, :ymax) do
@@ -91,22 +89,19 @@ end
91
89
 
92
90
  def work_copy(src)
93
91
  work = { edges: {}, nodes: {} }
94
- # Expected nodes, edges. Other pass-through.
95
- nodes = src.fetch('nodes', [])
92
+ nodes = src['nodes']
96
93
  nodes.each_index do |k|
97
94
  node = nodes[k]
98
- sid = node.fetch('sid', nil)
99
- xo = node.fetch('xo', nil)
100
- yo = node.fetch('yo', nil)
101
- next if sid.nil? || xo.nil? || yo.nil?
95
+ sid = node['sid']
96
+ xo = node['xo']
97
+ yo = node['yo']
102
98
  work[:nodes][sid] = work[:nodes].fetch(sid, []).push(Node.new(k, sid, xo, yo))
103
99
  end
104
- edges = src.fetch('edges', [])
100
+ edges = src['edges']
105
101
  edges.each_index do |k|
106
102
  edge = edges[k]
107
- path = edge.fetch('path', nil)
108
- sid = edge.fetch('sid', nil)
109
- next if path.nil? || sid.nil?
103
+ path = edge['path']
104
+ sid = edge['sid']
110
105
  work[:edges][sid] = work[:edges].fetch(sid, []).push(Edge.new(k, sid, path))
111
106
  end
112
107
  work[:subsets] = work[:nodes].keys.to_set.merge(work[:edges].keys.to_set).to_a
@@ -140,7 +135,7 @@ def area_order(bbs)
140
135
  end
141
136
  order.sort! do |a, b|
142
137
  d = area_compare(a[1], b[1])
143
- (d != 0) ? d : (a[0] <=> b[0])
138
+ d.zero? ? (a[0] <=> b[0]) : d
144
139
  end
145
140
  order
146
141
  end
@@ -362,6 +357,7 @@ $QUIET = false
362
357
  def main
363
358
  input = nil
364
359
  output = nil
360
+ input_output_schema = 'edges'
365
361
  algo = 'tallwide'
366
362
  parser = OptionParser.new do |opts|
367
363
  opts.summary_indent = ' '
@@ -389,10 +385,13 @@ def main
389
385
  $stdout.puts %(
390
386
  Algorithm names are: #{$algorithms.keys.sort.join(' ')}
391
387
 
392
- Input YAML file is expected to be the output of diagrammatron-edges.
388
+ Input and output YAML file schema is returned by:
389
+ diagrammatron-schema #{input_output_schema}
390
+
391
+ There can be other fields present but they are ignored and retained.
393
392
 
394
- Output is the input file with 'xo' and 'yo' modified so that the sub-diagrams
395
- do not overlap.
393
+ Output is the input file with 'xo', 'yo', and 'path' modified to remove
394
+ overlap between sub-diagrams and edges.
396
395
  )
397
396
  exit 0
398
397
  end
@@ -415,7 +414,7 @@ do not overlap.
415
414
  end
416
415
  algo = $algorithms[algo]
417
416
 
418
- doc = load_source(input)
417
+ doc = load_verified(input, input_output_schema)
419
418
  return 2 if doc.nil?
420
419
 
421
420
  begin
@@ -427,7 +426,7 @@ do not overlap.
427
426
 
428
427
  algo.call(work)
429
428
  prepare_output(doc, work)
430
- dump_result(output, YAML.dump(doc, line_width: 1_000_000), 4)
429
+ save_verified(output, doc, 4, input_output_schema)
431
430
  end
432
431
 
433
432
  exit(main) if (defined? $unit_test).nil?
@@ -1,14 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # Copyright © 2021 Ismo Kärkkäinen
4
+ # Copyright © 2021-2023 Ismo Kärkkäinen
5
5
  # Licensed under Universal Permissive License. See LICENSE.txt.
6
6
 
7
7
  require_relative '../lib/common'
8
8
  require 'optparse'
9
- require 'yaml'
10
9
  require 'set'
11
- require 'pathname'
12
10
 
13
11
 
14
12
  def prune_array(original, matching, keep)
@@ -26,7 +24,12 @@ def prune(doc, patterns, keep)
26
24
  nodes = doc.fetch('nodes', [])
27
25
  matching = Array.new(nodes.size, false)
28
26
  nodes.each_index do |k|
29
- label = nodes[k].fetch('label', '')
27
+ label = nodes[k].fetch('label', nil)
28
+ if label.nil?
29
+ # Schemas require 'label' to be present.
30
+ matching[k] = !keep
31
+ next
32
+ end
30
33
  patterns.each do |p|
31
34
  next unless p.match(label)
32
35
  matching[k] = true
@@ -40,10 +43,12 @@ def prune(doc, patterns, keep)
40
43
  edges.each_index do |k|
41
44
  between = edges[k].fetch('between', [])
42
45
  if between.size != 2
43
- matching[k] = keep # Removing these suppresses later warnings.
46
+ matching[k] = !keep # Schemas require between size to be 2.
44
47
  elsif keep
48
+ # Both ends matched reversed remove rule, keep the edge.
45
49
  matching[k] = (labels.member?(between[0]) && labels.member?(between[1]))
46
50
  else
51
+ # If either end matched remove rule, remove the edge.
47
52
  matching[k] = (labels.member?(between[0]) || labels.member?(between[1]))
48
53
  end
49
54
  end
@@ -53,6 +58,7 @@ end
53
58
  def main
54
59
  input = nil
55
60
  output = nil
61
+ input_output_schema = 'nodes'
56
62
  keep = false
57
63
  ENV['POSIXLY_CORRECT'] = '1' # Leaves patterns as they are.
58
64
  parser = OptionParser.new do |opts|
@@ -75,6 +81,8 @@ def main
75
81
  Patterns are strings used to create Ruby Regexps.
76
82
 
77
83
  Input YAML file is expected to be dot_json2diagrammatron output.
84
+ Input and output YAML file schema is returned by:
85
+ diagrammatron-schema #{input_output_schema}
78
86
 
79
87
  Output is the input file with nodes that have labels that match patterns
80
88
  removed or kept depending on options. Edges to removed nodes are removed.
@@ -89,7 +97,7 @@ removed or kept depending on options. Edges to removed nodes are removed.
89
97
  rescue StandardError => e
90
98
  return aargh("Error creating Regexp: #{e}", 1)
91
99
  end
92
- doc = load_source(input)
100
+ doc = load_verified(input, input_output_schema)
93
101
  return 2 if doc.nil?
94
102
 
95
103
  begin
@@ -98,7 +106,7 @@ removed or kept depending on options. Edges to removed nodes are removed.
98
106
  return aargh('Error processing input.', 3)
99
107
  end
100
108
 
101
- dump_result(output, YAML.dump(doc, line_width: 1_000_000), 4)
109
+ save_verified(output, doc, 4, input_output_schema)
102
110
  end
103
111
 
104
112
  exit(main) if (defined? $unit_test).nil?
@@ -61,8 +61,7 @@ def separate_coordinates(doc)
61
61
  push_coords(xcoords, ycoords, ckd2count, node.clone, 1, 1, true)
62
62
  end
63
63
  doc['edges'].each do |edge|
64
- path = edge.fetch('path', nil)
65
- next if path.nil?
64
+ path = edge['path']
66
65
  xdirection, ydirection = end_directions(path[0], path[1])
67
66
  push_coords(xcoords, ycoords, ckd2count, path[0], xdirection, ydirection)
68
67
  (1...(path.size - 1)).each do |k|
@@ -99,7 +98,7 @@ class Styles
99
98
  @d = base_styles(base_styles({}, template_styles, 'diagram'), diagram_styles, 'diagram')
100
99
  end
101
100
 
102
- def fill(mapping, type_name, item)
101
+ def fill(mapping, _type_name, item)
103
102
  styles = item.fetch('style', [ 'default' ])
104
103
  styles = [ styles ] unless styles.is_a?(Array)
105
104
  s = {}
@@ -112,7 +111,7 @@ class Styles
112
111
  end
113
112
  s.merge!(mapping['default']) unless found # Merge default at least.
114
113
  # Keep values specified explicitly.
115
- item.merge!(s) { |key, existing, from_template| existing || from_template }
114
+ item.merge!(s) { |_key, existing, from_template| existing || from_template }
116
115
  end
117
116
 
118
117
  def apply_node_styles(node)
@@ -169,7 +168,7 @@ def estimate_sizes(doc, ckd2count)
169
168
  $render = SizeEstimation.new(ckd2count, doc)
170
169
  doc['nodes'].each do |node|
171
170
  $render.node = node
172
- label = node.fetch('label', 'unnamed')
171
+ label = node['label']
173
172
  style = node.fetch('style', 'default')
174
173
  code = node.fetch('size_estimator',
175
174
  %(raise NotImplementedError, "No size estimator for style: #{style}"))
@@ -187,7 +186,7 @@ end
187
186
  def maxima(doc)
188
187
  xmax = Hash.new(0)
189
188
  ymax = Hash.new(0)
190
- doc.fetch('nodes', []).each do |node|
189
+ doc['nodes'].each do |node|
191
190
  xmax[node['xo']] = [ node['w'], xmax[node['xo']] ].max
192
191
  ymax[node['yo']] = [ node['h'], ymax[node['yo']] ].max
193
192
  end
@@ -195,7 +194,7 @@ def maxima(doc)
195
194
  end
196
195
 
197
196
  def apply_maxima(doc, xmax, ymax)
198
- doc.fetch('nodes', []).each do |node|
197
+ doc['nodes'].each do |node|
199
198
  node['w'] = xmax[node['xo']]
200
199
  node['h'] = ymax[node['yo']]
201
200
  end
@@ -258,13 +257,12 @@ class Render
258
257
  def dimensions
259
258
  w = 0
260
259
  h = 0
261
- @doc.fetch('nodes', []).each do |node|
260
+ @doc['nodes'].each do |node|
262
261
  w = [ w, node['xo'] + node['w'] ].max
263
262
  h = [ h, node['yo'] + node['h'] ].max
264
263
  end
265
- @doc.fetch('edges', []).each do |edge|
266
- path = edge.fetch('path', nil)
267
- next if path.nil?
264
+ @doc['edges'].each do |edge|
265
+ path = edge['path']
268
266
  path.each do |p|
269
267
  w = [ w, p['xo'] ].max
270
268
  h = [ h, p['yo'] ].max
@@ -305,6 +303,7 @@ end
305
303
  def main
306
304
  template = nil
307
305
  input = nil
306
+ input_schema = 'render'
308
307
  output = nil
309
308
  styles = nil
310
309
  parser = OptionParser.new do |opts|
@@ -324,7 +323,10 @@ def main
324
323
  end
325
324
  opts.on('-h', '--help', 'Print this help and exit.') do
326
325
  $stdout.puts %(#{opts}
327
- Input YAML file is expected to be the output of diagrammatron-place.
326
+ Input YAML file schema is returned by:
327
+ diagrammatron-schema #{input_schema}
328
+
329
+ There can be other fields present as needed by the template.
328
330
 
329
331
  Output is the file produced by the erb-template.
330
332
  )
@@ -347,15 +349,15 @@ Output is the file produced by the erb-template.
347
349
  end
348
350
  end
349
351
 
350
- doc = load_source(input)
352
+ doc = load_verified(input, input_schema)
351
353
  return 2 if doc.nil?
352
354
 
353
355
  styles = Styles.new(template.fetch('styles', {}), doc.fetch('styles', {}))
354
- doc.fetch('nodes', []).each do |node|
356
+ doc['nodes'].each do |node|
355
357
  styles.apply_node_styles(node)
356
- node['text'] = node.fetch('text', node.fetch('label', '')).split("\n")
358
+ node['text'] = node.fetch('text', node['label']).split("\n")
357
359
  end
358
- doc.fetch('edges', []).each { |edge| styles.apply_edge_styles(edge) }
360
+ doc['edges'].each { |edge| styles.apply_edge_styles(edge) }
359
361
  doc['diagram'] = {} unless doc.key? 'diagram'
360
362
  styles.apply_diagram_styles(doc['diagram'])
361
363
 
@@ -376,16 +378,16 @@ Output is the file produced by the erb-template.
376
378
  remap_coordinates(xcoords, xmax, x2min, doc.dig('diagram', 'edge_gap'))
377
379
  remap_coordinates(ycoords, ymax, y2min, doc.dig('diagram', 'edge_gap'))
378
380
 
379
- doc['nodes'] = reverse_depth_sort(doc.fetch('nodes', []))
380
- doc['edges'] = reverse_depth_sort(doc.fetch('edges', []))
381
- all = doc.fetch('nodes', []).map do |a|
381
+ doc['nodes'] = reverse_depth_sort(doc['nodes'])
382
+ doc['edges'] = reverse_depth_sort(doc['edges'])
383
+ all = doc['nodes'].map do |a|
382
384
  {
383
385
  'kind' => 'node',
384
386
  'depth' => a.fetch('depth', 0),
385
387
  'item' => a
386
388
  }
387
389
  end
388
- all.concat(doc.fetch('edges', []).map do |a|
390
+ all.concat(doc['edges'].map do |a|
389
391
  {
390
392
  'kind' => 'edge',
391
393
  'depth' => a.fetch('depth', 0),