elkrb 1.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +11 -0
- data/Gemfile +13 -0
- data/README.adoc +1028 -0
- data/Rakefile +64 -0
- data/benchmarks/README.md +172 -0
- data/benchmarks/elkjs_benchmark.js +140 -0
- data/benchmarks/elkrb_benchmark.rb +145 -0
- data/benchmarks/fixtures/graphs.json +10777 -0
- data/benchmarks/generate_report.rb +241 -0
- data/benchmarks/generate_test_graphs.rb +154 -0
- data/benchmarks/results/elkrb_results.json +280 -0
- data/benchmarks/results/elkrb_summary.json +285 -0
- data/elkrb.gemspec +39 -0
- data/examples/dot_export_demo.rb +133 -0
- data/examples/hierarchical_graph.rb +19 -0
- data/examples/layout_constraints_demo.rb +272 -0
- data/examples/port_constraints_demo.rb +291 -0
- data/examples/self_loop_demo.rb +391 -0
- data/examples/simple_graph.rb +50 -0
- data/examples/spline_routing_demo.rb +235 -0
- data/exe/elkrb +8 -0
- data/lib/elkrb/cli.rb +224 -0
- data/lib/elkrb/commands/batch_command.rb +66 -0
- data/lib/elkrb/commands/convert_command.rb +130 -0
- data/lib/elkrb/commands/diagram_command.rb +208 -0
- data/lib/elkrb/commands/render_command.rb +52 -0
- data/lib/elkrb/commands/validate_command.rb +241 -0
- data/lib/elkrb/errors.rb +30 -0
- data/lib/elkrb/geometry/bezier.rb +163 -0
- data/lib/elkrb/geometry/dimension.rb +32 -0
- data/lib/elkrb/geometry/point.rb +68 -0
- data/lib/elkrb/geometry/rectangle.rb +86 -0
- data/lib/elkrb/geometry/vector.rb +67 -0
- data/lib/elkrb/graph/edge.rb +95 -0
- data/lib/elkrb/graph/graph.rb +90 -0
- data/lib/elkrb/graph/label.rb +45 -0
- data/lib/elkrb/graph/layout_options.rb +247 -0
- data/lib/elkrb/graph/node.rb +79 -0
- data/lib/elkrb/graph/node_constraints.rb +107 -0
- data/lib/elkrb/graph/port.rb +104 -0
- data/lib/elkrb/graphviz_wrapper.rb +133 -0
- data/lib/elkrb/layout/algorithm_registry.rb +57 -0
- data/lib/elkrb/layout/algorithms/base_algorithm.rb +208 -0
- data/lib/elkrb/layout/algorithms/box.rb +47 -0
- data/lib/elkrb/layout/algorithms/disco.rb +206 -0
- data/lib/elkrb/layout/algorithms/fixed.rb +32 -0
- data/lib/elkrb/layout/algorithms/force.rb +165 -0
- data/lib/elkrb/layout/algorithms/layered/cycle_breaker.rb +86 -0
- data/lib/elkrb/layout/algorithms/layered/layer_assigner.rb +96 -0
- data/lib/elkrb/layout/algorithms/layered/node_placer.rb +77 -0
- data/lib/elkrb/layout/algorithms/layered.rb +49 -0
- data/lib/elkrb/layout/algorithms/libavoid.rb +389 -0
- data/lib/elkrb/layout/algorithms/mrtree.rb +144 -0
- data/lib/elkrb/layout/algorithms/radial.rb +64 -0
- data/lib/elkrb/layout/algorithms/random.rb +43 -0
- data/lib/elkrb/layout/algorithms/rectpacking.rb +93 -0
- data/lib/elkrb/layout/algorithms/spore_compaction.rb +139 -0
- data/lib/elkrb/layout/algorithms/spore_overlap.rb +117 -0
- data/lib/elkrb/layout/algorithms/stress.rb +176 -0
- data/lib/elkrb/layout/algorithms/topdown_packing.rb +183 -0
- data/lib/elkrb/layout/algorithms/vertiflex.rb +174 -0
- data/lib/elkrb/layout/constraints/alignment_constraint.rb +150 -0
- data/lib/elkrb/layout/constraints/base_constraint.rb +72 -0
- data/lib/elkrb/layout/constraints/constraint_processor.rb +134 -0
- data/lib/elkrb/layout/constraints/fixed_position_constraint.rb +87 -0
- data/lib/elkrb/layout/constraints/layer_constraint.rb +71 -0
- data/lib/elkrb/layout/constraints/relative_position_constraint.rb +110 -0
- data/lib/elkrb/layout/edge_router.rb +935 -0
- data/lib/elkrb/layout/hierarchical_processor.rb +299 -0
- data/lib/elkrb/layout/label_placer.rb +338 -0
- data/lib/elkrb/layout/layout_engine.rb +170 -0
- data/lib/elkrb/layout/port_constraint_processor.rb +173 -0
- data/lib/elkrb/options/elk_padding.rb +94 -0
- data/lib/elkrb/options/k_vector.rb +100 -0
- data/lib/elkrb/options/k_vector_chain.rb +135 -0
- data/lib/elkrb/parsers/elkt_parser.rb +248 -0
- data/lib/elkrb/serializers/dot_serializer.rb +339 -0
- data/lib/elkrb/serializers/elkt_serializer.rb +236 -0
- data/lib/elkrb/version.rb +5 -0
- data/lib/elkrb.rb +509 -0
- data/sig/elkrb/constraints.rbs +114 -0
- data/sig/elkrb/geometry.rbs +61 -0
- data/sig/elkrb/graph.rbs +112 -0
- data/sig/elkrb/layout.rbs +107 -0
- data/sig/elkrb/options.rbs +81 -0
- data/sig/elkrb.rbs +32 -0
- metadata +179 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
{
|
|
2
|
+
"timestamp": "2025-10-28T13:39:49+09:00",
|
|
3
|
+
"ruby_version": "3.3.2",
|
|
4
|
+
"elkrb_version": "0.4.3",
|
|
5
|
+
"results": {
|
|
6
|
+
"small_simple": {
|
|
7
|
+
"layered": {
|
|
8
|
+
"avg": 7.433500001206994,
|
|
9
|
+
"min": 6.854000006569549,
|
|
10
|
+
"max": 8.520000003045425,
|
|
11
|
+
"memory": 2368
|
|
12
|
+
},
|
|
13
|
+
"force": {
|
|
14
|
+
"avg": 127.22110000177054,
|
|
15
|
+
"min": 49.12799999874551,
|
|
16
|
+
"max": 782.6429999986431,
|
|
17
|
+
"memory": 1856
|
|
18
|
+
},
|
|
19
|
+
"stress": {
|
|
20
|
+
"avg": 18.048900002031587,
|
|
21
|
+
"min": 15.010000002803281,
|
|
22
|
+
"max": 23.14100001240149,
|
|
23
|
+
"memory": 288
|
|
24
|
+
},
|
|
25
|
+
"box": {
|
|
26
|
+
"avg": 6.4161000031162985,
|
|
27
|
+
"min": 5.8969999954570085,
|
|
28
|
+
"max": 6.9420000072568655,
|
|
29
|
+
"memory": 448
|
|
30
|
+
},
|
|
31
|
+
"random": {
|
|
32
|
+
"avg": 38.642400002572685,
|
|
33
|
+
"min": 6.706000000122003,
|
|
34
|
+
"max": 132.673000000068,
|
|
35
|
+
"memory": 128
|
|
36
|
+
},
|
|
37
|
+
"fixed": {
|
|
38
|
+
"avg": 15.259099996183068,
|
|
39
|
+
"min": 6.213999993633479,
|
|
40
|
+
"max": 80.09200000378769,
|
|
41
|
+
"memory": 16
|
|
42
|
+
},
|
|
43
|
+
"mrtree": {
|
|
44
|
+
"error": "Stack overflow (graph has cycles)"
|
|
45
|
+
},
|
|
46
|
+
"radial": {
|
|
47
|
+
"avg": 6.609599998046178,
|
|
48
|
+
"min": 5.982999995467253,
|
|
49
|
+
"max": 7.303000005776994,
|
|
50
|
+
"memory": 176
|
|
51
|
+
},
|
|
52
|
+
"rectpacking": {
|
|
53
|
+
"avg": 75.61830000486225,
|
|
54
|
+
"min": 6.420000005164184,
|
|
55
|
+
"max": 323.69800000742543,
|
|
56
|
+
"memory": 32
|
|
57
|
+
},
|
|
58
|
+
"disco": {
|
|
59
|
+
"avg": 11.099700002523605,
|
|
60
|
+
"min": 7.312999994610436,
|
|
61
|
+
"max": 19.619000013335608,
|
|
62
|
+
"memory": 64
|
|
63
|
+
},
|
|
64
|
+
"sporeOverlap": {
|
|
65
|
+
"error": "Unknown layout algorithm: sporeOverlap"
|
|
66
|
+
},
|
|
67
|
+
"sporeCompaction": {
|
|
68
|
+
"error": "Unknown layout algorithm: sporeCompaction"
|
|
69
|
+
},
|
|
70
|
+
"topdownpacking": {
|
|
71
|
+
"avg": 6.6470000019762665,
|
|
72
|
+
"min": 6.047000002581626,
|
|
73
|
+
"max": 7.645000005140901,
|
|
74
|
+
"memory": 192
|
|
75
|
+
},
|
|
76
|
+
"libavoid": {
|
|
77
|
+
"avg": 71.8007999996189,
|
|
78
|
+
"min": 29.284000003826804,
|
|
79
|
+
"max": 183.2050000084564,
|
|
80
|
+
"memory": 16
|
|
81
|
+
},
|
|
82
|
+
"vertiflex": {
|
|
83
|
+
"avg": 6.495000002905726,
|
|
84
|
+
"min": 6.106000000727363,
|
|
85
|
+
"max": 7.274999996297993,
|
|
86
|
+
"memory": 0
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"medium_hierarchical": {
|
|
90
|
+
"layered": {
|
|
91
|
+
"error": "nil can't be coerced into Float"
|
|
92
|
+
},
|
|
93
|
+
"force": {
|
|
94
|
+
"error": "undefined method `+' for nil"
|
|
95
|
+
},
|
|
96
|
+
"stress": {
|
|
97
|
+
"error": "nil can't be coerced into Float"
|
|
98
|
+
},
|
|
99
|
+
"box": {
|
|
100
|
+
"error": "undefined method `+' for nil"
|
|
101
|
+
},
|
|
102
|
+
"random": {
|
|
103
|
+
"error": "undefined method `+' for nil"
|
|
104
|
+
},
|
|
105
|
+
"fixed": {
|
|
106
|
+
"error": "nil can't be coerced into Float"
|
|
107
|
+
},
|
|
108
|
+
"mrtree": {
|
|
109
|
+
"error": "undefined method `+' for nil"
|
|
110
|
+
},
|
|
111
|
+
"radial": {
|
|
112
|
+
"error": "nil can't be coerced into Integer"
|
|
113
|
+
},
|
|
114
|
+
"rectpacking": {
|
|
115
|
+
"error": "undefined method `-@' for nil"
|
|
116
|
+
},
|
|
117
|
+
"disco": {
|
|
118
|
+
"error": "nil can't be coerced into Float"
|
|
119
|
+
},
|
|
120
|
+
"sporeOverlap": {
|
|
121
|
+
"error": "Unknown layout algorithm: sporeOverlap"
|
|
122
|
+
},
|
|
123
|
+
"sporeCompaction": {
|
|
124
|
+
"error": "Unknown layout algorithm: sporeCompaction"
|
|
125
|
+
},
|
|
126
|
+
"topdownpacking": {
|
|
127
|
+
"error": "nil can't be coerced into Integer"
|
|
128
|
+
},
|
|
129
|
+
"libavoid": {
|
|
130
|
+
"error": "undefined method `+' for nil"
|
|
131
|
+
},
|
|
132
|
+
"vertiflex": {
|
|
133
|
+
"error": "undefined method `+' for nil"
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"large_complex": {
|
|
137
|
+
"layered": {
|
|
138
|
+
"avg": 229.20739999826765,
|
|
139
|
+
"min": 216.13099999376573,
|
|
140
|
+
"max": 276.89000000827946,
|
|
141
|
+
"memory": 2448
|
|
142
|
+
},
|
|
143
|
+
"force": {
|
|
144
|
+
"error": "Timeout"
|
|
145
|
+
},
|
|
146
|
+
"stress": {
|
|
147
|
+
"error": "Timeout"
|
|
148
|
+
},
|
|
149
|
+
"box": {
|
|
150
|
+
"avg": 330.15480000176467,
|
|
151
|
+
"min": 149.9130000011064,
|
|
152
|
+
"max": 925.889999998617,
|
|
153
|
+
"memory": 256
|
|
154
|
+
},
|
|
155
|
+
"random": {
|
|
156
|
+
"avg": 211.05680000182474,
|
|
157
|
+
"min": 148.6330000043381,
|
|
158
|
+
"max": 580.1270000083605,
|
|
159
|
+
"memory": 352
|
|
160
|
+
},
|
|
161
|
+
"fixed": {
|
|
162
|
+
"avg": 215.3812000004109,
|
|
163
|
+
"min": 146.1669999989681,
|
|
164
|
+
"max": 722.7609999972628,
|
|
165
|
+
"memory": 1152
|
|
166
|
+
},
|
|
167
|
+
"mrtree": {
|
|
168
|
+
"error": "Stack overflow (graph has cycles)"
|
|
169
|
+
},
|
|
170
|
+
"radial": {
|
|
171
|
+
"avg": 245.07840000005672,
|
|
172
|
+
"min": 145.85999998962507,
|
|
173
|
+
"max": 760.4060000012396,
|
|
174
|
+
"memory": 288
|
|
175
|
+
},
|
|
176
|
+
"rectpacking": {
|
|
177
|
+
"avg": 426.3377000010223,
|
|
178
|
+
"min": 147.25500000349712,
|
|
179
|
+
"max": 1149.898999996367,
|
|
180
|
+
"memory": 160
|
|
181
|
+
},
|
|
182
|
+
"disco": {
|
|
183
|
+
"avg": 347.8637999985949,
|
|
184
|
+
"min": 271.66199999919627,
|
|
185
|
+
"max": 955.4760000028182,
|
|
186
|
+
"memory": 144
|
|
187
|
+
},
|
|
188
|
+
"sporeOverlap": {
|
|
189
|
+
"error": "Unknown layout algorithm: sporeOverlap"
|
|
190
|
+
},
|
|
191
|
+
"sporeCompaction": {
|
|
192
|
+
"error": "Unknown layout algorithm: sporeCompaction"
|
|
193
|
+
},
|
|
194
|
+
"topdownpacking": {
|
|
195
|
+
"avg": 166.12010000244481,
|
|
196
|
+
"min": 149.74500000244007,
|
|
197
|
+
"max": 237.6079999958165,
|
|
198
|
+
"memory": 48
|
|
199
|
+
},
|
|
200
|
+
"libavoid": {
|
|
201
|
+
"error": "Timeout"
|
|
202
|
+
},
|
|
203
|
+
"vertiflex": {
|
|
204
|
+
"avg": 288.4548999980325,
|
|
205
|
+
"min": 162.87099999317434,
|
|
206
|
+
"max": 799.9410000047646,
|
|
207
|
+
"memory": 176
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"dense_network": {
|
|
211
|
+
"layered": {
|
|
212
|
+
"avg": 426.88989999878686,
|
|
213
|
+
"min": 188.48299999081064,
|
|
214
|
+
"max": 798.5009999974864,
|
|
215
|
+
"memory": 16
|
|
216
|
+
},
|
|
217
|
+
"force": {
|
|
218
|
+
"error": "Timeout"
|
|
219
|
+
},
|
|
220
|
+
"stress": {
|
|
221
|
+
"error": "Timeout"
|
|
222
|
+
},
|
|
223
|
+
"box": {
|
|
224
|
+
"avg": 368.0181999996421,
|
|
225
|
+
"min": 143.31399998627603,
|
|
226
|
+
"max": 869.9789999955101,
|
|
227
|
+
"memory": 144
|
|
228
|
+
},
|
|
229
|
+
"random": {
|
|
230
|
+
"avg": 259.617500002787,
|
|
231
|
+
"min": 150.44500000658445,
|
|
232
|
+
"max": 697.4970000010217,
|
|
233
|
+
"memory": 16
|
|
234
|
+
},
|
|
235
|
+
"fixed": {
|
|
236
|
+
"avg": 345.68620000063675,
|
|
237
|
+
"min": 148.51800000178628,
|
|
238
|
+
"max": 777.6560000056634,
|
|
239
|
+
"memory": 0
|
|
240
|
+
},
|
|
241
|
+
"mrtree": {
|
|
242
|
+
"error": "Stack overflow (graph has cycles)"
|
|
243
|
+
},
|
|
244
|
+
"radial": {
|
|
245
|
+
"avg": 323.07310000032885,
|
|
246
|
+
"min": 154.51900000334717,
|
|
247
|
+
"max": 1149.6579999948153,
|
|
248
|
+
"memory": 48
|
|
249
|
+
},
|
|
250
|
+
"rectpacking": {
|
|
251
|
+
"avg": 238.30970000271918,
|
|
252
|
+
"min": 154.08700000261888,
|
|
253
|
+
"max": 729.6930000011344,
|
|
254
|
+
"memory": -2864
|
|
255
|
+
},
|
|
256
|
+
"disco": {
|
|
257
|
+
"avg": 294.9923999971361,
|
|
258
|
+
"min": 224.77599998819642,
|
|
259
|
+
"max": 809.6569999906933,
|
|
260
|
+
"memory": 0
|
|
261
|
+
},
|
|
262
|
+
"sporeOverlap": {
|
|
263
|
+
"error": "Unknown layout algorithm: sporeOverlap"
|
|
264
|
+
},
|
|
265
|
+
"sporeCompaction": {
|
|
266
|
+
"error": "Unknown layout algorithm: sporeCompaction"
|
|
267
|
+
},
|
|
268
|
+
"topdownpacking": {
|
|
269
|
+
"avg": 202.70830000081332,
|
|
270
|
+
"min": 149.26500000001397,
|
|
271
|
+
"max": 604.8569999984466,
|
|
272
|
+
"memory": 0
|
|
273
|
+
},
|
|
274
|
+
"libavoid": {
|
|
275
|
+
"error": "Timeout"
|
|
276
|
+
},
|
|
277
|
+
"vertiflex": {
|
|
278
|
+
"avg": 197.8210000001127,
|
|
279
|
+
"min": 148.06800000951625,
|
|
280
|
+
"max": 396.78599999751896,
|
|
281
|
+
"memory": -192
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
data/elkrb.gemspec
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/elkrb/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "elkrb"
|
|
7
|
+
spec.version = Elkrb::VERSION
|
|
8
|
+
spec.authors = ["Ribose Inc."]
|
|
9
|
+
spec.email = ["open.source@ribose.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "ElkRb: Ruby implementation of Eclipse Layout Kernel (ELK)"
|
|
12
|
+
spec.description = <<~HEREDOC
|
|
13
|
+
Pure Ruby implementation of the Eclipse Layout Kernel (ELK) providing automatic
|
|
14
|
+
layout of node-link diagrams. Supports all ELK algorithms.
|
|
15
|
+
HEREDOC
|
|
16
|
+
|
|
17
|
+
spec.homepage = "https://github.com/claricle/elkrb"
|
|
18
|
+
spec.license = "BSD-2-Clause"
|
|
19
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
20
|
+
|
|
21
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
22
|
+
spec.metadata["source_code_uri"] = "https://github.com/claricle/elkrb"
|
|
23
|
+
spec.metadata["changelog_uri"] = "https://github.com/claricle/elkrb"
|
|
24
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
25
|
+
|
|
26
|
+
# Specify which files should be added to the gem when it is released.
|
|
27
|
+
spec.files = Dir.chdir(__dir__) do
|
|
28
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
29
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
spec.bindir = "exe"
|
|
33
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
34
|
+
spec.require_paths = ["lib"]
|
|
35
|
+
|
|
36
|
+
spec.add_dependency "lutaml-model", "~> 0.7"
|
|
37
|
+
spec.add_dependency "rbs", "~> 3.0"
|
|
38
|
+
spec.add_dependency "thor", "~> 1.4"
|
|
39
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../lib/elkrb"
|
|
5
|
+
|
|
6
|
+
# Example 1: Simple graph with layout and DOT export
|
|
7
|
+
puts "Example 1: Simple Graph"
|
|
8
|
+
puts "=" * 50
|
|
9
|
+
|
|
10
|
+
# Build graph using model objects
|
|
11
|
+
simple_graph = Elkrb::Graph::Graph.new(id: "root")
|
|
12
|
+
|
|
13
|
+
node1 = Elkrb::Graph::Node.new(id: "n1", width: 100, height: 60)
|
|
14
|
+
node1.labels = [Elkrb::Graph::Label.new(text: "Node 1")]
|
|
15
|
+
|
|
16
|
+
node2 = Elkrb::Graph::Node.new(id: "n2", width: 100, height: 60)
|
|
17
|
+
node2.labels = [Elkrb::Graph::Label.new(text: "Node 2")]
|
|
18
|
+
|
|
19
|
+
node3 = Elkrb::Graph::Node.new(id: "n3", width: 100, height: 60)
|
|
20
|
+
node3.labels = [Elkrb::Graph::Label.new(text: "Node 3")]
|
|
21
|
+
|
|
22
|
+
simple_graph.children = [node1, node2, node3]
|
|
23
|
+
simple_graph.edges = [
|
|
24
|
+
Elkrb::Graph::Edge.new(id: "e1", sources: ["n1"], targets: ["n2"]),
|
|
25
|
+
Elkrb::Graph::Edge.new(id: "e2", sources: ["n2"], targets: ["n3"]),
|
|
26
|
+
Elkrb::Graph::Edge.new(id: "e3", sources: ["n1"], targets: ["n3"]),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# Layout the graph
|
|
30
|
+
result = Elkrb.layout(simple_graph, algorithm: "layered")
|
|
31
|
+
|
|
32
|
+
# Export to DOT format
|
|
33
|
+
dot_output = Elkrb.export_dot(result, rankdir: "TB")
|
|
34
|
+
puts dot_output
|
|
35
|
+
puts "\n"
|
|
36
|
+
|
|
37
|
+
# Example 2: Hierarchical graph
|
|
38
|
+
puts "Example 2: Hierarchical Graph"
|
|
39
|
+
puts "=" * 50
|
|
40
|
+
|
|
41
|
+
# Build hierarchical graph using model objects
|
|
42
|
+
hierarchical_graph = Elkrb::Graph::Graph.new(id: "root")
|
|
43
|
+
|
|
44
|
+
# Cluster 1
|
|
45
|
+
cluster1 = Elkrb::Graph::Node.new(id: "cluster1")
|
|
46
|
+
cluster1.labels = [Elkrb::Graph::Label.new(text: "Group 1")]
|
|
47
|
+
n1 = Elkrb::Graph::Node.new(id: "n1", width: 50, height: 30)
|
|
48
|
+
n1.labels = [Elkrb::Graph::Label.new(text: "A")]
|
|
49
|
+
n2 = Elkrb::Graph::Node.new(id: "n2", width: 50, height: 30)
|
|
50
|
+
n2.labels = [Elkrb::Graph::Label.new(text: "B")]
|
|
51
|
+
cluster1.children = [n1, n2]
|
|
52
|
+
cluster1.edges = [Elkrb::Graph::Edge.new(id: "e1", sources: ["n1"],
|
|
53
|
+
targets: ["n2"])]
|
|
54
|
+
|
|
55
|
+
# Cluster 2
|
|
56
|
+
cluster2 = Elkrb::Graph::Node.new(id: "cluster2")
|
|
57
|
+
cluster2.labels = [Elkrb::Graph::Label.new(text: "Group 2")]
|
|
58
|
+
n3 = Elkrb::Graph::Node.new(id: "n3", width: 50, height: 30)
|
|
59
|
+
n3.labels = [Elkrb::Graph::Label.new(text: "C")]
|
|
60
|
+
n4 = Elkrb::Graph::Node.new(id: "n4", width: 50, height: 30)
|
|
61
|
+
n4.labels = [Elkrb::Graph::Label.new(text: "D")]
|
|
62
|
+
cluster2.children = [n3, n4]
|
|
63
|
+
cluster2.edges = [Elkrb::Graph::Edge.new(id: "e2", sources: ["n3"],
|
|
64
|
+
targets: ["n4"])]
|
|
65
|
+
|
|
66
|
+
hierarchical_graph.children = [cluster1, cluster2]
|
|
67
|
+
hierarchical_graph.edges = [Elkrb::Graph::Edge.new(id: "e3", sources: ["n2"],
|
|
68
|
+
targets: ["n3"])]
|
|
69
|
+
|
|
70
|
+
# Layout and export
|
|
71
|
+
hierarchical_result = Elkrb.layout(hierarchical_graph, algorithm: "layered")
|
|
72
|
+
dot_hierarchical = Elkrb.export_dot(hierarchical_result)
|
|
73
|
+
puts dot_hierarchical
|
|
74
|
+
puts "\n"
|
|
75
|
+
|
|
76
|
+
# Example 3: Force-directed layout with custom DOT attributes
|
|
77
|
+
puts "Example 3: Force-Directed Layout with Custom Attributes"
|
|
78
|
+
puts "=" * 50
|
|
79
|
+
|
|
80
|
+
# Build force-directed graph
|
|
81
|
+
force_graph = Elkrb::Graph::Graph.new(id: "root")
|
|
82
|
+
|
|
83
|
+
center = Elkrb::Graph::Node.new(id: "center", width: 80, height: 80)
|
|
84
|
+
center.labels = [Elkrb::Graph::Label.new(text: "Center")]
|
|
85
|
+
|
|
86
|
+
na = Elkrb::Graph::Node.new(id: "a", width: 60, height: 60)
|
|
87
|
+
na.labels = [Elkrb::Graph::Label.new(text: "A")]
|
|
88
|
+
|
|
89
|
+
nb = Elkrb::Graph::Node.new(id: "b", width: 60, height: 60)
|
|
90
|
+
nb.labels = [Elkrb::Graph::Label.new(text: "B")]
|
|
91
|
+
|
|
92
|
+
nc = Elkrb::Graph::Node.new(id: "c", width: 60, height: 60)
|
|
93
|
+
nc.labels = [Elkrb::Graph::Label.new(text: "C")]
|
|
94
|
+
|
|
95
|
+
nd = Elkrb::Graph::Node.new(id: "d", width: 60, height: 60)
|
|
96
|
+
nd.labels = [Elkrb::Graph::Label.new(text: "D")]
|
|
97
|
+
|
|
98
|
+
force_graph.children = [center, na, nb, nc, nd]
|
|
99
|
+
force_graph.edges = [
|
|
100
|
+
Elkrb::Graph::Edge.new(id: "e1", sources: ["center"], targets: ["a"]),
|
|
101
|
+
Elkrb::Graph::Edge.new(id: "e2", sources: ["center"], targets: ["b"]),
|
|
102
|
+
Elkrb::Graph::Edge.new(id: "e3", sources: ["center"], targets: ["c"]),
|
|
103
|
+
Elkrb::Graph::Edge.new(id: "e4", sources: ["center"], targets: ["d"]),
|
|
104
|
+
Elkrb::Graph::Edge.new(id: "e5", sources: ["a"], targets: ["b"]),
|
|
105
|
+
Elkrb::Graph::Edge.new(id: "e6", sources: ["c"], targets: ["d"]),
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
force_result = Elkrb.layout(force_graph, algorithm: "force")
|
|
109
|
+
dot_force = Elkrb.export_dot(
|
|
110
|
+
force_result,
|
|
111
|
+
graph_name: "ForceDirected",
|
|
112
|
+
graph_attrs: { bgcolor: "white", splines: "true" },
|
|
113
|
+
node_attrs: { shape: "ellipse", style: "filled", fillcolor: "lightblue" },
|
|
114
|
+
edge_attrs: { color: "gray", arrowsize: 0.8 },
|
|
115
|
+
)
|
|
116
|
+
puts dot_force
|
|
117
|
+
puts "\n"
|
|
118
|
+
|
|
119
|
+
# Example 4: Write to file
|
|
120
|
+
puts "Example 4: Saving to File"
|
|
121
|
+
puts "=" * 50
|
|
122
|
+
|
|
123
|
+
output_file = "output_graph.dot"
|
|
124
|
+
File.write(output_file, dot_output)
|
|
125
|
+
puts "DOT file saved to: #{output_file}"
|
|
126
|
+
puts "You can render it with Graphviz:"
|
|
127
|
+
puts " dot -Tpng #{output_file} -o output_graph.png"
|
|
128
|
+
puts " dot -Tsvg #{output_file} -o output_graph.svg"
|
|
129
|
+
puts "\n"
|
|
130
|
+
|
|
131
|
+
puts "Demo complete!"
|
|
132
|
+
puts "All graphs have been laid out and exported to DOT format."
|
|
133
|
+
puts "Note: This is a pure Ruby implementation with no runtime Graphviz dependency."
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "elkrb"
|
|
6
|
+
|
|
7
|
+
# Hierarchical graph layout example
|
|
8
|
+
# This demonstrates nested graphs with parent-child relationships
|
|
9
|
+
|
|
10
|
+
# Create a hierarchical graph with nested nodes
|
|
11
|
+
graph = Elkrb::Graph.new(
|
|
12
|
+
id: "root",
|
|
13
|
+
children: [
|
|
14
|
+
{
|
|
15
|
+
id: "parent1",
|
|
16
|
+
width: 400,
|
|
17
|
+
height: 300,
|
|
18
|
+
children: [
|
|
19
|
+
{ id: "p1_child1", width: 80, height: 40 },
|