rsyntaxtree 2.4.1 → 2.5.0
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/CHANGELOG.md +138 -540
- data/CITATION.cff +2 -2
- data/README.md +2 -1
- data/bin/rsyntaxtree +3 -0
- data/lib/rsyntaxtree/base_graph.rb +94 -30
- data/lib/rsyntaxtree/element.rb +1 -1
- data/lib/rsyntaxtree/elementlist.rb +95 -0
- data/lib/rsyntaxtree/json_graph.rb +22 -47
- data/lib/rsyntaxtree/notation_core.md +1 -0
- data/lib/rsyntaxtree/svg_graph.rb +122 -26
- data/lib/rsyntaxtree/version.rb +1 -1
- data/lib/rsyntaxtree.rb +19 -9
- data/rsyntaxtree.gemspec +2 -2
- metadata +1 -2
- data/.dockerignore +0 -5
data/CITATION.cff
CHANGED
|
@@ -11,8 +11,8 @@ repository-code: "https://github.com/yohasebe/rsyntaxtree"
|
|
|
11
11
|
# Concept DOI: always resolves to the latest archived version on Zenodo
|
|
12
12
|
doi: "10.5281/zenodo.21916150"
|
|
13
13
|
license: MIT
|
|
14
|
-
version:
|
|
15
|
-
date-released: 2026-
|
|
14
|
+
version: 2.5.0
|
|
15
|
+
date-released: 2026-11-04
|
|
16
16
|
keywords:
|
|
17
17
|
- linguistics
|
|
18
18
|
- syntax tree
|
data/README.md
CHANGED
|
@@ -144,7 +144,7 @@ older setup still fails to build `gobject-introspection`, `cairo-gobject` or
|
|
|
144
144
|
`gio2`, install those three first with
|
|
145
145
|
`-- --with-ldflags="-Wl,-undefined,dynamic_lookup"` appended, then install
|
|
146
146
|
RSyntaxTree; or skip the build entirely with the
|
|
147
|
-
[Docker image](https://
|
|
147
|
+
[Docker image](https://github.com/yohasebe/rsyntaxtree_web) or the
|
|
148
148
|
[web interface](https://yohasebe.com/rsyntaxtree).
|
|
149
149
|
|
|
150
150
|
## Usage
|
|
@@ -172,6 +172,7 @@ Usage:
|
|
|
172
172
|
| `-c, --color` | Color mode: modern, traditional, gray (black text, grey lines), off | `modern` |
|
|
173
173
|
| `-p, --polyline` | Polyline connectors: on, off | `off` |
|
|
174
174
|
| `--hide-default-connectors` | Draw no parent-to-child lines, leaving only the ones written with `+-n`: on, off | `off` |
|
|
175
|
+
| `--leaf-align` | Line the leaves up on one row: off, words (lower the words), subtrees (lower whole subtrees) | `off` |
|
|
175
176
|
| `-d, --direction` | Tree layout direction: ttb, ltr, btt | `ttb` |
|
|
176
177
|
| `--tidy` | Layout scale: off, symmetric, low, medium, high | `off` |
|
|
177
178
|
| `--hspacing` | How far apart sisters sit, all layout modes (0.5-3.0) | `1.0` |
|
data/bin/rsyntaxtree
CHANGED
|
@@ -27,6 +27,7 @@ CONFIG_VALIDATORS = {
|
|
|
27
27
|
transparent: ->(v) { /\A(on|off|true|false)\z/ =~ v.to_s ? nil : "must be on or off" },
|
|
28
28
|
polyline: ->(v) { /\A(on|off|true|false)\z/ =~ v.to_s ? nil : "must be on or off" },
|
|
29
29
|
hide_default_connectors: ->(v) { /\A(on|off|true|false)\z/ =~ v.to_s ? nil : "must be on or off" },
|
|
30
|
+
leaf_align: ->(v) { OPTION_VALUES[:leaf_align].include?(v.to_s) ? nil : "must be off, words, or subtrees" },
|
|
30
31
|
mirror: ->(v) { /\A(on|off|true|false)\z/ =~ v.to_s ? nil : "must be on or off" },
|
|
31
32
|
shear: ->(v) { v.is_a?(Numeric) && v >= -45 && v <= 45 ? nil : "must be in the range of -45-45" },
|
|
32
33
|
vmargin: ->(v) { v.is_a?(Numeric) && v >= 0.1 && v <= 1.0 ? nil : "must be in the range of 0.1-1.0" },
|
|
@@ -118,6 +119,7 @@ opts = Optimist.options do
|
|
|
118
119
|
opt :transparent, "Make background transparent: on or off", default: "off", short: :r
|
|
119
120
|
opt :polyline, "draw polyline connectors: on or off", default: "off", short: :p
|
|
120
121
|
opt :hide_default_connectors, "make default connectors transparent: on or off", default: "off", short: :none
|
|
122
|
+
opt :leaf_align, "line the leaves up on one row: off, words (lower the words), or subtrees (lower whole subtrees)", default: "off", short: :none
|
|
121
123
|
opt :mirror, "flip the tree horizontally (RTL linguistics convention: first word at the right edge): on or off", default: "off"
|
|
122
124
|
opt :shear, "Tilt the drawn figure, in degrees: -45 to 45, positive leaning the top to the right", default: 0.0, short: :none
|
|
123
125
|
opt :shear_plane, "The plane behind a sheared figure: on, off, a colour name, or a hex colour", default: "on", short: :none
|
|
@@ -157,6 +159,7 @@ Optimist.die :vheight, "must be in the range of 0.5-5.0" if opts[:vheight] < 0.5
|
|
|
157
159
|
Optimist.die :transparent, "must be either on or off" unless /\A(on|off|true|false)\z/ =~ opts[:transparent]
|
|
158
160
|
Optimist.die :polyline, "must be either on or off" unless /\A(on|off|true|false)\z/ =~ opts[:polyline]
|
|
159
161
|
Optimist.die :hide_default_connectors, "must be either on or off" unless /\A(on|off|true|false)\z/ =~ opts[:hide_default_connectors]
|
|
162
|
+
Optimist.die :leaf_align, "must be off, words, or subtrees" unless OPTION_VALUES[:leaf_align].include?(opts[:leaf_align])
|
|
160
163
|
Optimist.die :mirror, "must be either on or off" unless /\A(on|off|true|false)\z/ =~ opts[:mirror]
|
|
161
164
|
Optimist.die :tidy, "must be off, symmetric, low, medium, or high" unless OPTION_VALUES[:tidy].include?(opts[:tidy])
|
|
162
165
|
Optimist.die :hspacing, "must be in the range of 0.5-3.0" if opts[:hspacing] < 0.5 || opts[:hspacing] > 3.0
|
|
@@ -67,6 +67,7 @@ module RSyntaxTree
|
|
|
67
67
|
@fontsize = params[:fontsize]
|
|
68
68
|
@mirror = params[:mirror] == true
|
|
69
69
|
@derivation = params[:derivation] == true
|
|
70
|
+
@leaf_align = !params[:leaf_align].nil? && params[:leaf_align].to_s != "off"
|
|
70
71
|
# tidy is one layout scale: "symmetric" (radical symmetrization) |
|
|
71
72
|
# "off" | "low" (packing, strict leaf positions) | "medium" (packing
|
|
72
73
|
# with cross-row tucking as long as no two leaves swap left-right
|
|
@@ -247,7 +248,43 @@ module RSyntaxTree
|
|
|
247
248
|
end
|
|
248
249
|
target.height = accum_array.max - target.vertical_indent
|
|
249
250
|
accum_array.max
|
|
250
|
-
end.tap
|
|
251
|
+
end.tap do
|
|
252
|
+
level_derivation_rows if id == 1 && @derivation
|
|
253
|
+
level_leaf_rows if id == 1 && @leaf_align && !@derivation
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Leaf alignment gives every leaf the same depth, but equal depth is not
|
|
258
|
+
# an equal row: each label is set below its own parent, so a taller parent
|
|
259
|
+
# (a label of two lines) or a shorter drop (a leaf pulled up under
|
|
260
|
+
# leafstyle nothing) leaves the words of one depth at different heights.
|
|
261
|
+
# Drawn left to right it is worse, a level being as wide as the label
|
|
262
|
+
# standing in it. Every depth is therefore given one row: it starts below
|
|
263
|
+
# the tallest label of the row above, by the largest drop any of its
|
|
264
|
+
# elements had, so each connector keeps at least the room it was given.
|
|
265
|
+
# Worked in the swapped coordinates the left-to-right layout runs in,
|
|
266
|
+
# where a column is a row.
|
|
267
|
+
def level_leaf_rows
|
|
268
|
+
elements = @element_list.get_elements
|
|
269
|
+
drop = Hash.new(0.0)
|
|
270
|
+
elements.each do |e|
|
|
271
|
+
next unless e.parent.to_i.positive?
|
|
272
|
+
|
|
273
|
+
parent = @element_list.get_id(e.parent)
|
|
274
|
+
drop[e.level] = [drop[e.level], e.vertical_indent - parent.vertical_indent - parent.content_height].max
|
|
275
|
+
end
|
|
276
|
+
tallest = Hash.new(0.0)
|
|
277
|
+
elements.each { |e| tallest[e.level] = [tallest[e.level], e.content_height].max }
|
|
278
|
+
|
|
279
|
+
top = {}
|
|
280
|
+
y = 0.0
|
|
281
|
+
tallest.keys.sort.each do |level|
|
|
282
|
+
y += drop[level]
|
|
283
|
+
top[level] = y
|
|
284
|
+
y += tallest[level]
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
elements.each { |e| e.vertical_indent = top[e.level] }
|
|
251
288
|
end
|
|
252
289
|
|
|
253
290
|
# In a derivation every premise is given at the start, so the words stand in
|
|
@@ -412,7 +449,9 @@ module RSyntaxTree
|
|
|
412
449
|
parent.triangle || (ETYPE_LEAF == child.type && child.triangle)
|
|
413
450
|
end
|
|
414
451
|
|
|
415
|
-
|
|
452
|
+
# `through` carries the connector chosen above a run of joints leaf
|
|
453
|
+
# alignment inserted, down that run to the child it reaches.
|
|
454
|
+
def draw_connector(id = 1, through = nil)
|
|
416
455
|
parent = @element_list.get_id(id)
|
|
417
456
|
children = parent.children.map { |c| @element_list.get_id(c) }
|
|
418
457
|
|
|
@@ -425,40 +464,65 @@ module RSyntaxTree
|
|
|
425
464
|
return
|
|
426
465
|
end
|
|
427
466
|
|
|
428
|
-
if
|
|
429
|
-
child = children
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
line_to_parent(parent, child)
|
|
443
|
-
end
|
|
444
|
-
when "nothing", "none"
|
|
445
|
-
if forced
|
|
446
|
-
triangle_to_parent(parent, child)
|
|
447
|
-
elsif ETYPE_LEAF != child.type
|
|
448
|
-
line_to_parent(parent, child)
|
|
449
|
-
end
|
|
450
|
-
end
|
|
451
|
-
else
|
|
452
|
-
children.each do |child|
|
|
453
|
-
line_to_parent(parent, child)
|
|
467
|
+
if through
|
|
468
|
+
child = children.first
|
|
469
|
+
line_to_parent(parent, child) if through == :line
|
|
470
|
+
draw_connector(child.id, (through if child.inserted_joint))
|
|
471
|
+
return
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
chosen = children.map do |child|
|
|
475
|
+
joints, target = past_inserted_joints(child)
|
|
476
|
+
connector = connector_for(parent, target, children.size == 1)
|
|
477
|
+
case connector
|
|
478
|
+
when :triangle then triangle_to_parent(parent, target, joints)
|
|
479
|
+
when :line then line_to_parent(parent, child)
|
|
480
|
+
else no_connector(parent, target, joints)
|
|
454
481
|
end
|
|
482
|
+
[child, connector]
|
|
455
483
|
end
|
|
456
484
|
|
|
457
|
-
|
|
458
|
-
|
|
485
|
+
chosen.each { |child, connector| draw_connector(child.id, (connector if child.inserted_joint)) }
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# Leaf alignment puts joints between a node and the child it reaches. They
|
|
489
|
+
# are there to place the child, not to change how it is joined, so the
|
|
490
|
+
# connector is chosen for the node and the child past them and carried
|
|
491
|
+
# through them: a triangle runs from the node down to the words, a line
|
|
492
|
+
# passes through each joint, and where no line is drawn none is drawn at
|
|
493
|
+
# all. Joints written into the input are nodes like any other.
|
|
494
|
+
def past_inserted_joints(child)
|
|
495
|
+
joints = []
|
|
496
|
+
while child.inserted_joint
|
|
497
|
+
joints << child
|
|
498
|
+
child = @element_list.get_id(child.children.first)
|
|
459
499
|
end
|
|
500
|
+
[joints, child]
|
|
460
501
|
end
|
|
461
502
|
|
|
503
|
+
def connector_for(parent, child, only_child)
|
|
504
|
+
return :line unless only_child
|
|
505
|
+
|
|
506
|
+
forced = triangle_asked_for?(parent, child)
|
|
507
|
+
case @leafstyle
|
|
508
|
+
when "auto"
|
|
509
|
+
forced || child.contains_phrase ? :triangle : :line
|
|
510
|
+
when "bar"
|
|
511
|
+
forced ? :triangle : :line
|
|
512
|
+
when "nothing", "none"
|
|
513
|
+
if forced
|
|
514
|
+
:triangle
|
|
515
|
+
elsif ETYPE_LEAF != child.type
|
|
516
|
+
:line
|
|
517
|
+
else
|
|
518
|
+
:none
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
# A child joined to its parent by nothing at all: no mark to draw.
|
|
524
|
+
def no_connector(_parent, _child, _joints = []); end
|
|
525
|
+
|
|
462
526
|
def get_leftmost(id = 1)
|
|
463
527
|
target = @element_list.get_id(id)
|
|
464
528
|
target_indent = target.horizontal_indent
|
data/lib/rsyntaxtree/element.rb
CHANGED
|
@@ -13,7 +13,7 @@ require_relative "color_names"
|
|
|
13
13
|
|
|
14
14
|
module RSyntaxTree
|
|
15
15
|
class Element
|
|
16
|
-
attr_accessor :ink_top, :ink_bottom, :rule_name, :label_with_rule_name, :id, :parent, :type, :level, :width, :height, :content, :content_width, :text_width, :content_height, :horizontal_indent, :vertical_indent, :triangle, :enclosure, :children, :font, :fontsize, :contains_phrase, :path, :color, :raw_content, :region, :region_color
|
|
16
|
+
attr_accessor :ink_top, :ink_bottom, :rule_name, :label_with_rule_name, :id, :parent, :type, :level, :width, :height, :content, :content_width, :text_width, :content_height, :horizontal_indent, :vertical_indent, :triangle, :enclosure, :children, :font, :fontsize, :contains_phrase, :path, :color, :raw_content, :region, :region_color, :inserted_joint
|
|
17
17
|
|
|
18
18
|
# names_a_rule says the content is a mother's label rather than a leaf's
|
|
19
19
|
# text. Only a mother has a step under it for a name to sit beside, and a
|
|
@@ -99,5 +99,100 @@ module RSyntaxTree
|
|
|
99
99
|
end
|
|
100
100
|
maxlevel + 1
|
|
101
101
|
end
|
|
102
|
+
|
|
103
|
+
# Add invisible `<>` joints to put every leaf at the deepest leaf level.
|
|
104
|
+
def align_leaves(mode, fontset, fontsize, global)
|
|
105
|
+
leaves = @elements.select { |e| e.type == ETYPE_LEAF && e.children.empty? }
|
|
106
|
+
return if leaves.size < 2
|
|
107
|
+
|
|
108
|
+
next_id = @elements.map(&:id).max + 1
|
|
109
|
+
|
|
110
|
+
if mode == "subtrees"
|
|
111
|
+
root = @elements.find { |element| element.parent.zero? }
|
|
112
|
+
add_high_joints(root, next_id, fontset, fontsize, global) if root
|
|
113
|
+
else
|
|
114
|
+
deepest = leaves.map(&:level).max
|
|
115
|
+
|
|
116
|
+
leaves.each do |leaf|
|
|
117
|
+
joints = deepest - leaf.level
|
|
118
|
+
next unless joints.positive?
|
|
119
|
+
|
|
120
|
+
next_id = insert_empty_joints(leaf, joints, next_id, fontset, fontsize, global)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
renumber_preorder
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def add_high_joints(parent, next_id, fontset, fontsize, global)
|
|
130
|
+
children = parent.children.map { |id| get_id(id) }
|
|
131
|
+
heights = children.map { |child| subtree_height(child) }
|
|
132
|
+
deepest = heights.max
|
|
133
|
+
|
|
134
|
+
children.zip(heights).each do |child, height|
|
|
135
|
+
joints = deepest - height
|
|
136
|
+
if joints.positive?
|
|
137
|
+
next_id = insert_empty_joints(child, joints, next_id, fontset, fontsize, global)
|
|
138
|
+
shift_subtree_levels(child, joints)
|
|
139
|
+
end
|
|
140
|
+
next_id = add_high_joints(child, next_id, fontset, fontsize, global) unless child.children.empty?
|
|
141
|
+
end
|
|
142
|
+
next_id
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def subtree_height(element)
|
|
146
|
+
return 0 if element.children.empty?
|
|
147
|
+
|
|
148
|
+
1 + element.children.map { |id| subtree_height(get_id(id)) }.max
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def insert_empty_joints(child, count, next_id, fontset, fontsize, global)
|
|
152
|
+
parent = get_id(child.parent)
|
|
153
|
+
return next_id unless parent
|
|
154
|
+
|
|
155
|
+
count.times do
|
|
156
|
+
joint = Element.new(next_id, parent.id, WHITESPACE_BLOCK, parent.level + 1, fontset, fontsize, global)
|
|
157
|
+
joint.inserted_joint = true
|
|
158
|
+
next_id += 1
|
|
159
|
+
@elements << joint
|
|
160
|
+
insert_between(parent, child, joint)
|
|
161
|
+
parent = joint
|
|
162
|
+
end
|
|
163
|
+
next_id
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def shift_subtree_levels(element, count)
|
|
167
|
+
element.level += count
|
|
168
|
+
element.children.each { |id| shift_subtree_levels(get_id(id), count) }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def insert_between(parent, child, joint)
|
|
172
|
+
parent.children[parent.children.index(child.id)] = joint.id
|
|
173
|
+
joint.add_child(child.id)
|
|
174
|
+
joint.type = ETYPE_NODE
|
|
175
|
+
child.parent = joint.id
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def renumber_preorder
|
|
179
|
+
root = @elements.find { |element| element.parent.zero? }
|
|
180
|
+
return unless root
|
|
181
|
+
|
|
182
|
+
ordered = []
|
|
183
|
+
visit = lambda do |element|
|
|
184
|
+
ordered << element
|
|
185
|
+
element.children.each { |id| visit.call(get_id(id)) }
|
|
186
|
+
end
|
|
187
|
+
visit.call(root)
|
|
188
|
+
|
|
189
|
+
ids = ordered.each_with_index.to_h { |element, index| [element.id, index + 1] }
|
|
190
|
+
ordered.each do |element|
|
|
191
|
+
element.parent = ids.fetch(element.parent, 0)
|
|
192
|
+
element.children = element.children.map { |id| ids.fetch(id) }
|
|
193
|
+
element.id = ids.fetch(element.id)
|
|
194
|
+
end
|
|
195
|
+
@elements = ordered
|
|
196
|
+
end
|
|
102
197
|
end
|
|
103
198
|
end
|
|
@@ -60,58 +60,32 @@ module RSyntaxTree
|
|
|
60
60
|
@edges << { from: parent.id, to: child.id, type: "dominance", connector: "line" }
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
# A triangle spans from the parent to its child, through any joints leaf
|
|
64
|
+
# alignment put between them; each edge it crosses is recorded as part of
|
|
65
|
+
# it, so the joints keep their place in the tree.
|
|
66
|
+
def triangle_to_parent(parent, child, joints = [])
|
|
67
|
+
chain_edges(parent, joints, child, "triangle")
|
|
65
68
|
end
|
|
66
69
|
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
# A derivation joins a node to its daughters with one rule across all of
|
|
73
|
-
# them. Recorded as what it is: this format states that it carries how
|
|
74
|
-
# the figure was drawn, so calling these lines would be a false record.
|
|
75
|
-
if @derivation && !children.empty?
|
|
76
|
-
children.each do |child|
|
|
77
|
-
@edges << { from: parent.id, to: child.id, type: "dominance", connector: "rule" }
|
|
78
|
-
end
|
|
79
|
-
parent.children.each { |c| draw_connector(c) }
|
|
80
|
-
return
|
|
81
|
-
end
|
|
70
|
+
# Recorded rather than left out: this format carries how the figure was
|
|
71
|
+
# drawn, and a child with no mark under it is part of that.
|
|
72
|
+
def no_connector(parent, child, joints = [])
|
|
73
|
+
chain_edges(parent, joints, child, "none")
|
|
74
|
+
end
|
|
82
75
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
triangle_to_parent(parent, child)
|
|
90
|
-
else
|
|
91
|
-
line_to_parent(parent, child)
|
|
92
|
-
end
|
|
93
|
-
when "bar"
|
|
94
|
-
if forced
|
|
95
|
-
triangle_to_parent(parent, child)
|
|
96
|
-
else
|
|
97
|
-
line_to_parent(parent, child)
|
|
98
|
-
end
|
|
99
|
-
when "nothing", "none"
|
|
100
|
-
if forced
|
|
101
|
-
triangle_to_parent(parent, child)
|
|
102
|
-
elsif ETYPE_LEAF != child.type
|
|
103
|
-
line_to_parent(parent, child)
|
|
104
|
-
else
|
|
105
|
-
@edges << { from: parent.id, to: child.id, type: "dominance", connector: "none" }
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
else
|
|
109
|
-
children.each do |child|
|
|
110
|
-
line_to_parent(parent, child)
|
|
111
|
-
end
|
|
76
|
+
# A derivation joins a node to its daughters with one rule across all of
|
|
77
|
+
# them. Recorded as what it is: this format states that it carries how
|
|
78
|
+
# the figure was drawn, so calling these lines would be a false record.
|
|
79
|
+
def rule_to_parent(parent, children)
|
|
80
|
+
children.each do |child|
|
|
81
|
+
@edges << { from: parent.id, to: child.id, type: "dominance", connector: "rule" }
|
|
112
82
|
end
|
|
83
|
+
end
|
|
113
84
|
|
|
114
|
-
|
|
85
|
+
def chain_edges(parent, joints, child, connector)
|
|
86
|
+
([parent] + joints).zip(joints + [child]).each do |from, to|
|
|
87
|
+
@edges << { from: from.id, to: to.id, type: "dominance", connector: connector }
|
|
88
|
+
end
|
|
115
89
|
end
|
|
116
90
|
|
|
117
91
|
def draw_paths
|
|
@@ -331,6 +305,7 @@ module RSyntaxTree
|
|
|
331
305
|
# before 2.0 carry a separate symmetrize flag beside it; the
|
|
332
306
|
# scale is what it meant, and it is all that is written now.
|
|
333
307
|
tidy: @params[:tidy] || "off",
|
|
308
|
+
leaf_align: @params[:leaf_align],
|
|
334
309
|
mirror: @params[:mirror] == true,
|
|
335
310
|
direction: @params[:direction] || "ttb",
|
|
336
311
|
# Degrees, positive leaning the top right. The geometry below
|
|
@@ -107,6 +107,7 @@ Options (the command line spells them --like-this):
|
|
|
107
107
|
| vheight | how far apart levels sit, 0.5–5.0 |
|
|
108
108
|
| polyline | draw each connector as two right angles rather than a slanted line |
|
|
109
109
|
| hide_default_connectors | draw no parent-to-child lines, leaving only the ones written with +-n — how a lattice or a network is drawn |
|
|
110
|
+
| leaf_align | line the leaves up on one row: off, words (lower the words) or subtrees (lower whole subtrees) |
|
|
110
111
|
| transparent | leave the background clear instead of painting it white |
|
|
111
112
|
| derivation | one rule across the daughters, categorial-grammar style |
|
|
112
113
|
| hyphen | markup (hyphens underline) or literal (hyphens are hyphens) |
|