elkrb 1.0.0 → 2.0.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.adoc +364 -0
- data/LICENSE +25 -0
- data/README.adoc +695 -557
- data/exe/elkrb +36 -2
- data/lib/elkrb/atomic_destination.rb +53 -0
- data/lib/elkrb/best_effort_write.rb +49 -0
- data/lib/elkrb/cli/layout_flag_options.rb +54 -0
- data/lib/elkrb/cli.rb +155 -82
- data/lib/elkrb/command_resolver.rb +186 -0
- data/lib/elkrb/commands/batch_command.rb +28 -5
- data/lib/elkrb/commands/convert_command.rb +22 -52
- data/lib/elkrb/commands/diagram_command.rb +234 -102
- data/lib/elkrb/commands/options_command.rb +87 -0
- data/lib/elkrb/commands/render_command.rb +6 -1
- data/lib/elkrb/commands/validate_command.rb +146 -91
- data/lib/elkrb/errors.rb +20 -1
- data/lib/elkrb/format_sniffer.rb +362 -0
- data/lib/elkrb/geometry/bezier.rb +14 -4
- data/lib/elkrb/geometry/point.rb +5 -6
- data/lib/elkrb/geometry/rectangle.rb +2 -2
- data/lib/elkrb/graph/deep_stringify_keys.rb +30 -0
- data/lib/elkrb/graph/edge.rb +89 -3
- data/lib/elkrb/graph/graph.rb +43 -15
- data/lib/elkrb/graph/label.rb +16 -2
- data/lib/elkrb/graph/node.rb +13 -2
- data/lib/elkrb/graph/node_constraints.rb +139 -8
- data/lib/elkrb/graph/normalize_option_keys.rb +39 -0
- data/lib/elkrb/graph/port.rb +29 -21
- data/lib/elkrb/graph/read_only_mapping.rb +21 -0
- data/lib/elkrb/graphviz_wrapper.rb +152 -44
- data/lib/elkrb/layout/algorithm_registry.rb +65 -8
- data/lib/elkrb/layout/algorithms/base_algorithm.rb +74 -74
- data/lib/elkrb/layout/algorithms/box.rb +47 -20
- data/lib/elkrb/layout/algorithms/disco.rb +150 -61
- data/lib/elkrb/layout/algorithms/fixed.rb +70 -10
- data/lib/elkrb/layout/algorithms/force.rb +145 -86
- data/lib/elkrb/layout/algorithms/layered/bk/aligner.rb +104 -0
- data/lib/elkrb/layout/algorithms/layered/bk/compactor.rb +87 -0
- data/lib/elkrb/layout/algorithms/layered/bk/view.rb +103 -0
- data/lib/elkrb/layout/algorithms/layered/bk_node_placer.rb +170 -0
- data/lib/elkrb/layout/algorithms/layered/components.rb +170 -0
- data/lib/elkrb/layout/algorithms/layered/crossing_minimizer.rb +306 -0
- data/lib/elkrb/layout/algorithms/layered/cycle_breaker.rb +118 -49
- data/lib/elkrb/layout/algorithms/layered/discovery_order.rb +75 -0
- data/lib/elkrb/layout/algorithms/layered/dummy_slot.rb +31 -0
- data/lib/elkrb/layout/algorithms/layered/layer_assigner.rb +188 -48
- data/lib/elkrb/layout/algorithms/layered/layer_constraints.rb +82 -0
- data/lib/elkrb/layout/algorithms/layered/layer_sweep.rb +195 -0
- data/lib/elkrb/layout/algorithms/layered/node_placer.rb +214 -36
- data/lib/elkrb/layout/algorithms/layered/orthogonal/gap_solver.rb +219 -0
- data/lib/elkrb/layout/algorithms/layered/orthogonal/hyper_edge_segment.rb +152 -0
- data/lib/elkrb/layout/algorithms/layered/orthogonal/junction_points.rb +58 -0
- data/lib/elkrb/layout/algorithms/layered/orthogonal/segment_order.rb +134 -0
- data/lib/elkrb/layout/algorithms/layered/orthogonal_router.rb +253 -0
- data/lib/elkrb/layout/algorithms/layered/port_order.rb +188 -0
- data/lib/elkrb/layout/algorithms/layered/port_spread.rb +42 -0
- data/lib/elkrb/layout/algorithms/layered.rb +548 -13
- data/lib/elkrb/layout/algorithms/libavoid.rb +951 -187
- data/lib/elkrb/layout/algorithms/mrtree.rb +585 -65
- data/lib/elkrb/layout/algorithms/radial.rb +188 -29
- data/lib/elkrb/layout/algorithms/random.rb +131 -17
- data/lib/elkrb/layout/algorithms/rectpacking.rb +28 -40
- data/lib/elkrb/layout/algorithms/spore_compaction.rb +57 -100
- data/lib/elkrb/layout/algorithms/spore_overlap.rb +78 -84
- data/lib/elkrb/layout/algorithms/stress.rb +391 -84
- data/lib/elkrb/layout/algorithms/topdown_packing.rb +95 -31
- data/lib/elkrb/layout/algorithms/vertiflex.rb +12 -47
- data/lib/elkrb/layout/constraints/alignment_constraint.rb +49 -12
- data/lib/elkrb/layout/constraints/base_constraint.rb +3 -15
- data/lib/elkrb/layout/constraints/constraint_processor.rb +9 -23
- data/lib/elkrb/layout/constraints/fixed_position_constraint.rb +22 -13
- data/lib/elkrb/layout/constraints/layer_constraint.rb +10 -33
- data/lib/elkrb/layout/constraints/relative_position_constraint.rb +94 -50
- data/lib/elkrb/layout/edge_router.rb +533 -337
- data/lib/elkrb/layout/hierarchical_processor.rb +19 -276
- data/lib/elkrb/layout/java_random.rb +59 -0
- data/lib/elkrb/layout/label_placer.rb +165 -157
- data/lib/elkrb/layout/layout_engine.rb +46 -23
- data/lib/elkrb/layout/node_index.rb +149 -0
- data/lib/elkrb/layout/polyomino/component_compactor.rb +138 -0
- data/lib/elkrb/layout/polyomino/footprint.rb +94 -0
- data/lib/elkrb/layout/polyomino/grid.rb +106 -0
- data/lib/elkrb/layout/polyomino/packer.rb +95 -0
- data/lib/elkrb/layout/polyomino/piece.rb +52 -0
- data/lib/elkrb/layout/port_constraint_processor.rb +131 -44
- data/lib/elkrb/layout/spore/body.rb +65 -0
- data/lib/elkrb/layout/spore/depth_first_compaction.rb +127 -0
- data/lib/elkrb/layout/spore/edge_cost.rb +69 -0
- data/lib/elkrb/layout/spore/gap.rb +76 -0
- data/lib/elkrb/layout/spore/geometry.rb +104 -0
- data/lib/elkrb/layout/spore/overlap_sweep.rb +90 -0
- data/lib/elkrb/layout/spore/placement.rb +57 -0
- data/lib/elkrb/layout/spore/rays.rb +119 -0
- data/lib/elkrb/layout/spore/spanning_tree.rb +53 -0
- data/lib/elkrb/layout/spore/triangulation.rb +159 -0
- data/lib/elkrb/layout_flags.rb +69 -0
- data/lib/elkrb/options/decimal.rb +50 -0
- data/lib/elkrb/options/element_options.rb +43 -0
- data/lib/elkrb/options/elk_padding.rb +20 -14
- data/lib/elkrb/options/k_vector.rb +6 -7
- data/lib/elkrb/options/k_vector_chain.rb +12 -20
- data/lib/elkrb/options/option_map.rb +85 -0
- data/lib/elkrb/options/registry.rb +335 -0
- data/lib/elkrb/options/resolver.rb +151 -0
- data/lib/elkrb/options/spellings.rb +62 -0
- data/lib/elkrb/options/unhonoured_report.rb +194 -0
- data/lib/elkrb/parsers/elkt/lexer.rb +309 -0
- data/lib/elkrb/parsers/elkt/parser.rb +424 -0
- data/lib/elkrb/parsers/elkt/resolver.rb +142 -0
- data/lib/elkrb/parsers/elkt/token.rb +40 -0
- data/lib/elkrb/parsers/elkt_parser.rb +32 -230
- data/lib/elkrb/serializers/dot_serializer.rb +342 -54
- data/lib/elkrb/serializers/elkt_serializer.rb +386 -155
- data/lib/elkrb/version.rb +1 -1
- data/lib/elkrb.rb +60 -61
- metadata +71 -49
- data/.rspec +0 -3
- data/.rubocop.yml +0 -11
- data/Gemfile +0 -13
- data/Rakefile +0 -64
- data/benchmarks/README.md +0 -172
- data/benchmarks/elkjs_benchmark.js +0 -140
- data/benchmarks/elkrb_benchmark.rb +0 -145
- data/benchmarks/fixtures/graphs.json +0 -10777
- data/benchmarks/generate_report.rb +0 -241
- data/benchmarks/generate_test_graphs.rb +0 -154
- data/benchmarks/results/elkrb_results.json +0 -280
- data/benchmarks/results/elkrb_summary.json +0 -285
- data/elkrb.gemspec +0 -39
- data/examples/dot_export_demo.rb +0 -133
- data/examples/hierarchical_graph.rb +0 -19
- data/examples/layout_constraints_demo.rb +0 -272
- data/examples/port_constraints_demo.rb +0 -291
- data/examples/self_loop_demo.rb +0 -391
- data/examples/simple_graph.rb +0 -50
- data/examples/spline_routing_demo.rb +0 -235
- data/lib/elkrb/graph/layout_options.rb +0 -247
- data/sig/elkrb/constraints.rbs +0 -114
- data/sig/elkrb/geometry.rbs +0 -61
- data/sig/elkrb/graph.rbs +0 -112
- data/sig/elkrb/layout.rbs +0 -107
- data/sig/elkrb/options.rbs +0 -81
- data/sig/elkrb.rbs +0 -32
|
@@ -21,25 +21,18 @@ module Elkrb
|
|
|
21
21
|
class RelativePositionConstraint < BaseConstraint
|
|
22
22
|
# Apply relative position constraint
|
|
23
23
|
#
|
|
24
|
-
# Positions nodes relative to their reference nodes
|
|
25
|
-
#
|
|
24
|
+
# Positions nodes relative to their reference nodes, a node only
|
|
25
|
+
# after the node it is relative to. Among nodes whose references are
|
|
26
|
+
# settled, higher position_priority goes first, then input order.
|
|
27
|
+
# Nodes on a relative_to cycle, and nodes whose reference is missing
|
|
28
|
+
# or has no coordinates, are left where they are; #validate reports
|
|
29
|
+
# them.
|
|
26
30
|
#
|
|
27
31
|
# @param graph [Graph::Graph] The graph
|
|
28
32
|
# @return [Graph::Graph] The modified graph
|
|
29
33
|
def apply(graph)
|
|
30
|
-
|
|
31
|
-
node
|
|
32
|
-
node.constraints.relative_offset
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Sort by priority (higher priority processed first)
|
|
36
|
-
nodes_with_relative.sort_by! do |node|
|
|
37
|
-
-(node.constraints.position_priority || 0)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Apply relative positioning
|
|
41
|
-
nodes_with_relative.each do |node|
|
|
42
|
-
apply_relative_position(node, graph)
|
|
34
|
+
resolution_order(graph).each do |node|
|
|
35
|
+
position_relative(node, graph)
|
|
43
36
|
end
|
|
44
37
|
|
|
45
38
|
graph
|
|
@@ -47,63 +40,114 @@ module Elkrb
|
|
|
47
40
|
|
|
48
41
|
# Validate relative position constraints
|
|
49
42
|
#
|
|
50
|
-
# Checks that reference nodes exist
|
|
43
|
+
# Checks that reference nodes exist, that relative_to does not form a
|
|
44
|
+
# cycle, and that positions are correct.
|
|
51
45
|
#
|
|
52
46
|
# @param graph [Graph::Graph] The graph to validate
|
|
53
47
|
# @return [Array<String>] List of validation errors
|
|
54
48
|
def validate(graph)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
all_nodes(graph).each do |node|
|
|
58
|
-
next unless node.constraints&.relative_to
|
|
49
|
+
cyclic = cyclic_nodes(graph)
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
all_nodes(graph).flat_map do |node|
|
|
52
|
+
next [] unless node.constraints&.relative_to
|
|
62
53
|
|
|
63
|
-
if
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Validate position if offset is specified
|
|
70
|
-
if node.constraints.relative_offset
|
|
71
|
-
expected_x = ref_node.x + node.constraints.relative_offset.x
|
|
72
|
-
expected_y = ref_node.y + node.constraints.relative_offset.y
|
|
73
|
-
|
|
74
|
-
tolerance = 0.01
|
|
75
|
-
if (node.x - expected_x).abs > tolerance ||
|
|
76
|
-
(node.y - expected_y).abs > tolerance
|
|
77
|
-
errors << "Node '#{node.id}' relative position incorrect. " \
|
|
78
|
-
"Expected (#{expected_x}, #{expected_y}), " \
|
|
79
|
-
"got (#{node.x}, #{node.y})"
|
|
80
|
-
end
|
|
54
|
+
if cyclic.any? { |member| member.equal?(node) }
|
|
55
|
+
["Node '#{node.id}' is on, or depends on, a relative_to cycle"]
|
|
56
|
+
else
|
|
57
|
+
check_relative_node(node, graph)
|
|
81
58
|
end
|
|
82
59
|
end
|
|
83
|
-
|
|
84
|
-
errors
|
|
85
60
|
end
|
|
86
61
|
|
|
87
62
|
private
|
|
88
63
|
|
|
89
|
-
|
|
90
|
-
def apply_relative_position(node, graph)
|
|
64
|
+
def check_relative_node(node, graph)
|
|
91
65
|
ref_id = node.constraints.relative_to
|
|
92
66
|
ref_node = find_node(graph, ref_id)
|
|
93
67
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return
|
|
68
|
+
if ref_node.nil?
|
|
69
|
+
return ["Node '#{node.id}' has relative_to constraint " \
|
|
70
|
+
"referencing '#{ref_id}' which doesn't exist"]
|
|
98
71
|
end
|
|
99
72
|
|
|
100
73
|
offset = node.constraints.relative_offset
|
|
101
|
-
return unless offset
|
|
74
|
+
return [] unless offset
|
|
75
|
+
|
|
76
|
+
unless coordinates?(node) && coordinates?(ref_node)
|
|
77
|
+
return ["Node '#{node.id}' relative position cannot be checked: " \
|
|
78
|
+
"'#{node.id}' or '#{ref_id}' has no coordinates"]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
expected_x = ref_node.x + offset.x
|
|
82
|
+
expected_y = ref_node.y + offset.y
|
|
83
|
+
return [] if within_tolerance?(node, expected_x, expected_y)
|
|
84
|
+
|
|
85
|
+
["Node '#{node.id}' relative position incorrect. " \
|
|
86
|
+
"Expected (#{expected_x}, #{expected_y}), " \
|
|
87
|
+
"got (#{node.x}, #{node.y})"]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def within_tolerance?(node, expected_x, expected_y)
|
|
91
|
+
tolerance = 0.01
|
|
92
|
+
(node.x - expected_x).abs <= tolerance &&
|
|
93
|
+
(node.y - expected_y).abs <= tolerance
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def coordinates?(node)
|
|
97
|
+
!node.x.nil? && !node.y.nil?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Apply relative position to a single node
|
|
101
|
+
def position_relative(node, graph)
|
|
102
|
+
ref_node = find_node(graph, node.constraints.relative_to)
|
|
103
|
+
offset = node.constraints.relative_offset
|
|
104
|
+
return unless ref_node && offset && coordinates?(ref_node)
|
|
102
105
|
|
|
103
|
-
# Calculate and set new position
|
|
104
106
|
node.x = ref_node.x + offset.x
|
|
105
107
|
node.y = ref_node.y + offset.y
|
|
106
108
|
end
|
|
109
|
+
|
|
110
|
+
# Nodes to position, each after the node it is relative to.
|
|
111
|
+
def resolution_order(graph)
|
|
112
|
+
pending = positioned_nodes(graph)
|
|
113
|
+
order = []
|
|
114
|
+
|
|
115
|
+
until pending.empty?
|
|
116
|
+
ready = pending.select { |node| ready?(node, graph, pending) }
|
|
117
|
+
break if ready.empty?
|
|
118
|
+
|
|
119
|
+
node = ready.min_by do |candidate|
|
|
120
|
+
[-(candidate.constraints.position_priority || 0),
|
|
121
|
+
pending.index { |p| p.equal?(candidate) }]
|
|
122
|
+
end
|
|
123
|
+
pending.delete_if { |p| p.equal?(node) }
|
|
124
|
+
order << node
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
order
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# A node is ready when the node it is relative to is not itself
|
|
131
|
+
# still waiting to be positioned.
|
|
132
|
+
def ready?(node, graph, pending)
|
|
133
|
+
ref_node = find_node(graph, node.constraints.relative_to)
|
|
134
|
+
ref_node.nil? || pending.none? { |p| p.equal?(ref_node) }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def positioned_nodes(graph)
|
|
138
|
+
all_nodes(graph).select do |node|
|
|
139
|
+
node.constraints&.relative_to && node.constraints.relative_offset
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Nodes that can never be ready because relative_to leads back to
|
|
144
|
+
# them.
|
|
145
|
+
def cyclic_nodes(graph)
|
|
146
|
+
ordered = resolution_order(graph)
|
|
147
|
+
positioned_nodes(graph).reject do |node|
|
|
148
|
+
ordered.any? { |placed| placed.equal?(node) }
|
|
149
|
+
end
|
|
150
|
+
end
|
|
107
151
|
end
|
|
108
152
|
end
|
|
109
153
|
end
|