diagrammatron 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/diagrammatron-edges +73 -95
- data/bin/diagrammatron-render +47 -1
- data/template/internal.yaml +3 -1
- data/template/root.yaml +2 -0
- data/template/svg_1.1.erb +39 -36
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f4b7974989ef344d2eaaae0c54f82567b8ed9edd9c7fb5d2dfc3e405895efa7
|
4
|
+
data.tar.gz: efdd52da27d648f9da7fed309ba668eae6237eeddd51a703c47c46c45c394435
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e235a20fb0fb4013e4f0006731743830f84500519d2ab4262b04caa9bae0962bd7da5127cd30e44c9e6bf891b735a02f10fe193149047fa6b54ec0bd6185f2e5
|
7
|
+
data.tar.gz: e6c4c3db889c83d23390ee874379215747fb4336fbb3f1f2f543226ab72788fe948a6394e500ea89ed4d4a013126a324b10fa5b49bf73be9233cd94e9781a024
|
data/bin/diagrammatron-edges
CHANGED
@@ -158,6 +158,10 @@ Segment = Struct.new(:vertical, :cc, :range, :edge_index, :at_node, :segment_ind
|
|
158
158
|
def increase?
|
159
159
|
range[0] < range[1]
|
160
160
|
end
|
161
|
+
|
162
|
+
def within(s)
|
163
|
+
s.range.min < range.min && range.max < s.range.max
|
164
|
+
end
|
161
165
|
end
|
162
166
|
|
163
167
|
def segment(x0, y0, x1, y1)
|
@@ -415,40 +419,16 @@ def path_order_at_side(a, b, conn)
|
|
415
419
|
d
|
416
420
|
end
|
417
421
|
|
418
|
-
def
|
419
|
-
d = a[
|
422
|
+
def length_order(a, b)
|
423
|
+
d = a[2].length <=> b[2].length
|
420
424
|
return d unless d.zero?
|
421
|
-
case a[1]
|
422
|
-
when 0 # Ascending on length, range minimum. =|
|
423
|
-
d = a[2].length <=> b[2].length
|
424
|
-
return d unless d.zero?
|
425
|
-
d = a[2].range.min <=> b[2].range.min
|
426
|
-
return d unless d.zero?
|
427
|
-
when 1 # Top left, vertical, bottom right: -|_
|
428
|
-
d = a[2].range.min <=> b[2].range.min # Ascend on minimum.
|
429
|
-
return d unless d.zero?
|
430
|
-
d = a[2].length <=> b[2].length # Ascend on length.
|
431
|
-
return d unless d.zero?
|
432
|
-
when 2 # From top right, down, bottom left: _|-
|
433
|
-
d = a[2].range.max <=> b[2].range.max # Descend on maximum.
|
434
|
-
return -d unless d.zero?
|
435
|
-
d = a[2].length <=> b[2].length # Ascend on length.
|
436
|
-
return d unless d.zero?
|
437
|
-
when 3 # Descending on length, range maximum. |=
|
438
|
-
d = a[2].length <=> b[2].length
|
439
|
-
return -d unless d.zero?
|
440
|
-
d = a[2].range.max <=> b[2].range.max
|
441
|
-
return -d unless d.zero?
|
442
|
-
end
|
443
425
|
a[0].edge_index <=> b[0].edge_index
|
444
426
|
end
|
445
427
|
|
446
|
-
def
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
from_left_up_to_right.each { |x| out.push x }
|
451
|
-
out
|
428
|
+
def min_order(a, b)
|
429
|
+
d = a[2].range.min <=> b[2].range.min
|
430
|
+
return d unless d.zero?
|
431
|
+
a[0].edge_index <=> b[0].edge_index
|
452
432
|
end
|
453
433
|
|
454
434
|
def overlaps_set(c, others)
|
@@ -458,47 +438,59 @@ def overlaps_set(c, others)
|
|
458
438
|
false
|
459
439
|
end
|
460
440
|
|
461
|
-
def
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
441
|
+
def layered_order(u_shaped, z_shaped)
|
442
|
+
u_shaped.sort! { |a, b| length_order(a, b) }
|
443
|
+
z_shaped.sort! { |a, b| min_order(a, b) }
|
444
|
+
out = []
|
445
|
+
layer = []
|
446
|
+
until u_shaped.empty?
|
447
|
+
rejects = []
|
448
|
+
found = false
|
449
|
+
u_shaped.each do |a|
|
450
|
+
unless overlaps_set(a[2], layer)
|
451
|
+
found = true
|
452
|
+
# Creates crossing by putting in front of shorter segment fully within?
|
453
|
+
rejects.each do |r|
|
454
|
+
if r[2].within(a[2])
|
455
|
+
found = false
|
456
|
+
break
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
if found
|
461
|
+
layer.push a
|
462
|
+
found = false
|
463
|
+
else
|
464
|
+
rejects.push a
|
465
|
+
end
|
466
|
+
end
|
467
|
+
u_shaped = rejects
|
468
|
+
unless layer.empty?
|
469
|
+
out.concat layer
|
470
|
+
out.push nil
|
471
|
+
layer = []
|
472
472
|
end
|
473
|
-
next if fit
|
474
|
-
cands.push(cands.last + 1)
|
475
|
-
off[cands.last] = [ a ]
|
476
473
|
end
|
477
|
-
|
478
|
-
|
479
|
-
|
474
|
+
until z_shaped.empty?
|
475
|
+
rejects = []
|
476
|
+
found = false
|
477
|
+
z_shaped.each do |a|
|
478
|
+
if overlaps_set(a[2], layer)
|
479
|
+
rejects.push a
|
480
|
+
else
|
481
|
+
layer.push a
|
482
|
+
found = true
|
483
|
+
end
|
484
|
+
end
|
485
|
+
z_shaped = rejects
|
486
|
+
unless layer.empty?
|
487
|
+
out.concat layer
|
488
|
+
out.push nil
|
489
|
+
layer = []
|
480
490
|
end
|
481
491
|
end
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
def group_stacked_offsets(group)
|
486
|
-
return 0 if group.empty?
|
487
|
-
group.each_index do |k|
|
488
|
-
group[k][0].offset = k + 1
|
489
|
-
end
|
490
|
-
return group.size
|
491
|
-
%q(
|
492
|
-
# This produces narrower gaps but they may be less clear.
|
493
|
-
prev = group[0]
|
494
|
-
prev[0].offset = 1
|
495
|
-
(1...group.size).each do |k|
|
496
|
-
g = group[k]
|
497
|
-
g[0].offset = prev[0].offset + (g[2].range_overlap(prev[2]) ? 1 : 0)
|
498
|
-
prev = g
|
499
|
-
end
|
500
|
-
prev[0].offset
|
501
|
-
)
|
492
|
+
out.concat(layer) unless layer.empty?
|
493
|
+
out
|
502
494
|
end
|
503
495
|
|
504
496
|
def direct_range(paths)
|
@@ -631,36 +623,22 @@ def place_edges(work)
|
|
631
623
|
end
|
632
624
|
gaps.each_value do |direction|
|
633
625
|
direction.each_value do |gap|
|
634
|
-
#gap.sort! { |a, b| segment_order(a, b) }
|
635
626
|
gleft = gap.select { |a| a[1].zero? }
|
636
|
-
|
627
|
+
grul = gap.select { |a| a[1] == 1 }
|
628
|
+
glur = gap.select { |a| a[1] == 2 }
|
637
629
|
gright = gap.select { |a| a[1] == 3 }
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
group_minimal_offsets(gleft),
|
651
|
-
group_stacked_offsets(gmiddle),
|
652
|
-
0,
|
653
|
-
group_minimal_offsets(gright)
|
654
|
-
]
|
655
|
-
before = [ 0 ]
|
656
|
-
denominator = 1 + c[0]
|
657
|
-
(1...c.size).each do |k|
|
658
|
-
denominator += c[k]
|
659
|
-
before.push(c[k - 1] + before.last)
|
660
|
-
end
|
661
|
-
gap.each do |sg|
|
662
|
-
sg[0].offset = c[sg[1]] + 1 - sg[0].offset if sg[1] > 1
|
663
|
-
sg[0].offset = Rational(sg[0].offset + before[sg[1]], denominator)
|
630
|
+
all = layered_order(gleft, grul)
|
631
|
+
all.push nil
|
632
|
+
all.concat(layered_order(gright, glur).reverse)
|
633
|
+
# Give each rational offset using layer index + 1 and layer count + 2.
|
634
|
+
denominator = 2 + all.count { |x| x.nil? }
|
635
|
+
layer = 1
|
636
|
+
all.each do |sg|
|
637
|
+
if sg.nil?
|
638
|
+
layer += 1
|
639
|
+
else
|
640
|
+
sg[0].offset = Rational(layer, denominator)
|
641
|
+
end
|
664
642
|
end
|
665
643
|
end
|
666
644
|
end
|
data/bin/diagrammatron-render
CHANGED
@@ -80,9 +80,10 @@ class Styles
|
|
80
80
|
def base_styles(m, styles, group)
|
81
81
|
d = styles.dig(group, 'default')
|
82
82
|
if d.nil?
|
83
|
-
d = m.fetch('default', {})
|
83
|
+
d = m.fetch('default', {}) # No default in styles.
|
84
84
|
else
|
85
85
|
m['default'] = m.fetch('default', {}).merge(d)
|
86
|
+
d = m['default']
|
86
87
|
end
|
87
88
|
styles.fetch(group, {}).each_pair do |name, values|
|
88
89
|
s = d.clone
|
@@ -280,6 +281,27 @@ def apply(doc, template)
|
|
280
281
|
out
|
281
282
|
end
|
282
283
|
|
284
|
+
def reverse_depth_order(a, b)
|
285
|
+
d = b['depth'] <=> a['depth']
|
286
|
+
return d unless d.zero?
|
287
|
+
d = a['index'] <=> b['index']
|
288
|
+
return d unless d.zero?
|
289
|
+
a['kind'] <=> b['kind']
|
290
|
+
end
|
291
|
+
|
292
|
+
def reverse_depth_sort(items)
|
293
|
+
arr = []
|
294
|
+
items.each_index do |k|
|
295
|
+
arr.push({
|
296
|
+
'depth' => items[k].fetch('depth', 0),
|
297
|
+
'index' => k,
|
298
|
+
'kind' => items[k].fetch('kind', 'unknown')
|
299
|
+
})
|
300
|
+
end
|
301
|
+
arr.sort! { |a, b| reverse_depth_order(a, b) }
|
302
|
+
arr.map { |x| items[x['index']] }
|
303
|
+
end
|
304
|
+
|
283
305
|
def main
|
284
306
|
template = nil
|
285
307
|
input = nil
|
@@ -354,6 +376,30 @@ Output is the file produced by the erb-template.
|
|
354
376
|
remap_coordinates(xcoords, xmax, x2min, doc.dig('diagram', 'edge_gap'))
|
355
377
|
remap_coordinates(ycoords, ymax, y2min, doc.dig('diagram', 'edge_gap'))
|
356
378
|
|
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|
|
382
|
+
{
|
383
|
+
'kind' => 'node',
|
384
|
+
'depth' => a.fetch('depth', 0),
|
385
|
+
'item' => a
|
386
|
+
}
|
387
|
+
end
|
388
|
+
all.concat(doc.fetch('edges', []).map do |a|
|
389
|
+
{
|
390
|
+
'kind' => 'edge',
|
391
|
+
'depth' => a.fetch('depth', 0),
|
392
|
+
'item' => a
|
393
|
+
}
|
394
|
+
end)
|
395
|
+
all = reverse_depth_sort(all)
|
396
|
+
doc['all'] = all.map do |x|
|
397
|
+
{
|
398
|
+
'kind' => x['kind'],
|
399
|
+
'item' => x['item']
|
400
|
+
}
|
401
|
+
end
|
402
|
+
|
357
403
|
dump_result(output, apply(doc, template), 5)
|
358
404
|
end
|
359
405
|
|
data/template/internal.yaml
CHANGED
@@ -19,6 +19,7 @@ styles:
|
|
19
19
|
fill: "#ffffff"
|
20
20
|
stroke: "#000000"
|
21
21
|
stroke_width: 2
|
22
|
+
depth: 0
|
22
23
|
size_estimator: |
|
23
24
|
$render.default_size($render.node['font_size'],
|
24
25
|
$render.node['font_width'], $render.node['font_height'],
|
@@ -29,4 +30,5 @@ styles:
|
|
29
30
|
default:
|
30
31
|
stroke_width: 2
|
31
32
|
stroke: "#000000"
|
32
|
-
|
33
|
+
depth: 0
|
34
|
+
base64template: PD94bWwgdmVyc2lvbj0iMS4wIj8+CjwlPQp3LCBoaCA9ICRyZW5kZXIuZGltZW5zaW9ucwpoaCArPSAkcmVuZGVyLmRvYy5kaWcoJ2RpYWdyYW0nLCAnaGVpZ2h0X21hcmdpbicpCgpvdXQgPSBbCiAgJSg8c3ZnIHdpZHRoPSIje3cgKyAkcmVuZGVyLmRvYy5kaWcoJ2RpYWdyYW0nLCAnd2lkdGhfbWFyZ2luJyl9IiBoZWlnaHQ9IiN7aGh9IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiPikKXQokcmVuZGVyLmRvY1snYWxsJ10uZWFjaCBkbyB8aXRlbXwKICBpZiBpdGVtWydraW5kJ10gPT0gJ25vZGUnCiAgICBub2RlID0gaXRlbVsnaXRlbSddCiAgICB3ID0gbm9kZVsndyddLnRvX2kKICAgIGggPSBub2RlWydoJ10udG9faQogICAgeCA9IG5vZGVbJ3hvJ10udG9faQogICAgeSA9IGhoIC0gbm9kZVsneW8nXS50b19pIC0gaAogICAgbm9kZXN0eWxlID0gJShmaWxsPSIje25vZGVbJ2ZpbGwnXX0iIHN0cm9rZT0iI3tub2RlWydzdHJva2UnXX0iIHN0cm9rZS13aWR0aD0iI3tub2RlWydzdHJva2Vfd2lkdGgnXX0iKQogICAgb3V0LnB1c2goJSg8cmVjdCAje25vZGVzdHlsZX0gaGVpZ2h0PSIje2h9IiB3aWR0aD0iI3t3fSIgeD0iI3t4fSIgeT0iI3t5fSIvPikpCiAgICB4ICs9IG5vZGVbJ3dpZHRoX21hcmdpbiddCiAgICBmcyA9IG5vZGVbJ2ZvbnRfc2l6ZSddCiAgICBsaCA9IGZzICogKDEgKyBub2RlWydmb250X2xpbmVfc3BhY2luZyddKQogICAgeSArPSBub2RlWydoZWlnaHRfbWFyZ2luJ10gKyBmcyAqIG5vZGVbJ2ZvbnRfYXNjZW5kJ10gIyBCYXNlbGluZSBmb3IgZmlyc3QgbGluZS4KICAgIHVybCA9IG5vZGUuZmV0Y2goJ3VybCcsIG5pbCkKICAgIHVybC5lbmNvZGUhKDp4bWwgPT4gOmF0dHIpIHVubGVzcyB1cmwubmlsPwogICAgeTAgPSB5CiAgICB0ZXh0c3R5bGUgPSAlKGZpbGw9IiN7bm9kZVsnZm9udF9maWxsJ119IiBmb250LWZhbWlseT0ic2VyaWYiIGZvbnQtc2l6ZT0iI3tmc30iIHN0cm9rZT0iI3tub2RlWydmb250X2ZpbGwnXX0iIHN0cm9rZS13aWR0aD0iMCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIpCiAgICBsaW5rc3R5bGUgPSAlKGZpbGw9IiN7bm9kZVsndXJsX2ZpbGwnXX0iIGZvbnQtZmFtaWx5PSJzZXJpZiIgZm9udC1zaXplPSIje2ZzfSIgc3Ryb2tlPSIje25vZGVbJ3VybF9maWxsJ119IiBzdHJva2Utd2lkdGg9IjAiIHhtbDpzcGFjZT0icHJlc2VydmUiKQogICAgbm9kZVsndGV4dCddLmVhY2ggZG8gfGxpbmV8CiAgICAgIGxpbmUuZW5jb2RlISg6eG1sID0+IDp0ZXh0KQogICAgICBpZiB1cmwubmlsPwogICAgICAgIG91dC5wdXNoKCUoPHRleHQgI3t0ZXh0c3R5bGV9IHg9IiN7eH0iIHk9IiN7eTB9Ij4je2xpbmV9PC90ZXh0PikpCiAgICAgIGVsc2UKICAgICAgICBvdXQucHVzaCglKDxhIHhsaW5rOmhyZWY9I3t1cmx9IHRhcmdldD0iX3BhcmVudCI+PHRleHQgI3tsaW5rc3R5bGV9IHg9IiN7eH0iIHk9IiN7eTB9Ij4je2xpbmV9PC90ZXh0PjwvYT4pKQogICAgICBlbmQKICAgICAgeTAgKz0gbGggIyBTaGlmdCBiYXNlbGluZSBieSBmdWxsIGxpbmUgKyBzcGFjaW5nIGhlaWdodC4KICAgIGVuZAogIGVsc2UKICAgIGVkZ2UgPSBpdGVtWydpdGVtJ10KICAgIGxpbmVzdHlsZSA9ICUoZmlsbD0ibm9uZSIgc3Ryb2tlPSIje2VkZ2VbJ3N0cm9rZSddfSIgc3Ryb2tlLXdpZHRoPSIje2VkZ2VbJ3N0cm9rZV93aWR0aCddfSIpCiAgICBwYXRoID0gZWRnZS5mZXRjaCgncGF0aCcsIG5pbCkKICAgIG5leHQgaWYgcGF0aC5uaWw/CiAgICBwYXRoLmVhY2ggZG8gfHB8CiAgICAgIHBbJ3hvJ10gPSBwWyd4byddLnRvX2kudG9fcwogICAgICBwWyd5byddID0gKGhoIC0gcFsneW8nXSkudG9faS50b19zCiAgICBlbmQKICAgIGlmIHBhdGguc2l6ZSA9PSAyCiAgICAgIG91dC5wdXNoKCUoPGxpbmUgI3tsaW5lc3R5bGV9IHgxPSIje3BhdGhbMF1bJ3hvJ119IiB4Mj0iI3twYXRoWzFdWyd4byddfSIgeTE9IiN7cGF0aFswXVsneW8nXX0iIHkyPSIje3BhdGhbMV1bJ3lvJ119Ii8+KSkKICAgIGVsc2UKICAgICAgcHRzID0gcGF0aC5tYXAgeyB8cHwgIiN7cFsneG8nXX0sI3twWyd5byddfSIgfQogICAgICBvdXQucHVzaCglKDxwb2x5bGluZSAje2xpbmVzdHlsZX0gcG9pbnRzPSIje3B0cy5qb2luKCcgJyl9Ii8+KSkKICAgIGVuZAogIGVuZAplbmQKb3V0LmpvaW4oIlxuIikKJT4KPC9zdmc+Cg==
|
data/template/root.yaml
CHANGED
@@ -18,6 +18,7 @@ styles:
|
|
18
18
|
fill: "#ffffff"
|
19
19
|
stroke: "#000000"
|
20
20
|
stroke_width: 2
|
21
|
+
depth: 0
|
21
22
|
size_estimator: |
|
22
23
|
$render.default_size($render.node['font_size'],
|
23
24
|
$render.node['font_width'], $render.node['font_height'],
|
@@ -28,3 +29,4 @@ styles:
|
|
28
29
|
default:
|
29
30
|
stroke_width: 2
|
30
31
|
stroke: "#000000"
|
32
|
+
depth: 0
|
data/template/svg_1.1.erb
CHANGED
@@ -6,45 +6,48 @@ hh += $render.doc.dig('diagram', 'height_margin')
|
|
6
6
|
out = [
|
7
7
|
%(<svg width="#{w + $render.doc.dig('diagram', 'width_margin')}" height="#{hh}" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">)
|
8
8
|
]
|
9
|
-
$render.doc
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
$render.doc['all'].each do |item|
|
10
|
+
if item['kind'] == 'node'
|
11
|
+
node = item['item']
|
12
|
+
w = node['w'].to_i
|
13
|
+
h = node['h'].to_i
|
14
|
+
x = node['xo'].to_i
|
15
|
+
y = hh - node['yo'].to_i - h
|
16
|
+
nodestyle = %(fill="#{node['fill']}" stroke="#{node['stroke']}" stroke-width="#{node['stroke_width']}")
|
17
|
+
out.push(%(<rect #{nodestyle} height="#{h}" width="#{w}" x="#{x}" y="#{y}"/>))
|
18
|
+
x += node['width_margin']
|
19
|
+
fs = node['font_size']
|
20
|
+
lh = fs * (1 + node['font_line_spacing'])
|
21
|
+
y += node['height_margin'] + fs * node['font_ascend'] # Baseline for first line.
|
22
|
+
url = node.fetch('url', nil)
|
23
|
+
url.encode!(:xml => :attr) unless url.nil?
|
24
|
+
y0 = y
|
25
|
+
textstyle = %(fill="#{node['font_fill']}" font-family="serif" font-size="#{fs}" stroke="#{node['font_fill']}" stroke-width="0" xml:space="preserve")
|
26
|
+
linkstyle = %(fill="#{node['url_fill']}" font-family="serif" font-size="#{fs}" stroke="#{node['url_fill']}" stroke-width="0" xml:space="preserve")
|
27
|
+
node['text'].each do |line|
|
28
|
+
line.encode!(:xml => :text)
|
29
|
+
if url.nil?
|
30
|
+
out.push(%(<text #{textstyle} x="#{x}" y="#{y0}">#{line}</text>))
|
31
|
+
else
|
32
|
+
out.push(%(<a xlink:href=#{url} target="_parent"><text #{linkstyle} x="#{x}" y="#{y0}">#{line}</text></a>))
|
33
|
+
end
|
34
|
+
y0 += lh # Shift baseline by full line + spacing height.
|
35
|
+
end
|
19
36
|
else
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
out.push(%(<rect #{nodestyle} height="#{h}" width="#{w}" x="#{x}" y="#{y}"/>))
|
31
|
-
x += node['width_margin']
|
32
|
-
fs = node['font_size']
|
33
|
-
lh = fs * (1 + node['font_line_spacing'])
|
34
|
-
y += node['height_margin'] + fs * node['font_ascend'] # Baseline for first line.
|
35
|
-
url = node.fetch('url', nil)
|
36
|
-
url.encode!(:xml => :attr) unless url.nil?
|
37
|
-
y0 = y
|
38
|
-
textstyle = %(fill="#{node['font_fill']}" font-family="serif" font-size="#{fs}" stroke="#{node['font_fill']}" stroke-width="0" xml:space="preserve")
|
39
|
-
linkstyle = %(fill="#{node['url_fill']}" font-family="serif" font-size="#{fs}" stroke="#{node['url_fill']}" stroke-width="0" xml:space="preserve")
|
40
|
-
node['text'].each do |line|
|
41
|
-
line.encode!(:xml => :text)
|
42
|
-
if url.nil?
|
43
|
-
out.push(%(<text #{textstyle} x="#{x}" y="#{y0}">#{line}</text>))
|
37
|
+
edge = item['item']
|
38
|
+
linestyle = %(fill="none" stroke="#{edge['stroke']}" stroke-width="#{edge['stroke_width']}")
|
39
|
+
path = edge.fetch('path', nil)
|
40
|
+
next if path.nil?
|
41
|
+
path.each do |p|
|
42
|
+
p['xo'] = p['xo'].to_i.to_s
|
43
|
+
p['yo'] = (hh - p['yo']).to_i.to_s
|
44
|
+
end
|
45
|
+
if path.size == 2
|
46
|
+
out.push(%(<line #{linestyle} x1="#{path[0]['xo']}" x2="#{path[1]['xo']}" y1="#{path[0]['yo']}" y2="#{path[1]['yo']}"/>))
|
44
47
|
else
|
45
|
-
|
48
|
+
pts = path.map { |p| "#{p['xo']},#{p['yo']}" }
|
49
|
+
out.push(%(<polyline #{linestyle} points="#{pts.join(' ')}"/>))
|
46
50
|
end
|
47
|
-
y0 += lh # Shift baseline by full line + spacing height.
|
48
51
|
end
|
49
52
|
end
|
50
53
|
out.join("\n")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: diagrammatron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ismo Kärkkäinen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
|